entropyfs 0.5.2

Entropy-native Linux filesystem: persist irreducible state, materialize structure, preserve exact bytes.
# Write path

## 1. Request flow

```text
FUSE write(ino, offset, data)  (write-through; writeback off by default)
  → fuse::file::write
  → per-inode mutation lock (ADR-0013)
  → merge into 64 KiB logical extents:
        for each affected extent boundary:
            old = materialize(existing extent)     (or ZERO for holes)
            new = merge(old, offset, data)
            rep = encode_candidate(new)            (bounded search)
            validate: materialize(rep) == new      (exact, always)
            stage descriptor + payload records
  → transaction.commit()
  → ack
```

## 2. Foreground candidate pipeline (bounded, latency-conscious)

```text
1. exact dedup        content index hit → EXACT_REF (verify length + bytes)
2. ZERO / FILL / PERIODIC checks
3. SPARSE / PALETTE cheap structural candidates (rank/unrank, u128-checked)
4. BASE_RESIDUAL vs previous version / adjacent / file-family base
   (only when a basis exists and depth < 4)
5. rANS candidate     per-chunk model, exact cost
6. RAW                always available (escape hatch)
winner = min over candidates of J (ADR-0010), policy-aware
```

Foreground budget: fixed wall/budget counters (e.g., at most one rANS trial
plus structural trials; configurable). Deep searches are *not* foreground
work — they belong to the background optimizer.

## 3. Validation before commit (non-negotiable)

Every candidate is materialized and compared byte-for-byte (or by BLAKE3
of the materialized output) against the target chunk before its descriptor
may be committed. The invariant is enforced in one place
(`optimizer`/`store::transaction` validation gate) and tested adversarially
(fault-injected wrong-descriptor tests).

## 4. Partial extents and truncate

- Writes not aligned to chunk boundaries read-modify-write the affected
  chunk(s). Chunk boundaries are never user-visible (ADR-0006).
- `truncate` to a shorter size: the trailing partial chunk is re-encoded
  with the cut bytes; extents beyond the new EOF are dropped from the
  extent tree (the objects remain until GC — immutable store).
- `truncate` to a longer size: the extended region is a hole (ZERO).
- `fallocate(0)` (allocate) punches nothing physical — extents are
  assigned only on write; `fallocate(PUNCH_HOLE)` drops extents in range.

## 5. fsync semantics

`fsync` on an inode first performs a transaction commit of any pending
mutations (the write-through model means writes are already committed per
request; fsync then flushes the *superblock/segment* durability) and only
then returns. This matches the commit protocol ordering in
`docs/architecture/transaction-model.md`.

## 6. The background optimizer (Phase 4+, architecture fixed now)

```text
cold extent (age/access heuristic or DSFB recommendation)
  → deep candidate generation (rank/unrank families, residuals,
    universes, rebase, densification)
  → exact cost measurement
  → validate: hash(materialize(new)) == hash(materialize(old))
  → generation-CAS check (discard if a newer write landed)
  → commit replacement descriptor (same logical content ID)
```

The optimizer never defines correctness; it only proposes validated
alternatives. It runs on idle time with a configurable CPU budget, and is
throttled near the GC watermark (ADR-0009).