diff --git a/README.md b/README.md
index dd2ea70..486a09c 100644
--- a/README.md
+++ b/README.md
@@ -30,7 +30,7 @@ MCP, A2A, webhooks and ActivityPub standards solve different problems. EEP compo
- **MCP** lets an LLM *call a tool*. EEP lets an LLM *follow an entity over time* — discover it, subscribe and react to verified events from it.
- **A2A** lets two agents *collaborate on a task*. EEP defines how either agent learns that an entity changed at all.
-- **Plain webhooks** require a custom protocol per publisher (auth, signing, retries, replay window). EEP is one wire format with HMAC-signed delivery, SSE and a 60-second replay window — implemented once, reusable everywhere.
+- **Plain webhooks** require a custom protocol per publisher (auth, signing, retries, replay window). EEP is one wire format with signed delivery, SSE, and event replay with a minimum 24-hour retention window — implemented once, reusable everywhere.
- **ActivityPub** federates accounts in a social graph. EEP delivers state-change events from any entity (person org, agent, product, listing) to authorized subscribers, with optional payment / credential / identity gates.
- **DIDs and Verifiable Credentials** identify *who* an entity is. EEP defines *what they tell their subscribers* and *how subscribers verify it*.
diff --git a/ROADMAP.md b/ROADMAP.md
index 3252972..05764aa 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -26,9 +26,11 @@ Stabilization, hardening, and ecosystem unblocking. Drives toward the
SBOM, sigstore/cosign keyless signing, GitHub Action SHA pinning
via Renovate
- [x] DCO enforcement on every commit to `main`
-- [ ] **Offline conformance fixtures** under `tests/conformance-fixtures/`,
- consumable by `@eep-dev/compliance-cli --fixtures` without booting a
- live publisher
+- [x] **Offline conformance fixtures** under `tests/conformance-fixtures/`,
+ consumable by `npx @eep-dev/compliance-cli --fixtures
` without
+ booting a live publisher. The vectors and the release tarball existed
+ before; the CLI flag that made them runnable by anyone outside this
+ repo did not.
- [ ] **Python feature parity**: `eep-gates` (proof verifier, commerce,
service listing), `eep-middleware` (Flask/FastAPI/Django adapters)
- [ ] `py.typed` markers on every Python package; `mypy --strict` clean
@@ -42,8 +44,15 @@ Stabilization, hardening, and ecosystem unblocking. Drives toward the
Quality of life and adoption surface area.
-- [ ] **Monorepo migration to pnpm workspaces** with shared
- `tsconfig.base.json` and a single `pnpm -r test` entry point
+- [x] **Monorepo migration to pnpm workspaces** with shared
+ `tsconfig.base.json` and a single `pnpm -r test` entry point —
+ `pnpm-workspace.yaml`, `tsconfig.base.json` and the root
+ `packageManager: pnpm@9.15.0` all landed. What remains is narrower and
+ tracked separately below.
+- [ ] **Consolidate to a single `pnpm-lock.yaml`.** Per-package
+ `package-lock.json` files still coexist with the pnpm workspace because
+ CI invokes per-package `npm ci`; moving CI to `pnpm -r install` removes
+ N redundant dependency resolutions per run.
- [ ] **Schema → TypeScript / Pydantic codegen** with a CI drift gate
(no hand-maintained types diverging from `schemas/v0.1/*.json`)
- [ ] WebCrypto shim in `@eep-dev/signer` for edge runtimes
@@ -120,6 +129,19 @@ roadmap effort, and instead defers to the named external standard:
---
+## Keeping this file honest
+
+A roadmap that drifts from the tree is worse than no roadmap: it hides
+finished work and misrepresents what is left. Two entries had drifted in
+opposite directions — the pnpm migration had shipped while listed as pending,
+and the offline fixtures read as unstarted when only a CLI flag was missing.
+
+When updating an item, check the tree rather than memory:
+
+- "Done" needs a file, a command, or a CI job that demonstrates it.
+- "Not done" needs a check that it is genuinely absent — a partially shipped
+ item should be split so the remaining work is visible on its own.
+
## How this roadmap is updated
- Items move from **Now** → **Next** → **Later** → **v1.0** → done
diff --git a/decouple.py b/scripts/decouple-more-md-branding.py
similarity index 75%
rename from decouple.py
rename to scripts/decouple-more-md-branding.py
index ecdf2a2..10a9919 100644
--- a/decouple.py
+++ b/scripts/decouple-more-md-branding.py
@@ -1,9 +1,20 @@
+#!/usr/bin/env python3
+"""One-off de-branding pass from the more.md extraction.
+
+EEP was extracted from more.md's codebase and open-sourced; this script
+rewrote the remaining more.md identifiers to neutral examples. It has already
+run, and is kept only so the substitution list is auditable — if a stray
+more.md reference turns up, this records what the mapping was.
+
+It is not part of any build or test. Moved out of the repository root, where
+it was the first thing a new contributor saw.
+"""
import os
import re
REPLACEMENTS = [
(r"Copyright 2026 more\.md", "Copyright 2026 EEP Contributors"),
- (r"hello@more\.md", "security@eep.dev"),
+ (r"hello@more\.md", "hello@eep.dev"),
(r"MoreMDEntityCredential", "EEPEntityCredential"),
(r"more-md SDK", "example SDK"),
(r"did:web:api\.more\.md", "did:web:api.example.com"),
diff --git a/tests/conformance-fixtures/README.md b/tests/conformance-fixtures/README.md
index 5797bee..ac0aa00 100644
--- a/tests/conformance-fixtures/README.md
+++ b/tests/conformance-fixtures/README.md
@@ -20,7 +20,7 @@ tests/conformance-fixtures/
├── envelope/ ← EEP/CloudEvents event envelope shapes
├── signature/ ← HMAC sign + verify, replay window, multi-signature
├── gates/ ← 402 / 403 / 429 / 451 response shapes; access resolution
-└── subscription/ ← subscribe/unsubscribe request and response shapes
+└── subscription/ ← subscribe request shapes, SSRF rejection, leases, filters
```
Every fixture directory contains either: