# Changelog

All notable changes to **mgf-hrb** are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this project will adhere to [Semantic Versioning](https://semver.org/) once a public surface is published.

> **Status.** First public surface published as **0.1.0** (2026-06-02) to the
> magogi-admin Codeberg registry. Gates green: 308+ Zig tests (broker; ~44 more
> in the carved leaf repos) + 485 Python tests (default suite); marker-gated
> stress/perf/e2e add ~94 more.

## [0.1.7] - 2026-06-26

### Changed
- Release the **mgf-common 0.49** pin (`<0.50`) onto devpi so the full
  fleet co-installs at the 0.49 cornerstone (devpi backfill; no API change).

## [0.1.6] - 2026-06-20

### Changed
- **CI: the bespoke pipeline now auto-publishes on a `v*` tag** — the full gate
  runs, then tag-coherence → smoke → publish → verify-devpi twine-uploads the
  wheel to the devpi index. Two `.woodpecker.yml` defects had to be fixed: the
  global `when:` omitted the `tag` event (so a tag triggered NO pipeline — #23),
  and `zig-build-and-test`'s step-level `when:` also omitted `tag` while the
  test steps + `publish` depend on it (so the tag pipeline errored "test-3.13
  depends on unknown step zig-build-and-test" — #25). 0.1.4 was published by
  hand; **0.1.6 is the first release cut entirely through CI automation.** (The
  0.1.5 tag was the validation that surfaced #25 and was superseded.)

## [0.1.4] - 2026-06-20

A reliability release — a real broker concurrency fix plus a sweep that makes
the CI suite stable under load (the `test-3.13` flake and its siblings). The
Python client surface is unchanged.

### Fixed
- **Broker: shm regions are now freed per-CONNECTION, not per-uid, on
  disconnect.** The thread-per-client broker keyed region cleanup on the
  SO_PEERCRED uid, which is identical for every connection from one user — so a
  second same-uid connection (e.g. a dedicated event-stream alongside the main
  one) disconnecting zeroed a LIVE sibling connection's region, surfacing
  client-side as `shm channel: bad magic 0x00000000` right after
  `open_shm_channel` under concurrency. Regions now carry a per-connection
  `creator` token (the cfd, mirroring `subscription.reapPeer`) and disconnect
  calls `releaseAllForConn`. Cross-tenant access isolation (uid owner-check) is
  unchanged. (#20)
- **Broker: listen backlog 8 → 128.** A burst of concurrent connects faster
  than the single accept loop drains overflowed the 8-deep queue and the kernel
  returned `ECONNREFUSED` (a spurious, retry-worthy failure rather than the
  app-level shed). The per-uid S5 admission cap remains the real limit. (#20)
- **CI/tests: broker readiness now connect-probes instead of checking socket
  file existence.** A Unix socket file appears at `bind()` but refuses connects
  until `listen()`; the existence check raced under contention →
  `ConnectionRefusedError`. Fixed in the shared `tests/_broker.py` helper and
  the bespoke spawn helpers (`test_rate_limit`, `test_slowloris`, `test_e2e`,
  `test_cli_doctor`). (#19, #21)

### Changed
- Require **`mgf-test-supervisor>=0.1.16`** (was `>=0.1.6`). Pre-0.1.16,
  `--self-check` took the shared `.gates/.lock`; the parallel CI test steps
  (which share the workspace) collided on it and flaked `test-3.13`. 0.1.16's
  self-check is lock-free. (#18)

## [0.1.3] - 2026-06-16

### Changed
- Widened the `mgf-common` pin to `<0.48` — mgf-common 0.47 is the pure-library release (the end-user CLI moved to `mgf-fed[cli]`); the importable API is unchanged.

### Fixed
- `priv_helper R4` bounding-set test now skips honestly (exit 77) when the runtime grants `CAP_SETPCAP` in *permitted* but the effective power to narrow the bounding set has already been surrendered (the default-profile container case: root with `CAP_SETPCAP` in permitted, but `capsetMinimum()` drops it from *effective* before `dropBoundingSet()` can `PR_CAPBSET_DROP`). Previously it hard-failed (`exit 10+cap`) in CI containers. A surviving bounding cap is now only a real regression when effective `CAP_SETPCAP` is still held — the strong pin still fires on hosts that retain it.

## [Unreleased]

## [0.1.2] — 2026-06-06 — federation cohort: mgf-common 0.46 walk

Walk the `mgf-common` pin ceiling `<0.46 → <0.47` to accept the
library-API-neutral `mgf-common 0.46.0` (CLI-only release: new
`federation`/`design` commands + a `doctor --standards` gate; no library
surface change).

---

## [0.1.1] — 2026-06-04 — federation cohort: mgf-common 0.45 walk

PATCH: pin widened to `mgf-common…,<0.46` to accept mgf-common 0.45 (Codeberg
cutover — PKG-01..12). `mgf-*` resolve from the `codeberg-mgf` index (PKG-06).
No API change.

## [0.1.0] — 2026-06-02

The inaugural release: the full Python SDK (and the in-lockstep Zig broker in
`hrb-code/`) accumulated below, plus the trust-boundary hardening from the
2026-06-02 review.

### 2026-06-02 — SDK trust-boundary hardening (F-HRB-001/002/003)

The SDK trusts the broker after the S1 hello gate, but still took its *numbers*
on faith. Three reliability holes are now bounded (Reliability is priority #1):

- **F-HRB-001** — `_recv_line` / `_recv_line_with_optional_fd` looped on `recv`
  with no size cap; a wedged broker streaming bytes without a newline could grow
  the receive buffer until the client OOMs. Both paths now enforce an 8 MiB
  `_MAX_RESPONSE_BYTES` ceiling (the recvmsg path closes any already-arrived fd
  before raising, so the reject path never leaks it).
- **F-HRB-002** — `deadline_ms` was forwarded to the broker but never bounded
  the client's *own* socket wait; a broker that accepts a request and never
  replies hung the SDK thread forever. `_call_locked` now applies the deadline
  via `settimeout` over send+recv and restores blocking mode in a `finally`; a
  socket timeout re-types to the broker `TimeoutError` family.
- **F-HRB-003** — `ShmChannel` mapped `region_bytes` and read descriptor offsets
  without validating them. It now bounds `region_bytes` before `mmap`, requires a
  positive power-of-two `slot_count`, and rejects any offset whose span falls
  outside the region — a clean typed `MgfHrbError` instead of a raw `ValueError`
  / `struct.error`. Partially closes Tier-0 #5 (asymmetric shm trust).

### 2026-05-31 — Tier-1 #7 / R5: PCI revoke-below-the-fd + device-fd delivery (slice 3)

Slices 1+2 bound a device; slice 3 fills the two previously-`null` PCI vtable
slots that complete the lifecycle, so a bound device can be *used* and *reclaimed*:

- **`fd_for_token`** — hand the client the bound VFIO device fd (for SCM_RIGHTS
  passing), the "delegate usage" half of the bind (G4). Unknown token → -1.
- **`revoke`** — the survivability review's "single most consequential addition":
  revoke below the fd (§3.4). A forced reclaim by URN unbinds the VFIO device
  (closing the device/group/container fds drops the IOMMU mappings, so the
  client's dup'd fd references a dead kernel object) and reattaches the host
  driver. Idempotent when nothing is bound at the URN (the post-condition already
  holds → OK, not Unsupported).

The `vfio`/`driver` seams are now injected on `LinuxBackend` (DIP), so the whole
acquire → fd_for_token → revoke lifecycle is pinned **host-independently** with
fakes + a synthetic bound device — the revoke teardown (all three fds closed +
driver reattached), entry removal, and idempotency, verified non-vacuous by a
negative control (a no-op `revoke` fails the test). The wire `revoke` op for an
unheld PCI device now returns OK (idempotent) rather than Unsupported; its
contract test is updated to match. Live broker not regressed (full gate green,
incl. pytest). Zig 370/370.

### 2026-05-30 — Tier-1 #7 / R5: host-driver detach + atomic reattach (slice 2)

Slice 1 acquired the VFIO container/group/device fds; but `GROUP_GET_DEVICE_FD`
only works once the device is OFF its host driver (nvme, e1000e, …) and on
vfio-pci. This adds that, atomically: `vfio.zig` gains `detachHostDriver` /
`reattachHostDriver` behind a `DriverOps` seam — unbind the host driver →
`driver_override=vfio-pci` → `drivers_probe` — and if any sysfs write fails after
the unbind, the device is put **straight back on its original driver** (a failed
detach must never leave a device orphaned). `vRelease` reverses it.

- `linux_backend.vAcquire` now performs **detach → bind as one atomic operation**:
  a bind failure after a successful detach reattaches the host driver
  (`errdefer`), and a failure to track the bind unbinds AND reattaches. `vRelease`
  does unbind → reattach; the bound table now carries the detach state + BDF.
- Pinned with an injected `DriverOps` fake (4 new Zig tests): the detach write
  sequence, the unbound-device skip, the reattach reversal, and — the valuable
  one — the **atomic reattach on a mid-detach failure**, verified non-vacuous by
  a negative control (dropping the `errdefer` leaves the device orphaned and
  fails the test).
- Still honestly hardware-gated: the raw sysfs/VFIO writes in
  `realDriverOps`/`realOps` are UNVERIFIED without root + a passthrough device;
  on this sandbox they fail at the privileged open and the caller returns an
  honest `Err`. The live broker is not regressed (full gate green, including
  pytest). Zig 369/369.

### 2026-05-30 — Tier-1 #7 / R5: VFIO bind orchestration (slice 1)

The no-IOMMU refusal (2026-05-29) said "no" safely. This adds the "yes" path's
spine: `vfio.zig` — the VFIO container/group/device-fd bind dance behind an
injectable `VfioOps` seam (open container → API + TYPE1 extension checks → open
the device's IOMMU group → VIABLE gate → SET_CONTAINER → SET_IOMMU →
GROUP_GET_DEVICE_FD), with **atomic errdefer-unwind** so a failure at any step
closes every descriptor opened so far (Part 3.4 — a partial bind leaks nothing),
plus `unbind` teardown and an honest errno→`Err` map. Wired into
`linux_backend.vAcquire` (DMA-capable claims) + `vRelease` (a token→bound-device
table).

- The orchestration, the VIABLE gate, the errno map, and above all the atomic
  unwind are pinned with an injected fake (8 Zig tests). The unwind tests assert
  exact close counts at each failure stage and are **verified non-vacuous** by a
  negative control: dropping an `errdefer` fails them (expected 2 closes, found 1).
- **Honestly hardware-gated:** the raw VFIO `ioctl` numbers / struct layouts in
  `realOps` are written to the kernel UAPI but UNVERIFIED on this sandbox (no
  root / passthrough device). On a host where `/dev/vfio` exists but we lack
  root, the privileged open fails and the caller returns an honest `Err` — never
  a faked success. `observe` stays a read-only handle (no bind), unchanged, so
  the existing acquire behavior + the live broker are not regressed (full gate
  green, including pytest). **Slice 2:** the host-driver sysfs detach. Zig 365/365.

### 2026-05-30 — Tier-1 #10: priority-ordered admission (C3 §5.4, the priority tier's first half)

The FIFO anti-starvation core (2026-05-29) gave "everyone eventually wins." This
adds "the right one wins next" for the waiter half of C3 (priority inversion):
`fairness.zig` gains a `PrioritySource` seam (uid → priority, higher = more
urgent) and `onRelease` now reserves a freed contended resource for the
HIGHEST-priority waiter — longest-waiting within a priority — instead of strictly
the FIFO front.

- Priority is **policy-assigned**, keyed on the unforgeable SO_PEERCRED uid and
  looked up through an injected source — a tenant can't self-escalate by asking,
  and the priority VALUES (operator policy that needs real deployment to set
  well) stay out of the mechanism.
- **Exact back-compat:** with no source configured every waiter is priority 0, so
  the selection collapses to the original pure FIFO. The C2/C4 pins are unchanged
  and the core leaves the source unset today (no priority policy is wired yet).
- Pinned (4 Zig): priority jumps the queue ahead of an earlier low-priority
  waiter; equal priority falls back to FIFO; the highest of several tiers wins;
  no-source == FIFO.
- **Gated remainder** (unchanged scope, now narrower): ACTIVE preemption of a
  live lower-priority HOLDER — per §5.4 that *is* forced revocation (graceful
  holder-signal + grace period, then forced IOMMU/VFIO teardown), so it waits on
  the holder-signal channel + #7's VFIO teardown — plus weighted-fair queues,
  hold-time limits, and the actual uid→priority values + `GRACE_MS` tuning.
  `Fairness` stays the single swap point the core calls through. Zig 357/357.

### 2026-05-30 — clean-stop marker cleanup: SIGTERM/SIGINT no longer fake a crash-restart (R-T1-8 a residual)

`restart_marker` distinguishes a host reboot (tmpfs cleared → cold start) from a
broker crash (marker survives → restart → auto-reconcile). A clean exit is meant
to remove the marker so the next start is cold. But the broker's accept loop
never returns — in production it stops only on a signal, and a default-disposition
signal terminates the process WITHOUT running the `defer clear`. So every
operator-initiated stop (systemd `stop`, `kill`, Ctrl-C) left the marker behind
and **every restart looked like a crash-restart**, spuriously auto-reconciling.

- `restart_marker.installCleanupHandler(path)` installs SIGTERM + SIGINT handlers
  that `unlink` the marker (one async-signal-safe syscall) then `exit_group(0)`.
  Those are the deliberate, graceful stops → clean exit → cold next start. A
  crash / SIGKILL can't run a handler, so it still (correctly) leaves the marker.
- `server.zig` arms it right after `checkAndMark`. An `armMarker`/`clearArmedMarker`
  split keeps the clear-path plumbing unit-testable without installing process-wide
  handlers.
- Pinned by a Zig plumbing unit + a Python contract test (spawn → SIGTERM →
  respawn on the same socket is COLD, not a restart). **Verified non-vacuous** by
  a negative control: with the handler removed, the marker survives SIGTERM and
  the next start spuriously detects a restart. Zig 353/353; Python 477.

### 2026-05-30 — R11: concurrent claims-table race pin (G6) — wave-2 pins all cleared

Cluster A4 dropped `Core.lock` around `rawChannel` (correct at the lease layer)
but left `LinuxUsbBackend.claims` read by the unlocked raw path while
release/revoke mutate it — a `swapRemove` reorders/reallocs the list under a
mid-iteration reader → iterator invalidation + use-after-free. The fix
(`c04fb59`) added `claims_lock` + value-copy returns from `findClaim`. The pin
was "blocked on real USB hardware or a deeper FakeBackend."

- That gate was too pessimistic: the race surface is only the `claims` table +
  lock, which a synthetic seed populates with **no hardware**. A `linux_usb.zig`
  contention pin races the REAL `findClaim`/`findClaimByUrn` (6 reader threads)
  against a writer reproducing `vAcquire`'s `append` and `vRelease`'s
  `swapRemove` critical sections verbatim, over a frequently-freed buffer.
- Detectors: a value-copy invariant (every observed claim must be canonical for
  its id — no torn `id`↔`urn`↔`if_num`↔`fd` blend) + the testing allocator.
  **Verified non-vacuous**: stripping `claims_lock` from `findClaim` fails the
  pin ~90%/run (index-out-of-bounds on a freed/reallocated buffer); the locked
  code is deterministically green (12/12).
- Honestly scoped: a contention/sampling test (no ThreadSanitizer in this
  toolchain), not the exhaustive proof R9/R10 are. The real-hardware
  raw+release e2e (live `USBDEVFS_CLAIMINTERFACE`, SCM_RIGHTS, device-fd
  teardown) stays gated. Closes R11 — **all 6 wave-2 cluster-G pins now
  closed**. Zig 352/352.

### 2026-05-30 — R9: fd-leak burst pin for the privileged-helper IPC seam (G3)

A hostile or buggy privileged helper could leak the broker's descriptors via
`SCM_RIGHTS`: the kernel installs a cmsg fd into the broker's table *during*
`recvmsg`, before any payload validation, and a single cmsg can carry an *array*
of fds. Sustained malformed traffic → EMFILE → broker death. Both fixes shipped
(`52b3a91` — errdefer-guarded extraction; the M6 multi-fd close) but the pin was
"blocked on a Zig helper-subprocess fuzz harness."

- Drives `recvResponseWithFd` over a socketpair (a faithful stand-in for the
  helper) with a sustained burst of all three hostile fd-bearing frame shapes:
  G3 malformed-short, M6 well-formed-two-fd, and the M6/`MSG_CTRUNC`
  three-fd overflow.
- Detects a leak of even a single descriptor with a kernel-lowest-free-fd probe
  (no `/proc` parsing): after ~24 hostile frames the next-free fd is *exactly*
  where it started. A deterministic sweep of every leak path — stronger than the
  fuzz harness the pin was waiting on, and needs no subprocess. Closes R9.
  Zig 351/351.

### 2026-05-29 — R10: exhaustive `@enumFromInt` UB pin for the privileged helper (G5)

The privileged USB helper decodes enum fields (`mode`/`verb`/`level`) from the
untrusted wire. `@enumFromInt` on an out-of-range ordinal is UB in release builds
— unacceptable in the most-privileged process. The bounds-check fix shipped
(`83c1ff4`) but its pin was "blocked on a hostile-protocol fuzz harness."

- Extracted the three identical inline bounds-checks into one
  `safeEnumFromInt(comptime E, ordinal) ?E` helper (valid range derived from
  `std.meta.fields(E).len`; out-of-range → null → `bad_request`), and refactored
  `handleAcquire` / `handleControl` / `handleReset` to use it.
- Added an **exhaustive** pin: all 256 `u8` ordinals × `{ClaimMode, ControlVerb,
  ResetLevel}` — in-range decodes to the right variant, every out-of-range
  ordinal is rejected (no UB). For a `u8` domain this is strictly stronger than
  random fuzzing. Closes R10. Zig 350/350.

### 2026-05-29 — broker self-restart detection + auto-reconcile (R-T1-8 residual a, I1/I7)

The broker holds its ownership table only in memory ("re-derive, don't persist")
and could not previously KNOW it had restarted — reconcile was operator-triggered.
This closes that, and now that (b) gives a precise orphan signal it's safe to
auto-trigger.

- **`restart_marker.zig`** — a one-byte marker beside the socket records "an
  instance is running." `checkAndMark` atomically (`O_CREAT|O_EXCL`) finds a
  marker a prior instance left → RESTART; `clear` removes it on a clean exit;
  any fs error → cold (fail-safe). The runtime/tmpfs location gives the right
  distinction: a **host reboot** clears tmpfs → cold start (a reboot reset every
  device — no orphans); a **crash/kill** (host still up) leaves the marker →
  restart (vfio bindings persist → real orphans).
- **`linux_bound.boundSourceStrict`** — the unattended auto-reconcile uses a
  STRICT policy: quarantine ONLY confident `vfio-pci` PCI orphans (non-PCI,
  unbound, and host-driver-bound all excluded) — so a restart never disrupts idle
  or USB devices. The operator wire-op reconcile keeps its conservative source.
- **`server.zig`** — on a detected restart, auto-reconcile (strict) + log; a cold
  start logs and does nothing.
- Pins: `restart_marker` Zig unit (cold → restart → clear → cold) + a strict-bound
  Zig test + a Python integration test (spawn → `kill -9` leaves the marker →
  respawn detects RESTART). On this host (no vfio orphans) the auto-reconcile is a
  clean no-op. Zig 349/349; pytest 476.
- Residual: a clean `SIGTERM` stop doesn't run the `clear` defer, so it reads as a
  restart next boot (which is *correct* — vfio orphans persist a kill too);
  `SIGTERM`-clears-the-marker would need a signal handler (follow-on).

### 2026-05-29 — reconcile precision: bound-at-hardware classification (R-T1-8 residual b)

`reconcileAfterRestart` quarantines resources "bound in hardware but unclaimed"
(crash orphans), but approximated "bound" with the PRESENT set — over-quarantining
idle-free / host-used devices on a healthy host. This gives reconcile the real
signal for PCI.

- **`linux_bound.zig`** (new) — classify a URN's bound state from sysfs
  (`/sys/bus/pci/devices/<BDF>/driver`). A device bound to `vfio-pci` (the
  broker's passthrough binding) persists a crash → orphan candidate; a
  host-driver-bound or unbound PCI function is in host use / idle-free → NOT an
  orphan; a non-PCI (USB) URN can't be classified this way → conservatively
  included (reconcile keeps its prior present-set behaviour for it). A
  `BoundSource` seam.
- **`facade.reconcileAfterRestart(scope, ?BoundSource, deadline)`** — the
  collector filters present → bound when a source is given (null = present-set,
  backward-compat). `fmtReconcile` passes the real source, so the wire op now
  only quarantines genuine orphans (idle / host-used PCI is spared); non-PCI test
  devices stay included, so the existing reconcile contract tests are unchanged.
- Pins: 4 Zig classifier (incl. a host-integration test that a real host-bound
  PCI device is NOT flagged) + 1 facade end-to-end (the bound source spares the
  host-used device; `null` still quarantines all three). Zig 347/347.
- **Honestly gated:** exercising reconcile against a REAL `vfio-pci`-bound orphan
  end-to-end needs an actual passthrough device bound to vfio (root + a dedicated
  device — the same gate as #7's VFIO bind); the integration is tested via
  injection and the classifier against this host's real PCI driver bindings.

### 2026-05-29 — Tier-1 #7: refuse DMA-capable claims with no IOMMU protection (D9 / LINUX-01)

A PCI device granted for shared/exclusive (DMA-capable) use that is NOT behind an
IOMMU can DMA anywhere in host memory — D9's "silent total compromise." The
broker must never *silently* grant that; it refuses, declaring the degraded
posture by saying no (G2).

- **`linux_iommu.zig`** (new) — IOMMU-group resolution from sysfs
  (`/sys/bus/pci/devices/<BDF>/iommu_group` symlink → group number),
  `hostHasIommu`, and a DMA-safety `Posture` (`not_pci` / `no_iommu` /
  `iommu_protected`). The precondition the VFIO bind needs + the honest no-IOMMU
  refusal.
- **`linux_backend.vAcquire`** — a shared/exclusive PCI claim on a device with no
  IOMMU group is **refused** (`Err.NotPermitted`, logged); `observe` (read-only,
  no DMA) is exempt; an IOMMU-protected device proceeds to the bind.
- Pins: deterministic edge cases + a host-integration test (a real
  IOMMU-protected device resolves to a group) + a `linux_test` refusal pin
  (exercised live on a host with VFIO + IOMMU). Zig 342/342.
- **Honestly gated:** the actual VFIO *enforcement* bind/teardown (open
  `/dev/vfio/<group>`, detach the host driver, `VFIO_GROUP_GET_DEVICE_FD`, and the
  IOMMU-mapping teardown on revoke) needs root AND the device released from its
  host driver — it can't run against a live in-use device — so it stays gated on
  a dedicated passthrough device. This slice is the IOMMU-safety *decision*
  (refuse the unsafe grant), not the bind; the group is already resolved where
  the bind slots in.

### 2026-05-29 — quarantine the in-flight resource on a helper-fault (survivability I3)

When the Tier-0 #3 supervisor kills the privileged USB helper mid-op, the
device's state is unknown. The `acquire` path already quarantines on
`Err.Timeout`; this extends the same safety net to the other client-facing
helper-touching ops, so a half-finished op can't leave a device silently
re-grantable in a dirty state.

- **`core.quarantineOnHelperFault(urn, err)`** — on `Err.Timeout` (the op didn't
  complete → state unknown) mark the URN quarantined (`.op_timeout`); a clean
  error (`HardwareFault`/`Unsupported` = known state) is left alone.
- **`core.raw`** captures the URN and quarantines if `rawChannel` times out; a
  new **`core.control`** wraps `backend.control` + quarantines, and
  `facade.control` now routes through it (not straight to the backend). `reset`
  was already covered by `recoverByReset`.
- Pins: a test wrapper returns `Timeout` for raw/control (FakeBackend has no such
  knob) — raw/control timeout → quarantine + claim refused; a clean
  `HardwareFault` → NOT quarantined (still claimable). Zig 338/338.
- Residual sliver (noted): `release()`'s backend call is `void` (no error
  surfaced), and the U7 bad-fd `HardwareFault` path.

### 2026-05-29 — Tier-1 #10: FIFO anti-starvation fairness (survivability C2/C4)

Atomicity (Part 3.4) guarantees *a* winner of a contended claim, not a *fair*
one. Plain first-come-first-served has no anti-starvation: a tenant repeatedly
claim+releasing a contended device could lock a rival out indefinitely — to that
rival, an outage. This is the cold-path contention policy that closes it.

- **`fairness.zig`** — a per-resource FIFO wait queue + **reservation-on-release**.
  When a contended resource frees, it's reserved for the longest-waiting tenant
  (keyed on the unforgeable SO_PEERCRED **uid**, so reconnecting can't jump the
  queue) for a grace window, so a spammer re-claiming immediately is denied and
  the fair waiter wins on its next retry; a no-show forfeits its turn after the
  window. Bounded (S5). Pure + unit-tested.
- **Core integration** — `claimImpl` consults the policy (deny if reserved for a
  longer-waiting tenant, enqueue the denied tenant on conflict, clear+dequeue on
  grant); `release` reserves a freed resource for the front waiter. **No wire or
  SDK change** — it works transparently via the existing `Conflict`-and-retry
  protocol; fairness just decides *whose* retry wins. In-process (non-peer)
  claims are unaffected.
- Pins: 5 Zig policy + 2 Zig end-to-end (spammer-can't-starve a waiter via the
  real `claimWithCap`+`release` path; uncontended claims never delayed).
  Zig 335/335.
- **Slice 1** — the C2/C4 anti-starvation closure. The richer policy
  (weighted-fair, priority/preemption per §5.4, hold-time limits) and tuning of
  `GRACE_MS` genuinely need real multi-tenant traffic; `fairness.zig` is the swap
  point for that future `ContentionPolicy`.

### 2026-05-29 — D7: binary-search the interrupt snapshot

The per-scan `/proc/interrupts` snapshot is now stored sorted by IRQ
(`parseAllInterrupts` sorts once after parsing), so each device's per-vector
count is a binary search (`lookupSum` via `std.sort.binarySearch`) instead of a
linear scan of the whole table — O(device_irqs · log n) rather than
O(device_irqs · n). The win shows on big-NUMA / many-MSI-X hosts where the IRQ
table runs to thousands of rows; the sort cost (once per scan) is negligible
against it.

- The snapshot moved from parallel `irqs`/`counts` arrays to a sorted
  `[]IrqCount`. Pins: the parser test asserts the result is sorted, and a new
  test feeds deliberately-descending `/proc` rows to prove the sort makes the
  binary search correct (don't rely on `/proc/interrupts` already being
  ascending). Re-verified on this host (device `0000:00:01.0` → 46, unchanged).
  Zig 328/328.

### 2026-05-29 — D7: per-scan `/proc/interrupts` cache

The interrupt-rate reader read + parsed all of `/proc/interrupts` once *per
granted device*; a scan of N devices did N full reads. Now it reads + parses
once *per scan*.

- The `IrqCounterSource` seam gains an optional `refresh` hook;
  `Facade.scanInterruptRates` calls it once up front. `linux_irq.refresh` takes a
  single point-in-time snapshot — `/proc/interrupts` parsed into a cached
  `(irq, count)` table (`parseAllInterrupts`) — and each device's count is then a
  cheap lookup against it (`lookupSum`). Cost drops from O(N reads) to O(1 read)
  per scan. A standalone `get` (no scan) lazy-primes the snapshot once.
- The snapshot is a bounded, persistent global (`SNAPSHOT_MAX` rows) behind a
  `SpinMutex` — refilled in place (never freed mid-flight), so a pathological
  concurrent scan can't trip a use-after-free. Injected test sources leave
  `refresh` null and are unaffected.
- Pins: `parseAllInterrupts` + `lookupSum` are pure + unit-tested (parse-once →
  lookup; bounded-table truncation). Zig 327/327.

### 2026-05-29 — D7: the real `/proc/interrupts` reader is wired

The interrupt-rate containment hook now reads live per-device interrupt counts
instead of the null placeholder — `irq_scan` actually contains a flooding PCI
device end-to-end on a real host.

- **`linux_irq`** (new) is the production `IrqCounterSource`: for a PCI URN it
  collects the device's IRQ vectors from `/sys/bus/pci/devices/<BDF>/msi_irqs/`
  (falling back to the legacy line IRQ in `…/irq`) and sums the per-CPU columns
  of those rows in `/proc/interrupts`; a USB interface shares the host-controller
  IRQ (no per-device count) so it stays null. Wired at startup via
  `irq_monitor.setCounterSource(linux_irq.counterSource())`; `irq_monitor` keeps
  a safe null fallback for degraded/test paths.
- The bug-prone half — the parsers `bdfFromPciUrn` + `sumInterruptsForIrqs` — is
  pure and unit-tested (one/many/absent IRQs; non-numeric `NMI:`/`LOC:` labels
  and `ehci_hcd:usb1`-style descriptor colons handled); a host-integration test
  asserts the reader stays total over every real PCI device on the machine.
  Verified against this host (device `0000:00:01.0`, IRQ 122 → 46, matching
  `/proc/interrupts`). Zig 326/326.

### 2026-05-29 — Tier-2 defense-in-depth: interrupt-rate containment (survivability D7)

A device raising interrupts continuously is a use-path DoS against the host. The
broker steps out of the interrupt path (G4) but *granted* the device, so it owns
the policy of whether to keep it granted under abuse. New cold-path hook (the
third of the Tier-2 trio, alongside U7 + I8):

- **`irq_monitor.IrqMonitor`** — per-device interrupt-rate policy: history (last
  counter + timestamp) + rate-vs-ceiling decision; re-baselines on a counter
  reset/wrap (no false positive), never divides by zero, bounded history. Pure
  and unit-tested.
- **`core.containByFlooding(urn)`** — revoke BELOW the fd (the IOMMU mapping, not
  just the descriptor) + quarantine (`fault_maybe_dirty`); a flooder isn't
  silently re-grantable until an operator recovers it via the #12 reset ladder.
- **`facade.scanInterruptRates(...)`** — sweeps every granted device, samples its
  counter through an injectable `IrqCounterSource` seam, contains flooders.
- **`irq_scan` wire op + `Client.irq_scan()`**, steal-gated on `hw:/**`
  (operator/supervisor-triggered — the broker has no background poller; an
  always-on poller isn't warranted for a 🟡-MINOR hook).
- **Honestly scoped:** per-device IRQ attribution is substrate-specific (PCI MSI
  via `/proc/interrupts` + `/sys/.../msi_irqs`; a USB interface shares the HCD
  IRQ), so the production counter source returns null for now — the op + policy +
  containment are live and tested (the Zig facade test injects a flooding source
  and verifies revoke+quarantine), and the real `/proc/interrupts` reader is the
  documented follow-on.
- Pins: 5 Zig policy + 1 Zig facade scan + 3 Python contract. Zig 322/322.

### 2026-05-29 — Tier-2 defense-in-depth: deadlock-freedom invariant (survivability I8)

States + pins the broker↔helper deadlock-freedom argument. The messaging is
strictly hierarchical request/reply (facade → worker → helper); the helper only
ever receives a request and sends a reply — it never initiates a call back into
the broker — so the wait graph is acyclic by construction (no upward-blocking
edge). The only residual risk, a helper that is alive but wedged, is removed by
bounding every recv: `SO_RCVTIMEO` abandons the wait after
`HELPER_RECV_TIMEOUT_SEC` and `recvWithSupervisor` kills + respawns (Tier-0 #3).
Hierarchical messaging + deadline-bounded waits ⇒ deadlock-free by construction.

- Extracted the timeout-arming into `setRecvTimeout` with the invariant written
  out in its doc-comment, and added a regression pin: a Zig test arms it on a
  socketpair and reads `SO_RCVTIMEO` back, asserting the wait is bounded at a
  non-zero timeout — so a refactor can't silently turn it into an unbounded wait
  and reopen the wedged-helper deadlock. Zig 316/316.

### 2026-05-29 — Tier-2 defense-in-depth: fd validation on receipt (survivability U7)

The broker receives an fd via `SCM_RIGHTS` from the privileged USB helper (the
raw-channel / device fd). It was trusted unverified. A real device fd is a
**character device** (the `/dev/bus/usb/*` usbfs node the helper opens); a
compromised higher-privileged helper could otherwise smuggle a crafted fd — a
socket, the broker's own listen fd, a regular file, a pipe — and have the broker
treat it as a device channel.

- **`priv_ipc.isCharDeviceFd(fd)`** — `statx(AT_EMPTY_PATH)` on the fd (no path,
  no TOCTOU); fail-closed on any stat error. Wired into
  `usb_backend_via_ipc.recvWithSupervisorFd`: a received fd that isn't a char
  device is closed, the helper is killed (same Tier-0 #3 containment a recv
  failure triggers), and the op fails `HardwareFault`.
- Pin: a Zig unit test asserts a char device (`/dev/null`) passes while a
  FIFO / socket / pipe fd is rejected. Zig 315/315.

`SleepingBackend` (in `core_test.zig`) — a `KernelBackend` wrapper that delegates
every op to a wrapped backend but whose `acquire` really sleeps before
delegating (unlike `FakeBackend.slow_acquire_ms`, which only fails the
deadline-budget check and never blocks). It pins the R2-A4 lock-drop
**empirically**: one thread runs a 500 ms claim (parked in `backend.acquire`
with `Core.lock` dropped, phase 2) while the main thread times a lock-requiring
op (`liveLeaseCount`) and asserts it returns in microseconds, not blocked the
remaining ~420 ms — a test that would fail under the pre-A4 "lock held across the
backend acquire" shape. Closes R-J-A4-TIMING.

### 2026-05-29 — restart-recovery wiring: the broker-wide `reconcile` op (R-T1-8-RESTART-WIRING)

Wires the Tier-1 #8 recovery contract live. `Core.reconcileAfterRestart` was a
tested-but-uncalled mechanism; this gives it a real hardware-derived input and
an operator trigger.

- **`Facade.reconcileAfterRestart(scope, deadline)`.** For each worker
  intersecting `scope`, enumerates the present-in-hardware set (bounded by
  `MAX_RECONCILE_URNS`, scope-filtered) and feeds each core's
  `reconcileAfterRestart`, which quarantines every present-but-unclaimed orphan
  (`unclaimed_after_restart`) — never assumed free (may be dirty), never assumed
  owned (the owner is gone). Returns `{scanned, quarantined, truncated}`.
- **`reconcile` wire op + `Client.reconcile_after_restart(*, scope="hw:/**")`.**
  Steal-gated on the requested scope (reusing `permitsRevokeFromPeer`, like
  `recover`): only a cap with authority over `scope` can reconcile it. **Trigger
  only on an actual restart** — the broker has no self-restart-detection
  persistence, so this is operator/supervisor-driven; on a healthy broker it
  would quarantine every idle device, which the steal-gate prevents from being a
  stray client's call.
- **Honestly scoped.** The genuinely infra-gated tail is documented in ROADMAP:
  automatic self-restart detection (needs persistence), real-Linux
  bound-at-hardware enumeration (vs the present-set used today), and helper-mid-op
  in-flight quarantine on non-acquire ops.
- Pins: 2 Zig facade (`facade_test.zig` — broker-wide + scoped) + 3 Python
  contract (`tests/contract/test_reconcile.py` — orphan quarantine→recover,
  idempotent, authority-gate). Zig 313/313; Python suite green. PUBLIC_API.md
  updated.

### 2026-05-29 — shm data plane: background per-channel drain thread (R-TIER0-5-DATAPLANE slice 4)

Closes the live shared-memory data plane (Part 5.3). Slices 0–3 shipped the
deterministic PULL path (`open_shm_channel` + `drain_shm_channel`); slice 4
adds the opt-in PUSH path so a streaming producer needs no per-batch RPC.

- **`start_shm_drain` wire op + `Client.start_shm_drain(channel)`.** Spawns a
  broker-side background thread (`shm.Drainer`) bound to the channel's region;
  it drains the client-produced ring continuously via the asymmetric-trust
  `popUntrustedProducer`. After it's running, `drain_shm_channel` returns the
  thread's *accumulated* stats (`drained`/`checksum`/`last_seq`/`dropped`)
  instead of pulling — same keys, so a caller polls progress mode-agnostically.
  Default pull-mode (slices 0–3) is unchanged.
- **Lifecycle correct by construction.** The drainer is owned by the
  `Region`; `unmapRegion` stop+joins the thread *before* unmapping the pages,
  so every free path (`release` / `releaseAllForUid` on disconnect / `deinit`)
  joins first — no use-after-free of a region under a live consumer, no leaked
  thread. Owner-checked (a stranger can't drain another tenant's channel) and
  idempotent-refusing (a second start is rejected).
- **arm64 weak-ordering was a non-issue:** the ring already carries the
  producer/consumer acquire-release ordering, so the thread is a plain bounded
  loop over `popUntrustedProducer`.
- Pins: 2 Zig (`shm.zig` — push-mode accumulation round-trip + disconnect-join
  no-leak via `testing.allocator`) + 3 Python contract (`tests/contract/test_shm.py`
  — push-mode round-trip, owner-check/idempotent-refuse, close-doesn't-hang).
  Zig 311/311; Python suite green. PUBLIC_API.md + ROADMAP updated; the stale
  shm.zig "wiring is a follow-on" header refreshed to the live state.

### 2026-05-29 — federation sync to mgf-common 0.42.0: OB-10 Zig LogRecord conformance

`/sync` to the mgf-common 0.42.0 unified-logging cohort. The new cross-language
`LogRecord` contract (**OB-10**) names mgf-hrb as the federation member that
compiles + pins the **Zig** half (mgf-common ships no Zig toolchain). That
obligation was previously uncaught and unwired.

- **Vendored** mgf-common's Zig reference emitter + its golden fixture to
  `hrb-code/vendor/logrecord/` (`log_record.zig` + `testdata/log_record_fixture.jsonl`).
- **Ported to Zig 0.16.** Upstream's `clients/zig/log_record.zig` did not compile
  under this repo's 0.16 toolchain (it used the removed managed
  `std.ArrayList(u8).init` / `.writer()` API). The buffer plumbing in
  `toJsonlAlloc` and the conformance test now use `std.Io.Writer`; the emit logic
  (`writeJsonObject` + helpers) is byte-for-byte identical to upstream.
- **Wired into CI.** `hrb-code/build.zig`'s `test` step now compiles + runs the
  emitter's embedded conformance test, so `zig build test` (CI `zig-build-and-test`)
  asserts byte-for-byte reproduction of the shared golden on every push — **OB-10 ✅**.
- **Docs.** New §2.3b OB section in the conformance tracker (OB-10 ✅; OB-09/-11
  🚫 N/A by shape; production-broker LogRecord emission ⏳); stale tracker header
  refreshed (v0.38.0 → v0.42.0); pins confirmed current (`mgf-common>=0.41,<0.43`
  accepts 0.42.0). Cross-repo recommendation filed as FEEDBACK `LOG-01` (mgf-common
  should adopt the 0.16 idiom upstream so the two copies reconverge).

### 2026-05-28 — live shared-memory data plane (R-TIER0-5-DATAPLANE, slices 0–3)

The broker spoke only line-JSON (the control plane). Part 5.3's **data
plane** — a lock-free shared-memory ring so a client streams items to the
broker with no syscall per item — is now wired end-to-end across two real
processes. Two mechanisms that were unit-tested in isolation are connected:
the asymmetric-trust ring consumer (`popUntrustedProducer`) and the
per-tenant region pool.

- **`mgf-hrb-rt`**: new `HostedRing(T)` — the shared-memory overlay of the
  SPSC ring. head/tail/dropped/slots are pointers into a caller region at
  fixed, versioned offsets (`ShmRingHeader` ABI, magic/version/sizes so
  both Zig and the Python SDK validate the layout). The inline `Ring(T)`
  (+ its tier0_5 + bench pins) is untouched.
- **`shm.zig`**: regions are now **memfd-backed** (`memfd_create` +
  `ftruncate` + `mmap` MAP_SHARED) so the broker can `SCM_RIGHTS`-pass the
  fd and both processes map the same pages; owner-checked `fdFor`;
  zero-then-munmap-then-close on release (strictly stronger residue
  closure). A broker-global pool, reclaimed per-connection on disconnect.
- **Wire ops**: `open_shm_channel` (acquire a private region, lay out the
  ring, fd-pass the memfd + a descriptor) and `drain_shm_channel`
  (owner-checked; drains via `popUntrustedProducer`, **bounded by
  capacity** so a hostile producer-written `head` can't make the broker
  spin; returns drained/checksum/last_seq/dropped).
- **SDK**: `ShmChannel` (`mmap` the passed fd, validate the in-region
  header against the wire descriptor, `push` the SPSC producer half) +
  `Client.open_shm_channel`/`drain_shm_channel`.

Proven by the deterministic round-trip (push 50 known u64s → broker drain
→ count + XOR checksum + last_seq match), overflow accounting, and an
adversarial hostile-`head` drain that returns cleanly. Remaining: a
background per-channel drain thread (slice 4, deferred). Broker `zig build
test` 306→308 (+ 5 HostedRing pins in mgf-hrb-rt); Python 460→466.

### 2026-05-28 — reuse-proven leaves carved into their own repos (E2)

Following the E1 in-repo module DAG, the five reuse-proven leaves are now
their own Codeberg repos, consumed by the broker as Zig package
dependencies (pinned by commit + content hash in `hrb-code/build.zig.zon`):

| Repo | Modules | Tests that moved with it |
|---|---|---|
| `mgf-hrb-model` | `mgf_hrb_model` | — |
| `mgf-hrb-kabi` | `mgf_hrb_kabi`, `mgf_hrb_fake_backend` | conformance (13) |
| `mgf-hrb-cap` | `mgf_hrb_cap` | — |
| `mgf-hrb-rt` | `mgf_hrb_ring`, `mgf_hrb_fdpass`, `mgf_hrb_liveness` | ring (10) + fdpass (3) |
| `mgf-common-identity` | `mgf_hrb_identity` | identity (18) |

The broker keeps `core`/`facade`/`linux*`/wire/`usb*` (no second consumer
yet). Tests that depend only on a carved leaf moved into its repo; tests
that cross-check broker-internal modules (cap_test→core, liveness_test→
core) stayed. So the broker's `zig build test` count is 350 → 306 — the
44 difference runs in the leaf repos now, not lost.

Auth: the leaf repos are PRIVATE under a limited-visibility account, and
zig's git client ignores `~/.netrc`, so `scripts/zig-fetch-deps` injects a
token (from `$CODEBERG_TOKEN` / gitignored file) to populate zig's cache;
`zig build` then resolves by hash offline. The token is never committed.

The Python **SDK is NOT carved** — it is published as a wheel FROM this
monorepo (`scripts/publish-wheel`) so the broker and SDK stay in lockstep
across the wire protocol. `model`/`identity` keep their module names
(`mgf_hrb_*`) so broker source was untouched; only `build.zig` wiring +
file deletions changed.

### 2026-05-28 — broker packagized into an enforced module DAG (E1) + testing hardening (WS-B)

A SOLID-redesign + testing pass on the Zig broker. Two themes.

**Redesign — `server.zig` decomposition (Part 1).** The 2,807-line
god-object was split into single-responsibility modules — `admission.zig`
(connection caps, rate-limit token bucket, slowloris deadline, injected
clock), `format.zig` (the ~18 wire formatters), `dispatch.zig` (op
handling) — leaving `server.zig` as pure transport + assembly (~572 LOC).
This simultaneously closed the top test-coverage gap: those formatters +
admission control were previously inside an *executable* (not a `zig
build test` target) and so had no direct unit tests; they are now
unit-tested deterministically with no broker spawn.

`core.zig` (1B): each cap-checked op (claim, claimDetach, raw) had a
non-peer `*WithCap` and a peer-aware `*WithCapFromPeer` variant; since
the former is exactly the latter with `peer = null`, the three pairs
collapse to three fns taking a mandatory `peer: ?cap.PeerCred`.
Behavior-preserving (`peer` is mandatory, so no call site silently skips
the Tier-0 #2 `bound_to` check). cap.zig's verifier + facade's wrappers
keep their explicit peer/non-peer split.

**Extraction — in-repo packagization (Part 2, Stage E1).** Every
`INDEX.md` sibling + shared internal helper in `hrb-code/src` is now a
named Zig module whose `.imports` list IS its enforced dependency
boundary. `model` provably imports nothing; `kabi` reaches only `model`;
`cap`/`core`/`facade`/wire layer above. **Zero inter-file path imports
remain** — the clean dependency DAG is a compile-time guarantee, not a
convention, and carving the reuse-proven leaves into their own repos
(E2) is now a near-mechanical follow-on (deferred). 21 modules, wired via
`build.zig`'s `pkg_mods`; `@import("model.zig")` → `@import("mgf_hrb_model")`
at every call site.

**Testing (WS-B).**
- Contract suite parallelized: per-worker Unix sockets keyed off
  `PYTEST_XDIST_WORKER` (`tests/_broker.py`) so `pytest -n auto` runs
  without socket collision.
- Property/fuzz tests: `urn_test.zig` (URN⇄glob matcher invariants) and a
  lease-table fuzz in `core_test.zig` (random claim/release/holderDied
  sequences asserting `auditInvariants() == null`).
- `format_test.zig` + `dispatch.zig` inline tests cover the wire-code
  escaping + dispatch surface unblocked by the decomposition.
- Hypothesis profile (`deadline=None`) de-flakes the pure-function
  property suite under `-n auto` CPU contention.
- **`scripts/zig-coverage`** — a kcov-based line-coverage lane (DWARF, no
  instrumentation; test-file list extracted from `build.zig` so it can't
  drift). Self-skips when kcov is absent, so it is wired into
  `scripts/gates` without blocking contributors who lack the tool.

Zig `zig build test` count moved 382 → 350: this is the elimination of
redundant cross-importer test re-runs (each inline test now runs exactly
once, in its module's listed target), verified to lose no distinct test.

### 2026-05-28 — `pushFanout` evaluates predicates outside the registry lock (R2-A3)

`subscription.pushFanout` held `registry.lock` across the ENTIRE fan-out,
including the predicate `evaluate` — which can call the `lookup` callback
(a sysfs attr read). That was correct (the lock serialized fan-out vs
`reapPeer`, so no UAF) but it (a) held a contended lock across a
potentially-slow syscall, serializing every other subscription op, and
(b) imposed a fragile "the `lookup` callback must not re-enter this
module or it deadlocks on the non-reentrant SpinMutex" contract.

**`hrb-code/src/subscription.zig`** — fan-out is now three phases so the
slow predicate eval runs OUTSIDE the lock, without a use-after-free
against a concurrent `reapPeer`/`unregister`:

1. **Snapshot (locked):** scope-pre-filter the slots; for each candidate,
   `refs += 1` and record the pointer. A non-zero `refs` PINS the
   subscription.
2. **Evaluate (unlocked):** run `evaluate(predicate, ev, lookup)` per
   snapshotted sub. Dereferencing the predicate AST is safe — the ref pin
   keeps the arena alive even if the peer disconnects mid-eval.
3. **Commit (locked):** deliver to each live match's queue, drop the ref,
   and free the sub if this was the last ref AND it was reaped meanwhile.

`reapPeer`/`unregister` now route through `retireSubLocked`: a sub with
`refs > 0` is unlinked from its slot and marked `dead` (deferred free),
never freed out from under an in-flight eval; the eval's commit phase
frees it when the last ref drops. This is the correct refcount +
deferred-free (RCU-lite) fix — a per-slot generation counter alone would
be insufficient (it detects slot ABA only AFTER eval already
dereferenced freed memory).

**Bonus:** the "lookup must not re-enter" contract is GONE — eval no
longer holds the registry lock, so the callback may safely re-enter.

**Pins:** a threaded stress test (`subscription_test.zig`) races ~4000
`pushFanout` cycles against ~4000 `register`/`reapPeer` cycles on the
same peer; the GPA-backed testing allocator's UAF/double-free/leak
accounting is the verdict (stable across repeated runs). All real
allocator calls stay under `registry.lock` (serialized), so no
thread-safe-allocator wrapper is needed.

ROADMAP R2-A3 closed (it was mis-scoped as a "generation counter" fix +
flagged as a latent UAF; on inspection the old code was correct and the
real win is lock-contention + a simpler contract).

### 2026-05-28 — Per-tenant private shm regions (Tier-0 #5, §5.5, S3)

Tier-0 #5 ("asymmetric shared-memory trust + per-tenant isolation") had
two halves. The **asymmetric-trust half (§3.5, the U6 shm-escape — a
CRITICAL item)** was already shipped: `ring.zig`'s consumer treats every
producer-written field as untrusted (`idx = field & mask`, no
client-written pointer/length ever dereferenced), pinned by the
`tier0_5:` fuzz/poison suite + `popUntrustedProducer`. This lands the
**per-tenant isolation half (§5.5, the C6 cross-tenant side channel)** as
a tested mechanism.

**`hrb-code/src/shm.zig`** — `RegionPool`: private per-tenant regions
keyed on the unforgeable SO_PEERCRED uid (consistent with the #9 lease
ceilings / #12 recovery). Properties, each pinned:
- **Private per tenant** — `acquire(uid)` returns a region only its owner
  can `view`; a different uid's `view`/`release` is refused. No region is
  ever shared between two tenants.
- **Zeroed before reuse** — `release` (and `releaseAllForUid` on
  disconnect) zeroes the region before it returns to the pool, and
  `acquire` hands out zeroed memory, so no residual data ever crosses
  from one tenant to the next (C6 made structurally absent, not merely
  mitigated).
- **Bounded (S5)** — aggregate `MAX_REGIONS = 64` + per-tenant
  `MAX_REGIONS_PER_UID = 16` (below the aggregate so one tenant can't
  starve others); over a ceiling, `acquire` sheds with `null` —
  back-pressure, the same per-tenant/aggregate shape as #9.

**Pins:** 6 Zig (`shm.zig`): distinct mutually-inaccessible regions per
tenant; zeroed-before-reuse (write secrets → release → next tenant's
region is all-zero); aggregate ceiling sheds; per-tenant ceiling bounds
one tenant without starving another; owner-checked view/release;
`releaseAllForUid` reclaims a disconnected tenant's regions.

**Scope.** This is the region-LIFECYCLE mechanism (allocation, isolation,
zeroing, bounds) on exactly the shape the OS layer will wrap. The live
data-plane WIRING — `mmap`-ing each region, fd-passing it to the client
via `SCM_RIGHTS`, hosting a `Ring` in it, and an open/close-channel wire
op — is the follow-on (the broker's transport is line-JSON today; the
shm data plane isn't connected to clients yet). Tracked as ROADMAP
**R-TIER0-5-DATAPLANE**. Same "formalise the mechanism, defer the OS
plumbing" discipline as the #8 recovery contract. `tests/_broker.py`
marks `shm.zig` test-only (not yet a server build input) so the staleness
guard doesn't flag it.

Tier-0 #5 is now mechanism-complete on both halves; only the data-plane
wiring remains (follow-on).

### 2026-05-28 — Recovery contract formalised in code (Tier-1 #8, S4)

The worker/helper restart recovery contract (survivability §4.3/§4.4)
was specified in prose but not formalised in code. This lands its two
core properties as tested, callable mechanisms in the lease engine.

**In-memory integrity invariants (§4.4).** `verifyLeaseInvariants(leases)`
— a pure check over the ownership table: every lease has a real id +
holder, ids are unique, and **no two mutually-incompatible leases share a
URN** (the double-grant guard — two `exclusive` on one resource is the
worst reliability lie). `Core.auditInvariants()` runs it and, on a
violation, quarantines the offending resource (`fault_maybe_dirty`) +
alerts loudly — a *controlled fault*, never a silent bad grant. Wired
into `holderDied` (a major table transition) as a safety net; in correct
operation it never fires, but a latent corruption surfaces as quarantine
+ alert instead of a bad grant.

**Re-derivation from authoritative sources (§4.3).**
`Core.reconcileAfterRestart(bound_urns)` — after a restart the broker
must re-derive truth from TWO authoritative sources (the hardware: what's
actually bound; the live clients: who holds a lease), never from lost
in-memory state. Anything bound in hardware but unclaimed by any live
client is `Quarantined` (`unclaimed_after_restart`) — never assumed free
(may be dirty), never assumed owned (the owner is gone), making restart
crash-consistent. It composes with #12: a reconciled-quarantined resource
is recovered via the reset ladder (`recover`). Idempotent.

**Pins:** 5 Zig (`core_test.zig`): the pure invariant check passes a
sound table (incl. observe+exclusive coexistence) and catches a
double-exclusive grant / zero-holder / duplicate-id; `auditInvariants`
on a normally-claimed core is clean; `reconcileAfterRestart` quarantines
bound-but-unclaimed resources while leaving a live-held one alone, and is
idempotent.

**Scope.** This formalises the *contract* as mechanism + tests. The live
restart PLUMBING — a restart-detection signal that calls
`reconcileAfterRestart` with a broker-wide hardware enumeration, and
quarantining the in-flight resource when the Tier-0 #3 supervisor
kills+respawns the helper mid-op (beyond the existing acquire-timeout →
quarantine path) — is tracked as a follow-on (ROADMAP
**R-T1-8-RESTART-WIRING**); it depends on infrastructure that doesn't
exist yet (restart detection, an enumerate-all-bound backend call).

### 2026-05-28 — Reset-escalation ladder + `recover` op (Tier-1 #12, S2/S4)

Two reset levels (`function_level` / `bus_level`) existed and the USB
backend even verified its post-condition (`verifyResetSucceeded`), but
nothing ORCHESTRATED them: `backend.reset` had no caller and
`clearQuarantine` was dead code, so a quarantined resource was a
dead-end — never recovered. This adds the ladder (survivability §2.5)
and an operator-triggerable wire op.

**`hrb-code/src/core.zig`** — `Core.recoverByReset(urn, deadline) ->
RecoveryOutcome` implements `FLR → (verify) → bus reset → (verify) →
quarantine`. Each rung is deadline-bounded and verified by the backend
(a backend that can't verify returns an error → the ladder escalates).
On a rung's success the quarantine clears and the resource is claimable
again; when every rung fails it stays quarantined with the reason
**escalated to `reset_unverified`** (FLR+bus tried — operator-actionable).
Recovery acts ONLY on a quarantined resource (`not_quarantined` no-op
otherwise — a device in service is never force-reset); a quarantined URN
has no live lease by construction, so there's no holder to disrupt.
Backend resets run OUTSIDE `Core.lock` (helper-IPC discipline, like
acquire). `RecoveryOutcome` = `not_quarantined` / `recovered_flr` /
`recovered_bus` / `still_quarantined`.

**`hrb-code/src/fake_backend.zig`** — new `reset_fails_below` knob so a
device that "ignores FLR but yields to a bus reset" is modellable,
exercising the escalation path.

**`recover` wire op** (`request.zig` `RecoverReq` + `server.zig`
`fmtRecover` + `facade.zig` `recoverByReset` passthrough) — **`steal`-right
gated** (a forceful hardware repair, the same operator-class authority as
`revoke`). Response `{"ok":true,"outcome":"<...>"}`. The ladder always
terminates, so the op never errors past the cap check — a frozen device
can never block the broker (each rung is abandonable at its deadline).

**`src/mgf/hrb/_client.py`** — `Client.recover(urn, *, deadline_ms=0) ->
str` returns the outcome; `steal`-gated, raises `NotPermittedError`
without it.

**Pins:** 4 Zig behavioural (`core_test.zig`: FLR succeeds → recovered_flr
+ claimable; FLR fails → escalates → recovered_bus; both fail →
still_quarantined + reason escalated + still Unavailable; non-quarantined
in-service device is a no-op) + 3 Python contract (`test_recover.py`:
healthy URN no-op, steal-gated refusal via an observe-only attenuated cap,
unknown URN no-op) + 2 SDK unit. The full ladder (recovered_flr/bus,
still_quarantined) is Zig-tested against the per-level FakeBackend; the
broker's seeded test backend never auto-quarantines, so the wire test
covers the gate + no-op surface.

Tier-1 #12 closed: the quarantine state (#6) now has a recovery path —
quarantine is no longer a dead-end.

### 2026-05-27 — `attenuate` wire op: mint attenuated sub-capabilities (R-CAP-ATTEN-WIRE)

Until now every connection only ever held the ROOT cap (scope `hw:/**`),
so #11's capability-filtered enumeration and #9's per-tenant caps could
only be exercised against the root — the narrowing path had no way to be
driven through a REAL narrow grant from a consumer. This adds the wire op
to mint one (Part 4.4 attenuation, on the wire).

**`hrb-code/src/cap.zig`** — `attenuateFromPeer(parent_id, peer,
restriction)`: the peer- and grant-gated entry point. Two authority
checks the plain `attenuate` (in-process / test path) skips:
- **connection binding** (Tier-0 #2) — when the parent is bound, the
  requesting peer must match, so a leaked cap id is useless from another
  connection;
- **the `grant` right** (Part 4.4) — a cap without `grant` cannot
  delegate.
The narrowing core was extracted into a shared `attenuateLocked`; the
child inherits the parent's `bound_to`, so a sub-cap of a
connection-bound cap stays bound to the same peer.

**`hrb-code/src/request.zig`** — new `attenuate` op + `AttenuateReq`
(parent `cap_id` required + non-zero; optional `scope` / `rights` /
`mode_ceiling` narrowings, each with a presence flag mirroring
`cap.Restriction`). `rights` is an array of Right names, parsed through a
closed-set `parseRightName` gate.

**`hrb-code/src/server.zig`** — `fmtAttenuate` formatter + dispatch +
classification (it's a write, not a fidelity-bearing read). **The root
cap now carries the `grant` right** so a connection can delegate a
narrower view to a sub-component (safe: attenuation only ever shrinks, so
a sub-cap can never escalate beyond the root). Response:
`{"ok":true,"cap_id":<sub_cap_id>}`; refusals are `NotPermitted` (missing
parent / no grant / wrong peer / attempted widen) or `Overloaded`
(cap-table OOM).

**`src/mgf/hrb/_client.py`** — `Client.attenuate(*, scope=None,
rights=None, mode_ceiling=None) -> Client` returns a **child view** that
SHARES the connection (same socket + peer, so the sub-cap's binding
matches) and presents the sub-cap on every op. The child does NOT own the
transport (new `owns_transport` flag): closing it leaves the parent's
connection intact; the socket is torn down once, by the owning client.
The wire code `TooBusy`/`Overloaded` mapping and `NotPermittedError`
already cover the refusals.

**Pins:**
- **4 Zig cap_test** pins: grant-required, connection-binding enforced,
  child inherits binding + still can't widen, missing-parent is
  `NotPermitted` (no existence leak).
- **5 Python contract** pins (`tests/contract/test_cap_attenuate.py`) —
  the headline one finally exercises #11 end-to-end through a real grant:
  a `hw:/test/**`-scoped sub-cap's `list("hw:/**")` returns EXACTLY the
  in-scope URNs (every pci/usb URN the root sees is filtered out); plus
  widen-refused, grant-gated delegation, rights-narrowing biting a
  `claim`, and unknown-parent refusal.
- **4 SDK unit** pins: the wire shape (parent cap + narrowings, omitted
  axes not sent), the child presents the sub-cap on its ops, `ClaimMode`
  enum accepted for the ceiling, and closing a child view leaves the
  shared socket open.

This closes the honest gap R-T1-11C flagged: the per-tenant lease/rate
caps (#9) and enumeration filtering (#11) can now be exercised — and are
pinned — through an actual attenuated grant, not just the root cap.

### 2026-05-27 — Bounded enumeration results (R-T1-9E, Tier-1 #9 COMPLETE, S5)

Fifth and final slice of Tier-1 #9. `dump_all` / `inspect_full` already
emitted an honest `truncated` flag, but `list` and `list_leases` did
not: on a full response buffer the collectors **silently dropped
records** (dishonest), and the fixed `]}\n` suffix write could run
**past the end of the buffer** when results filled it exactly — a latent
crash (the broker executable is built in Debug, so an out-of-bounds
write panics the connection thread; a host/backend with enough URNs to
fill 64 KiB could trigger it).

**`hrb-code/src/server.zig`** — `ListCollector` and `LeaseJsonWriter`
now:
- reserve a fixed tail margin (`ENUM_TAIL_MARGIN = 32`) and bound every
  element write to `out.len − margin`, so the closing tail always fits —
  no more write-past-the-end;
- set a `truncated` flag (and stop emitting) when a record doesn't fit,
  instead of silently dropping it and probing for smaller ones;
- emit `"truncated":<bool>` in the response (via a bounded `bufPrint`
  tail, not a raw `@memcpy`), matching `dump_all` / `inspect_full`. The
  consumer's signal to re-issue against a narrower scope.

Wire-additive: the SDK's `Client.list` / `list_leases` read
`resources` / `leases` and ignore the new field, so it's back-compatible.

**Pins:** 2 Python contract tests (`tests/contract/test_enum_result_bounds.py`)
— `list` and `list_leases` now carry a top-level `truncated` boolean
(False on a fitting host) while `resources` / `leases` stay present and
list-typed. Driving an actual truncation needs >64 KiB of URNs (more
than the fake backend exposes), so overflow-safety + truncation honesty
ride on the reserved-margin code path.

**Tier-1 #9 ("bound everything", S5) is now COMPLETE** — five slices:
lease ceilings (9A) + per-tenant connection cap (9B) + slowloris
deadline (9C) + per-tenant rate limit (9D) + enumeration result bounds
(9E), on top of the pre-existing request-size and aggregate-connection
bounds. Every client-driven resource (table entries, fds via leases,
connections, op rate, response size) is now bounded per-tenant and in
aggregate; the mechanism is back-pressure (`Overloaded` / `TooBusy` /
`Timeout`), never unbounded growth.

### 2026-05-27 — Rate limit: per-tenant token bucket (R-T1-9D, Tier-1 #9, S5)

Fourth slice of Tier-1 #9. The lease / connection / slowloris caps bound
resource HOLDING; this bounds op THROUGHPUT so a flood (op storm,
10^6 claims/sec) can't saturate a worker or burn the broker's CPU.

**`hrb-code/src/server.zig`** — a per-tenant token bucket keyed on the
SO_PEERCRED uid. Each control-path op costs one token; tokens refill at
a steady rate up to a burst ceiling; an op with no token is shed with
`Err.Overloaded`. Charged in `handle` AFTER the (cheap) parse — so the
audit record carries the real op name — and BEFORE dispatch, so a shed
op skips the expensive backend work.

- **Integer milli-token arithmetic** (1 token = 1000 mt) — no float on
  the broker path.
- **Per-uid state survives reconnects** (a 256-slot table, LRU-evicted
  when full, SpinMutex-guarded) — otherwise a flood reconnecting per op
  would reset its bucket and evade the limit. New uids start with a full
  burst.
- **Generous defaults** (burst 2048 tokens, refill 1000 tokens/s per
  tenant) so real interactive + GUI workloads — and even bursty test
  loops of hundreds of ops — never trip it; only a genuine flood does.
  Tunable via `MGF_HRB_RL_BURST` (whole tokens) and
  `MGF_HRB_RL_REFILL_PER_SEC` (0 disables refill).

**Pins:** 3 Python contract tests (`tests/contract/test_rate_limit.py`,
broker spawned with a tiny burst): a flood is shed after exactly the
burst (burst=5, refill=0 → 5 admits then sheds); the shed carries the
typed `Overloaded` code and a fresh tenant's first op is never throttled
(burst starts full); the bucket refills over time so a throttled tenant
recovers (transient back-pressure). The generous default was verified
not to perturb the existing high-op-count suites (e.g. the ~514-op
revoked-ring-ceiling test).

### 2026-05-27 — Slowloris guard: per-connection progress deadline (R-T1-9C, Tier-1 #9, S5)

Third slice of Tier-1 #9. Before this, `handleClient`'s `read()` blocked
indefinitely, so a connection that sent a partial request line (no
newline) and then went silent — or trickled bytes — held a connection
slot doing no work, indefinitely. A classic slowloris.

**`hrb-code/src/server.zig`** — the recv loop now sets `SO_RCVTIMEO`
(via the new `cap.setRecvTimeoutMs`) so `read()` returns `EAGAIN`
periodically even with no traffic. `partial_since_ms` marks when the
current INCOMPLETE request line first arrived; a line that doesn't
COMPLETE within the deadline of its **first byte** drops the connection
with a typed `Timeout`. Tracking time-since-first-byte (not
since-last-byte) means an attacker can't reset the deadline by trickling
one byte at a time. A **fully idle connection (nothing buffered) is NOT
penalised** — a legitimate long-lived client, already bounded by the
per-uid connection cap (R-T1-9B).

- **Deadline: 10 s default**, operator-tunable via `MGF_HRB_SLOWLORIS_MS`
  (read at startup with the new `envU64` helper). The poll interval is
  derived (¼ of the deadline, clamped to [50 ms, 2 s]) so a short tuned
  deadline is still observed promptly without busy-waking.
- **`envU64`** loops a 16 KiB read of `/proc/self/environ` rather than a
  single 4 KiB read, so a var set late in a large environment block is
  still found (the old single-read `envSet` could miss it).
- `cap.setRecvTimeoutMs` is best-effort: if `setsockopt` fails the socket
  stays blocking (pre-existing behaviour) — no regression for
  well-behaved clients, the guard just can't fire.

**Pins:** 2 Python contract tests (`tests/contract/test_slowloris.py`,
broker spawned with `MGF_HRB_SLOWLORIS_MS=400` so the drop is observable
in ~0.5 s): a stalled partial request is dropped with `Timeout`; an idle
connection survives well past the deadline and still serves a request
(the must-not-kill-healthy-clients pin).

### 2026-05-27 — Bounded connections: per-tenant connection cap (R-T1-9B, Tier-1 #9, S5)

Second slice of Tier-1 #9. The aggregate connection ceiling
(`MAX_ACTIVE_CLIENTS = 128`) already existed, but it lets ONE tenant
open all 128 connections and starve everyone else. This adds a
per-tenant cap so the connection budget is shared.

**`hrb-code/src/server.zig`:**
- **`MAX_CONN_PER_UID = 32`** — per-tenant concurrent-connection ceiling,
  keyed on the SO_PEERCRED uid. A small fixed-size registry
  (`uid_conn_uid` / `uid_conn_count`, sized to `MAX_ACTIVE_CLIENTS` so it
  can't fill before the aggregate cap already rejected the connection,
  guarded by a `SpinMutex` on the cold connection-lifecycle path) tracks
  live connections per uid. `uidConnAcquire` is checked in `handleClient`
  AFTER the peer-cred read (needs the uid) and BEFORE the hello, so an
  over-cap connection receives a `TooBusy` line instead of a working
  `cap_id`, then the broker closes it. `uidConnRelease` (deferred, runs
  only on a successful acquire) frees the slot on disconnect.
- The aggregate-cap site and the new per-uid site now share a single
  `TOO_BUSY_LINE` constant.

**`src/mgf/hrb/exceptions.py`:** mapped the wire code `"TooBusy"` →
`OverloadedError` (it was previously unmapped — a pre-existing gap, since
the aggregate cap already emitted `TooBusy` and the SDK fell back to a
generic `MgfHrbError`). `TooBusy` is connection-budget back-pressure,
same family as `Overloaded`. `OverloadedError`'s docstring now lists the
real sources (lease ceilings, connection caps) instead of only OOM.

**Pins:** 2 Python contract tests (`tests/contract/test_conn_cap.py`) —
one uid is admitted up to the cap (fixture's connection + the rest) then
shed with `TooBusy`; closing an admitted connection frees a slot so a
backed-off tenant can reconnect (transient back-pressure). No Zig unit
test — `server.zig` is built as an executable (not a Zig test target),
so the registry is pinned at the wire level end-to-end.

### 2026-05-27 — Bounded leases: per-tenant + aggregate lease ceiling (R-T1-9A, Tier-1 #9, S5)

First slice of Tier-1 #9 ("bound everything," survivability review
§3.7). Before this, the lease table — the broker's ownership ledger —
had **no count ceiling**: `Err.Overloaded` only fired on allocator OOM,
which never triggers before the host dies. Since observe-mode claims
never conflict (the lattice permits unlimited concurrent observers on
one URN), a single tenant could claim until the table grew toward OOM
(U8/I5). The `MAX_LEASES = 64` referenced in two `core.zig` comments was
aspirational — it didn't exist as enforcement.

**`hrb-code/src/core.zig`** — two real ceilings, both enforced in
`claimImpl`'s under-lock Phase 1 (after the compat lattice, so a
conflicting claim still gets the precise `Err.Conflict`; before
reserving the placeholder, so only a claim actually about to add a lease
counts):

- **`MAX_LEASES = 128`** — aggregate live-lease ceiling PER Core (per bus
  worker, matching the bus-sharded write path; aggregate broker bound is
  `MAX_LEASES × worker count`). The hard DoS bound.
- **`MAX_LEASES_PER_UID = 64`** — per-tenant ceiling, keyed on the
  **SO_PEERCRED uid** (kernel-attested, unforgeable) recorded on each
  `Lease` as a new `peer_uid` field. NOT the client-supplied `holder` id,
  which a hostile client varies at will. Kept below the aggregate so a
  capped tenant never blocks a second tenant. Only enforced when a peer
  uid is present; in-process / test claims (null peer) are bounded by the
  aggregate ceiling alone.

Both shed new claims with the existing `Err.Overloaded` — back-pressure,
never unbounded growth (the S5 discipline; already mapped to
`OverloadedError` in the SDK). `peer.uid` is threaded from the peer-aware
claim entry points (`claimWithCapFromPeer` / `claimDetachWithCapFromPeer`
— the paths the real broker dispatch uses) into `claimImpl`; the
non-peer entry points pass `null`. **fds are bounded transitively** — a
raw channel rides a lease, so capping leases caps fds-from-leases.

**Pins shipped this commit:**
- **3 Zig behavioural tests** (`core_test.zig`): aggregate ceiling sheds
  with `Err.Overloaded` and a release reopens a slot (back-pressure is
  transient); per-tenant ceiling keys on the unforgeable uid (holder id
  varied every claim to prove it's NOT the key) and a second uid is
  unaffected (per-tenant, not aggregate); a dead holder's reclaim frees
  its quota (the ceiling tracks LIVE leases). `MAX_LEASES` /
  `MAX_LEASES_PER_UID` are now `pub` so tests reference them rather than
  hardcoding.
- **2 Python contract tests** (`tests/contract/test_lease_ceiling.py`):
  prove the per-tenant ceiling holds **end-to-end through the real broker
  dispatch** — i.e. the SO_PEERCRED uid actually flows from the accept
  boundary into `claimImpl` (the Zig tests call `claimWithCapFromPeer`
  directly; only the wire test exercises the full peer-cred path). One
  connection (one uid) caps at 64 → 65th is `Overloaded`; releasing one
  reopens the slot.

**Reclaim invariant tightened.** `holderDied`'s `MAX_RECLAIM_BATCH = 64`
is now justified by `MAX_LEASES_PER_UID`: a connection (one holder, one
uid) holds ≤ 64 leases, so the single-batch token harvest covers it.

Remaining #9 slices (this session): per-tenant connection cap (R-T1-9B),
slowloris progress-deadline (R-T1-9C), per-tenant rate-limit token bucket
(R-T1-9D), enumerate result bounds (R-T1-9E). Tracker:
`docs/inprogress/ROADMAP.md` §3 R-T1-9 / §4 row #9.

### 2026-05-27 — SDK forwards cap_id on enumeration ops (R-T1-11C, Tier-1 #11 complete)

The broker mechanism for capability-filtered enumeration shipped
in R-T1-11A/B/D — every enum op (`list`, `list_leases`,
`dump_all`, `inspect`, `inspect_full`, `dump_chain`) honours a
`cap_id` on the wire (cap_id=0 ⇒ legacy unfiltered; non-zero ⇒
intersect the requested scope with the cap's scope before
emitting URNs, S3 isolation). The SDK passed `cap_id` only on
the claim/raw/revoke/audit ops; the read-shaped ops omitted it.

**`src/mgf/hrb/_client.py`** — `Client.list` / `list_leases` /
`dump_all` / `inspect` (+ `inspect_raw`) / `inspect_full` (+
`inspect_full_raw`) / `dump_chain` (+ `dump_chain_raw`) now
forward the connection-bound `self._cap` (the cap id the broker
minted in its hello). Forwarding is now uniform across every
cap-aware op.

**Why this matters even though nothing narrows today.** Every
client currently holds the ROOT cap (scope `hw:/**`), so the
scope intersection is the request itself and no narrowing
occurs — back-compat is total. The win is forward-looking: when
per-client attenuated caps land (U-future), an attenuated
client's reads are filtered automatically with no further SDK
change. Without this commit, an attenuated client would silently
enumerate outside its grant — the exact S3 leak Tier-1 #11
closes.

**7 new unit pins** in `tests/unit/mgf/hrb/test_client.py`
(`test_*_forwards_connection_cap_id`): each read op carries
`cap_id == self._cap` on the wire; `inspect`/`inspect_full`/
`dump_chain` cover both the typed and `_raw` variants; a
cap_id=0 client forwards 0 (the no-cap sentinel — the SDK
forwards whatever the hello gave, no special-casing). Two
existing exact-match request assertions (`list`, `inspect`)
updated to include the field.

**Honest gap.** The end-to-end narrowing through a REAL
attenuated grant can't be exercised from Python yet — there is
no wire op to mint an attenuated sub-cap (`request.zig::Op` has
no `attenuate`/`mint`; per-client attenuation is U-future per
`server.zig`). That gap is now tracked as **R-CAP-ATTEN-WIRE**
in `ROADMAP.md`. The narrowing behaviour itself is pinned at the
broker layer (`facade_test.zig` `capPermitsNaming` + the wire
contract suites `test_wire_*_cap_filter.py`).

Tier-1 #11 is now fully closed: mechanism (R-T1-11A/B/D) + SDK
plumbing (this commit). Tracker: `docs/inprogress/ROADMAP.md`
§4 row #11 → ✅.

### 2026-05-27 — USB-02 Slice A closure pin (MODEL-01 conformance)

The FEEDBACK USB-02 paper scoped Slice A as "sysfs-discovery
USB worker"; the implementation was already shipped (the Zig
worker `hrb-code/src/linux_usb.zig` is 2151 lines; the
`kabi.list` / `kabi.inspect` dispatch already routes `hw:/usb/**`
URNs to it). The gap was the cross-side conformance test
between broker `inspect` and SDK `mgf.hrb.sysfs.snapshot_from_sysfs`
(shipped 2026-05-26).

**`tests/conformance/test_usb_sysfs_model01.py`** — 3 pins:

- `test_broker_inspect_matches_sysfs_snapshot` — for every real
  USB URN the broker enumerates, decode URN → sysfs path,
  call `snapshot_from_sysfs` on the path, build a Snapshot
  from the broker's inspect attrs, assert field equivalence
  (vid / pid / bus / serial), AND `same_likely` scores
  `SAME` for INTRINSIC pairs (devices with serial) /
  `≥LIKELY_SAME` for TOPOLOGICAL (serial-less). This is the
  MODEL-01 round-trip: broker side and SDK side MUST agree
  on what sysfs says.
- `test_check_usb_sysfs_doctor_row_present` — pins the
  `USB_SYSFS_READABLE` finding in `Client.doctor()` output.
- `test_broker_enumerates_usb_devices_when_present` — pins
  `Client.list("hw:/usb/**")` returns ≥ `lsusb` device count
  (per-interface tally ≥ per-device tally).

Marker: `conformance` (release-gated; needs a live broker +
real `/sys`).

**Validated on this host:** 15 USB URNs round-tripped, all
scored `SAME`. lsusb reports 9 devices (composite-device
interfaces account for the count difference). Doctor row
present.

USB-02 Slice A — ✅ closed. Slice B (claim + raw channel) is the
next USB worker phase; the rule from USB-03 (refuse-on-conflict
when an external daemon holds the device) lands enforcement-side
with that slice.

Tracker: `docs/inprogress/POST_PRESENTATION_TRACKER.md` G1
moved to §4 (Closed items) with the full per-acceptance-
criterion ledger.

### 2026-05-26 — presentation-layer hardening + test plan (max-paranoia round)

The DRAFT PR-* standard's reference SDK gets locked in for the
mgf-hrb + hrb-app pair via the five "hardened pair" criteria
defined in mgf-common's `docs/standards/PRESENTATION_LAYER.md §8.1`.
The standard remains federation-DRAFT pending a second sibling-
pair adopter; for this pair, PR-* is now a hard requirement.

**Hardening (S1-S8):**

* **S1** — `mgf.hrb.fmt` rejects `bool` / `float` / `None` /
  non-int with `TypeError` instead of silent coercion. Bool is
  always a programmer bug; float means a wire-format
  translation defect; non-int means broken input. Negative ints
  continue to clamp to 0 (defensive against broker bugs).
* **S2** — `import-linter` upper-pinned `<3.0`. A future major
  release that changes contract syntax could silently weaken
  PR-01 / PR-06 enforcement; deliberate bump required.
* **S3** — `mgf-hrb cli` subcommand-module imports moved into
  `main()`, AFTER the deprecation-banner emission. If a legacy
  subcommand's transitive dep is missing, the operator now sees
  the migration pointer FIRST + then the traceback (was the
  reverse).
* **S4** — `settings.socket_path` sanitised before interpolation
  into argparse `help=` text. A careless `MGF_HRB_SOCKET_PATH`
  with control chars no longer bleeds into `--help` output.
* **S6** — sys.path module-shadowing guard at `mgf.hrb.__init__`.
  Asserts the loaded `fmt` module is the installed SDK's own,
  not a rogue wheel via typosquat / sys.path injection.
* **S7** — new `scripts/audit_transitive_deps.py` walks the
  production dep closure via `importlib.metadata`; fails if any
  UI framework appears in mgf-hrb's transitive deps. Wired into
  `scripts/gates` as a hard gate.
* **S8** — new `scripts/verify_wheel_contents.sh` (pre-publish
  hook): asserts `tests/` absent from the built wheel + required
  SDK files present.

**Test plan (T1-T9):**

* **T1** `tests/conformance/test_pr_standard_sdk.py` — 8 per-rule
  pins (PR-01 / PR-04 / PR-06 / PR-07).
* **T2 + T3** `tests/unit/mgf/hrb/test_fmt_property.py` — 18
  Hypothesis property tests (full input domain + negative-type
  rejection); `test_fmt.py` flipped 3 defensive tests to expect
  `TypeError` (S1 contract).
* **T4** `tests/unit/mgf/hrb/test_fmt_concurrency.py` — 6
  concurrency pins (16 threads × 5k iter; no race; no
  exceptions; structural check that fmt has no new module-level
  mutable state).
* **T5** `tests/conformance/test_import_linter_alive.py` — 3
  meta-tests proving the import-linter gate isn't vestigial
  (synthetic violation in a tmpdir fixture; `lint-imports`
  must reject).
* **T6** `tests/perf/test_fmt_helpers_latency.py` — 5 perf pins
  (p99 < 1 µs per helper; cold-import < 5 ms). Current
  measurements: 68-240 ns/call; cold-import 158 µs.
* **T7** `tests/conformance/test_conformance_tracker_shape.py` —
  3 shape pins on the `MGF_STANDARDS_CONFORMANCE.md` §PR section.
* **T9** `tests/contract/test_fmt_canonical_output.py` — 5 wire-
  format pins on the operator-visible strings.

**Test count:** 138 → 366 (default suite). All four gates green.

### 2026-05-26 — presentation-layer foundation (Phase A of the multi-repo plan)

Five SDK-side commits that prepare mgf-hrb to be cleanly consumed by both
its in-tree CLI (today) and hrb-app's `view/` layer (tomorrow). Plan in
`~/.claude/plans/async-tumbling-waterfall.md`; phases B (hrb-app CLI
absorption) and C (the Magogi Foundation PR-* standard) follow.

* **`mgf.hrb.fmt`** — new module exporting four medium-agnostic
  formatting helpers: `format_uptime` (ms → ``"Hh Mm Ss"``),
  `format_duration` (µs → cascading-units), `format_rss` (bytes →
  binary-prefix), `format_severity_glyph` (Severity → canonical
  5-char glyph). Stdlib-only, pure functions, defensive on
  bad inputs. The reference implementation of the upcoming
  Magogi Foundation PR-06 rule. 24 pin tests.
* **Computed properties on existing dataclasses** — `StateSnapshot.uptime_human`,
  `StateSnapshot.memory_rss_human`, `AuditRecord.duration_human`,
  `AuditTailResult.outcome_summary()`, `BackendCapability.ops_dict()`.
  Single-source the formatting decisions every UI wants; eliminates
  the silent-drift class where CLI's "1h 2m" and GUI's "62m" disagreed.
  13 pin tests.
* **`AuditOutcomeSummary`** — new frozen-slots dataclass returned by
  `AuditTailResult.outcome_summary()`. Typed `(ok, err, other,
  newest_error)` tally replaces the inline loop the CLI was doing.
* **`BackendCapability.KNOWN_OPS`** — class-level `ClassVar[tuple[str, ...]]`
  with the canonical kabi-seam op-name list in display order. Future
  ops added on the broker flow through to every UI's grid automatically
  (eliminates the hard-coded list the CLI's `_doctor.py` used to carry).
* **import-linter contracts** — PR-01 (SDK forbids UI-framework imports:
  PySide6, qasync, textual, rich, prompt_toolkit, click) and PR-06
  (mgf.hrb.fmt forbids all UI frameworks — strictest narrowing of PR-01).
  Wired into `scripts/gates` so accidental UI-into-SDK contamination
  fails CI immediately, not at runtime.
* **`_doctor.py` dog-fooded** — the in-tree CLI now reads `snap.uptime_human`,
  `tail.outcome_summary()`, `BackendCapability.KNOWN_OPS` instead of
  duplicating the logic inline. Proves the SDK helpers work end-to-end
  before the CLI extracts to hrb-app.

Test count: Python 293 → 336 (+24 fmt pins, +19 computed-property/KNOWN_OPS
pins). Marker-gated suites unchanged. All four gates (ruff / mypy /
pytest+coverage / import-linter) green.

### 2026-05-26 — review-driven hardening (clusters A/B/C of the audit pass)

A six-agent code review (Zig audit, Python SDK audit, test-coverage map, concurrency audit, doc-drift audit, cleanup audit) surfaced ~200 findings across the codebase. The first three clusters shipped 2026-05-26:

#### Cluster A — concurrency correctness
- **`dupeShort` static-buffer race in PCI + USB inspect** (`hrb-code/src/linux_backend.zig`, `hrb-code/src/linux_usb.zig`) — both backends funnelled attr values through a shared 256/512-byte static buffer; two concurrent inspect calls from different client threads could see corrupt values. Each attr already has its own stack-local buffer; `dupeShort` deleted, callers pass the local slice directly to the synchronous `AttrSink.push`.
- **`LinuxUsbBackend.pump_lock`** — `Facade.drainEvents` invokes the backend without holding `Core.lock`, so two clients calling `drain_events` concurrently raced on `snap_cache` + `synth`. Added a backend-local SpinMutex guarding the pump body; claim-mutating ops keep going through Core.lock as before.
- **`pushFanout` lookup-callback contract** (`hrb-code/src/subscription.zig`) — pinned the no-re-entry rule on the user-supplied lookup; the registry's SpinMutex is non-reentrant and a careless callback would infinite-spin. Structural fix (snapshot + per-slot generation counter so eval could leave the lock) tracked as a follow-up.
- **`Core.raw` lock-release before backend IPC** (`hrb-code/src/core.zig`) — the three `raw` entry points now capture the token under `Core.lock` then drop the lock before delegating to `backend.rawChannel`. On a wedged helper (Tier-0 #3 watchdog window), other ops on the same worker no longer block for the full 5s deadline. `claim`/`release` keep holding the lock pending a placeholder-protocol commit.

#### Cluster B — DoS hardening
- **`MAX_PENDING_BYTES = 64 KiB`** (`hrb-code/src/server.zig`) — per-client `pending` buffer was unbounded; a single client streaming bytes without `\n` could OOM the broker. Past the cap the broker writes a typed `BadRequest` and closes.
- **`MAX_ACTIVE_CLIENTS = 128`** (`hrb-code/src/server.zig`) — thread-per-client accept loop was unbounded; new connections past the cap get a typed `TooBusy` so SDKs back off cleanly instead of seeing connection-refused. Atomic counter tracked in `active_clients`.
- **`SOCK_CLOEXEC` on listen + accept** — listen socket created with `SOCK.STREAM | SOCK.CLOEXEC`; `accept` upgraded to `accept4` with the same flag. The privileged USB helper subprocess no longer inherits the listen fd or any other client fd.
- **Audit ring overflow surfaced** (`hrb-code/src/audit.zig`) — added `Ring.dropped_total` counter, exposed via the new `state_snapshot.audit_dropped_total` field. SDK `StateSnapshot.audit_dropped_total` mirrors. `mgf-hrb doctor`'s broker-health pane shows a `warn:` line when non-zero. Operators now have a signal that security history is being overwritten.

#### Cluster C — SDK forward-compat
- **`StateSnapshot.workers` is now a tuple** (was a `dict[str, WorkerSnapshot]`). `WorkerSnapshot` carries its own `scope_prefix`. The class is now genuinely hashable (the `frozen=True` claim used to fail with TypeError at `hash()` call time because of the unhashable dict field).
- **`InspectFullResult.__hash__ = None`** — explicit unhashable marker so the limitation surfaces cleanly at `hash()` time. Docstring suggests `(urn, captured_at_ms)` as the cache key for "did this change?" workflows.
- **`.get()`-with-default across SDK projections** — `SupersededBy`, `ChainEntry`, `AuditRecord`, `WorkerSnapshot` constructions stopped using `r["field"]` hard-indexing. A future broker adding fields or renaming them no longer KeyErrors the projection.

### Doctor — broker-health pane

- **`mgf-hrb doctor` gains a broker-health pane** consuming `state_snapshot` + `audit_tail` (Part 13 §5.6.2 + §5.6.4). Surfaces uptime, op throughput (ops/sec over the last 60s), active connection/lease/subscription counts, audit-ring fill, RSS, and a short recent-activity summary with the newest error if any. The pane appears only when the cap carries `audit_read`; on caps that don't, the rest of the report still renders.
- **Test coverage:** `tests/unit/mgf/hrb/test_cli_doctor.py::test_doctor_renders_broker_health_pane_with_live_broker` spawns a real broker, runs `doctor` as the FIRST connection (so cap-binding succeeds), asserts the pane renders.

### Privileged-helper subprocess (D-031) — Tier-0 #2/#3/#4 wave

- **`mgf-hrb-usb-priv`** binary at `hrb-code/src/usb_priv_main.zig` — separate fork+execve'd subprocess that runs the 6 privileged USB ops (`acquire`/`acquire_detach`/`release`/`reset`/`raw_channel`/`control`). Communicates with the broker via `priv_ipc` binary protocol over a Unix socketpair; SCM_RIGHTS for the raw-channel fd. **Opt-in** today via `MGF_HRB_USB_USE_HELPER=1` env var.
- **`UsbBackendViaIpc`** at `hrb-code/src/usb_backend_via_ipc.zig` — backend façade that wraps the helper. Inner `LinuxUsbBackend` keeps handling read-only sysfs (enumerate / watch / drain_events / capabilities); privileged ops round-trip through the helper.
- **Tier-0 #2 — connection-bound capabilities (§3.3)** — `SO_PEERCRED` on every accepted client; cap binds to the first peer that uses it and refuses every other identity. CSPRNG-sourced root cap id; no more "guess id=1 and walk in". Hello handshake echoes the bound cap id.
- **Tier-0 #3 — supervisor watchdog (§2.2 / §3.3)** — `SO_RCVTIMEO=5s` on the helper socket; if the helper exceeds the deadline, the supervisor sends SIGKILL, waits with `waitpid`, then respawns on the next op. A wedged ioctl costs one bus worker, never the broker.
- **Tier-0 #4 — `revoke()` below the fd (§3.4)** — broker holds the underlying USBDEVFS file descriptor; `revoke()` calls `USBDEVFS_RELEASEINTERFACE` + `close`. The client's `dup`'d fd then returns ENODEV on the next syscall. New `steal` right gates the op; lease engine surfaces `Err.Revoked` distinct from `Err.InvalidToken`. PCI/VFIO variant plugs into the same wire surface once `vAcquire` lands on a real host.
- **Helper privilege-drop seam** — `dropPrivileges()` in the helper main: `PR_SET_NO_NEW_PRIVS = 1` immediately after socketpair setup, plus `closeInheritedFds` (skip stdio + HELPER_FD). Without setuid the drop is mostly a no-op today, but the seam closes the structural "helper has LEAST privilege of {worker, parent, env}" property called for in the survivability review §6.4.

### Part 13 — self-aware hardware introspection (21/21 sub-features)

The complete design at `hrb-design/mgf-hrb-self-aware-introspection.md` §9 catalogues the per-sub-feature commit SHAs. Headline additions:
- **`inspect` per-resource attrs** (Part 13 §5.2.1) — driver_name + driver_module surfaced.
- **`inspect_full(urn, depth)`** (§5.5.1) — bulk introspection via tree walk; bounded MAX_DEPTH / MAX_ENTRIES / MAX_FILE_BYTES with honest `truncated` flag.
- **`dump_chain(urn)`** (§5.5.2) — the resource's parent chain, from leaf up to root_complex / root_hub.
- **`dump_all(scope)`** (§5.5.3) — every URN in scope with its inspect attrs in one response.
- **`driver_info(driver_name)`** (§5.2.2) — bound devices + module info for a kernel driver.
- **`subscribe(scope, predicate) / unsubscribe`** (§5.4.2) — server-side event filter with an AST grammar; per-peer queues with overflow-honest drain-time gap synthesis.
- **`driver_bound` / `driver_unbound` event kinds** (§5.4.1) — netlink uevent parsing surfaces driver lifecycle.
- **`control(urn, verb, value)`** with per-(urn, verb) FIFO serialiser (§5.1.1) + `superseded_by` disclosure (§5.1.2).
- **`audit_tail(n)` + `state_snapshot`** (§5.6.1 / §5.6.2) — broker self-awareness; the ring + a process-wide counters snapshot. `audit_read` cap right gates both ops.
- **Wire envelopes:** top-level `fidelity` field on every read-only response (§7.2); `correlation_id` echo on every response (§5.6.3 / §7.1).
- **`predicate_grammar_version`** on capabilities response (§7.3) so SDKs can detect grammar drift.
- **Driver-interface registry** at `hrb-code/src/data/driver_interfaces/<driver>.json` (not `.yml` — the design corpus referencing `.yml` is being back-patched). VFIO + DRM + V4L2 ioctl entries; variable-size HIDIOC* schema extension; CI currency gate via `scripts/regenerate_ioctl_registry.py`.

### Test infrastructure 2.0

- **`scripts/mtest`** budget-aware test supervisor — stdlib-only Python subprocess driver that outlives pytest crashes. 4-tier model (smoke / contract / use-case+conformance / perf+stress+e2e), `--tier N`, `--budget T`, atomic JSONL records, 3-of-5-failures auto-quarantine with 3-pass eviction, per-failure detail bundles at `.gates/last-run/failures/<id>.txt`, `--self-check` that verifies the supervisor itself before any real test run.
- **Foundation testing standard** at `docs/testing/FOUNDATION_TESTING_STANDARD.md` codifying the spec for every Magogi project: tier model, supervisor contract, structured records, quarantine policy. mgf-common adopted 2026-05-26.
- **Reliability pass** (`a57758d`): pytest-timeout fires on hangs, broker-freshness check fails-loud on stale binaries, four duplicate broker fixtures consolidated into `tests/conftest.py`, coverage pin enforced.
- **`docs/testing/DIAGNOSING_HANGS.md`** — operator playbook for the test suite.

### USB worker — Phase U1 + U2a

- **`hrb-code/src/usb.zig` (Phase U1)** — USB model helpers: `Interface` / `Composite` structs (interfaces-first per `hrb-design/mgf-hrb-usb-worker.md` §2.4 / D-013), `urnForInterface` + `urnForDevice` building the canonical `hw:/usb/bus<N>/port-<chain>/dev/if<K>` URN (§2.2), `snapshotForInterface` picking `identity_class` per §2.3 (intrinsic with serial, topological without), `seedComposite` for the FakeBackend. 14 pin tests at `usb_test.zig` covering URN grammar, blast-radius-one, identity-class assignment, and comparator cross-properties (Recovery→DFU on USB-built Snapshots scores `likely_same`).
- **`hrb-code/src/linux_usb.zig` + `mgf-hrb-usb-scan` (Phase U2a)** — `LinuxUsbBackend` walks `/sys/bus/usb/devices` via raw syscalls (Level-2 path; no libusb/libudev). Emits one Resource per interface with honest fidelity tags. Sysfs name parser handles root-hub interfaces (`1-0:1.0`), nested hubs (`3-9.4:1.0`), deep chains, device-only skips, `usbN` alias skips, garbage rejection — six in-file parser tests + six real-hardware tests. `mgf-hrb-usb-scan` executable: direct-substrate reporter, no broker needed; prints one URN per interface with its fidelity tag — the U2 exit gate. Other vtable ops return `Unsupported` honestly: U2b adds netlink `watch`; U3 adds acquire/release/reset/raw via the privileged helper.
- Zig test count: 110 → 124 (U1) → 136 (U2a). Subsequently grew to 336 through Part 13 + Tier-0 + the 2026-05-26 review-driven wave; see commit history for the per-feature increments.

### Developer setup — docs

- **`STARTHERE.md` gains a "Developer setup — the commands you'll actually run" section** covering the venv-zig alias (`alias zig='python3 -m ziglang'`), the cwd gotcha (`hrb-code/zig-out/` not the repo root), the four-gate Python invocation, the embedded-CLI usage, and an "after pulling new changes" reminder for worktree branches.
- **`hrb-code/README.md` "Run it" block** rewritten with the same alias + cwd guidance, including the `failed command:` noise note (Zig's per-test sub-process stderr is not authoritative — the final `Build Summary` is).

### Conformance — L1 → L2 flip

- **`HrbSettings`** at [`src/mgf/hrb/config.py`](src/mgf/hrb/config.py) — subclass of `mgf.common.config.BaseAppSettings` with env-prefix `MGF_HRB_`. Carries `socket_path` (default `/tmp/mgf-hrb.sock` — matches the skeleton broker; fixes the wrong `/run/mgf-hrb.sock` default that lived in the CLI) and `default_deadline_ms`. Exported from `mgf.hrb` and listed in `PUBLIC_API.md`. Closes CF-01 / -02 / -08 / -10.
- **Embedded CLI now bootstraps via `mgf.common.bootstrap()`** (EH-04 ✅) and declares module loggers in every command module with lazy `%`-formatting and structured `extra={...}` (LG-01 / -08 / -09 ✅ for the CLI). `build_parser(settings: HrbSettings | None = None)` takes injected settings for tests (CF-08 / CF-10).
- **LG-* declined for the SDK package** (not the CLI) — written rationale in conformance tracker §3: the client surfaces by typed exceptions per EH-01 and emits no logs by design. Carve-out, not blanket decline.
- **8 new Python unit tests** (`test_config.py` + `test_cli.py`) pinning the precedence chain and injection points. Test count: 8 → 16 Python tests.
- **Declared level flipped from L1 — Lite to L2 — Standard** in `README.md` and the conformance tracker. The four-gate run is green: ruff clean, mypy --strict clean, pytest 16/16, coverage ≥80%.

### Restructure — federation-sibling library shape

- **Python SDK moved to `src/mgf/hrb/`** (PEP-420 namespace; no `__init__.py` at `src/mgf/` so other `mgf.*` siblings can contribute). Split into `_client.py` (Client + Lease + transport), `domain.py` (frozen dataclasses — Resource, ClaimMode, etc.), `identity.py` (Snapshot + `same_likely`, Python mirror of `identity.zig`), `exceptions.py` (typed hierarchy extending `mgf.common.exceptions.AppError`). The single-file `hrb-code/sdk/mgf_hrb.py` is gone.
- **Embedded CLI at `src/mgf/hrb/cli/`** with `mgf-hrb` console script in `pyproject.toml`. Subcommands: `scan` (enumerate + render), `info` (honest `Unsupported` until the inspect op codes), `doctor` (precondition diagnosis with structured `Fix` rendering, the operator-facing surface FEEDBACK `TOOLS-01` requested). Designed so the cli/ sub-package extracts cleanly later as a separate `mgf-hrb-cli` package — nothing else under `mgf.hrb` imports from `mgf.hrb.cli`.
- **`tests/` tree** mirrors `src/` per TS-01: `tests/unit/mgf/hrb/test_identity.py` pins the Python comparator against the same iFig scores the Zig suite pins (drift detection for D-035). `tests/e2e/test_e2e.py` is the moved Python ↔ Zig integration test (now uses the new `Client`/`Lease`/typed-exception surface).
- **Examples directory** (`examples/01_list_devices.py`, `02_claim_and_use.py`, `03_track_identity.py`) — each runnable, each linked back to the canonical recipe that motivates it.
- **Documentation layout** updated to federation-sibling shape: `STARTHERE.md` added; `docs/CLAUDE.md` → `docs/claude/CLAUDE.md` (DOC-07); `docs/standards/README.md` is a pointer-only stub to `mgf-common` (DOC-02 §2.0.4); `PUBLIC_API.md` + `PUBLIC_API.json` track the `__all__` surface (DOC-08 / AP-10).
- **`pyproject.toml`** declares the package via hatchling (`packages = ["src/mgf"]`) and the console script (`mgf-hrb = "mgf.hrb.cli:main"`). The four-gate configuration is preserved.

### Architectural decision on UI layer

GUI support is **not** added to the SDK as a presentation layer. The library exposes event-loop-friendly primitives (`monitor_fd()` per design Part 8.3; reserved in the public API but Tier-1 wiring), and a future `mgf-hrb-gui` package depends on the SDK + PySide6 in its own repo. Keeps the SDK free of Qt/web deps and preserves DP-01 layering.

### Security

- **S2 deadlines on the kabi seam (D-029 / Tier-0 #3)** — every backend op (`enumerate`/`query_topology`/`watch`/`acquire`/`release`/`reset`/`map_region`/`raw_channel`/`enforce_cap?`) now takes a `kabi.Deadline`. `Err.Timeout` is a typed outcome — atomicity holds through the timeout path (D-012): a `claim` whose hardware-acquire step times out commits no lease, leaves the resource safe and claimable. The `FakeBackend` declares per-op work durations via `slow_*_ms` toggles; if the caller's deadline can't cover the declared work, the op returns `Err.Timeout` before touching state — making the contract testable without a real frozen device. The Python SDK accepts an optional `deadline_ms` kwarg on `claim`/`release`/`raw`; `0` (default) maps to `Deadline.none` on the Zig side; the trust gate rejects garbage values (wrong type or above the 1-hour cap). 12 new tests pin the contract (S2 timeouts in `conformance.zig` and `core_test.zig`; trust-gate handling in `request_test.zig`; SDK plumbing in `test_e2e.py`). **The implementation note:** the contract is in force at the *seam level*; the second half of the enforcement story — running blocking syscalls in a killable context so a wedged device kills one worker, not the broker — is the watchdog process model, a separate hardening pass.

- **S1 trust gate** ([`hrb-code/src/request.zig`](hrb-code/src/request.zig)) — the hardened decode layer the survivability review (Part 12 §3.2) called Tier-0 #1. Every byte from the client passes through typed extractors before becoming a `Request`; the dispatcher in [`server.zig`](hrb-code/src/server.zig) never reads `std.json.Value`. Closes the critical S1/U1/U2 gap where the skeleton's direct `.string`/`.integer` access panicked on type-confused input. Pinned by [`request_test.zig`](hrb-code/src/request_test.zig) — 29 cases covering type confusion, missing fields, oversized input, out-of-range integers, plus a 7 000-iteration deterministic-seed fuzzer asserting random bytes only ever yield `BadRequest` or a valid `Request`, never a panic.

### Added

- **MODEL-01 shipped** — `hrb-code/src/identity.zig` brings the runtime identity-continuity comparator into the model layer (the FEEDBACK paper of the same name, accepted in this pass). `Snapshot` captures a device's identity at a moment in time (URN, identity-class, vid, pid, optional serial, bus + port-chain, captured-at); `sameLikely(a, b, opts) → Confidence` returns `{SAME, LIKELY_SAME, AMBIGUOUS, DIFFERENT}` per the weight table in `hrb-design/mgf-hrb-usb-worker.md` §4.3. Algorithm validated against the iFig DFU sequence from `mgf-usb` v0.2.5: Recovery↔DFU scores 11 → `LIKELY_SAME`; Normal→Recovery (serial intact) scores 15 → `SAME`. `sameLikelyExplained` returns each scored signal for tooling. 18 tests pinning the algorithm against its spec.
- Federation artifacts to bring the repo up to current `mgf-common` v0.38 expectations: `README.md`, `LICENSE` (MIT), `CHANGELOG.md`, `SECURITY.md`, `USERS.md`, and `docs/inprogress/MGF_STANDARDS_CONFORMANCE.md` (declared L1 — Lite). Closes the DOC-17 contract gap `mgf-common catch-up` reported.
- `mgf-common` pinned in `pyproject.toml` (`>=0.38,<0.39`) — the project's first federation-tracked dependency. Four green gates (ruff / mypy strict / pytest filterwarnings=error / coverage ≥80%) configured.
- Phase 1–7 implementation skeleton in [`hrb-code/`](hrb-code/): `mgf-hrb-model`, `mgf-hrb-kabi` (the 9-op seam + behavioral contract), `mgf-hrb-core` (lease engine, atomic grant, G3 ghost-free reclamation), `mgf-hrb-cap` (monotone attenuation, `raw` as its own right), `mgf-hrb-rt` (liveness, ring at ~822M items/sec, SCM_RIGHTS fd-passing — the G4 survivability dividend), `mgf-hrb-linux` (real PCI enumeration via raw syscalls; honest-Unsupported acquire), `mgf-hrb-facade` (router + supervisor, two-worker constellation), and `mgf-hrb-sdk-py` (zero-FFI Python client over a Unix socket).
- The conformance suite ([`hrb-code/src/conformance.zig`](hrb-code/src/conformance.zig)) — behavioral seam tests runnable against any backend (today: fake + linux; future: `mgf-hrb-youros`).
- Complete design corpus in [`hrb-design/`](hrb-design/): the 241 KB spine ([`mgf-hrb-complete-design.md`](hrb-design/mgf-hrb-complete-design.md)) + four deepening docs (kernel brief, family-B, privileged-core hardening, USB worker) + the survivability review (S1–S5 first-class guarantees, ~30 findings converging on 4 primitives).
- [`DECISIONS.md`](DECISIONS.md) — 35 load-bearing ADRs (D-001..D-035) cited back into the design.
- [`FEEDBACK.md`](FEEDBACK.md) — design-conversation log with 5 accepted papers (`USB-01`, `MODEL-01`, `TOOLS-01`, `SDK-01`, `LINUX-01`).
- [`recipes/`](recipes/) — three consumer-pattern recipes: claim-for-VFIO-passthrough, USB event monitor with role-interfaces, track-a-device-across-a-mode-change.

### Known gaps (pre-`0.1.0`)

- VFIO `acquire` is hardware-gated (needs `/dev/vfio` + IOMMU); `linux_backend.zig` returns honest `Unsupported` until a real host exercises it. The PCI/VFIO branch of `revoke` will plug into the same wire surface that USB uses today — no protocol change needed.
- `mgf-hrb-usb` worker — direct-mode `LinuxUsbBackend` ships (Phase U3a complete); Phase U4 follow-ons (reset-escalation ladder fully wired against a stub-firmware fake, interrupt-rate watchdog → Quarantined state transitions) still open.
- Phase 6 of Part 13 (module helper sibling) needs the new `mgf-hrb-modules-priv` repo + its own design conversation. Explicitly out of scope for this quarter.
- `mgf-hrb-cpu` / `gpu` / `sdk-zig` / `mgf-hrb-youros` — designed, contracts fixed, deferred to post-Phase-7.

### Next survivability passes (Tier-0 remaining)

- **Tier-0 #5** — asymmetric shared-memory trust + per-tenant private regions (§3.5). The last remaining Tier-0 item; #1 (`f988500` trust gate), #2 (`47e2cf3` + `dd69abc`), #3 (`dc7d481`), and #4 (`61de2b4`) all shipped in this push wave.

---

[Unreleased]: https://example.invalid/compare/HEAD
