Skip to content

Add design specs for the provider architecture and permission model epic - #986

Draft
aram356 wants to merge 24 commits into
mainfrom
specs/providers-and-permissions
Draft

Add design specs for the provider architecture and permission model epic#986
aram356 wants to merge 24 commits into
mainfrom
specs/providers-and-permissions

Conversation

@aram356

@aram356 aram356 commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

What this is

Five design specs for the provider/permission epic (#777#782), written after review of PR #838 so the next implementation pass has a normative behavioral contract instead of discovering design decisions inside a 67-file diff. They live in docs/superpowers/specs/ per the existing convention. Where a spec contradicts PR #838 — or one of the source issues (each spec ends with an explicit divergence table) — the spec is authoritative.

Spec Covers
pluggable-providers-design EC/device/geo provider architecture: full identity lifecycle contract (mint / parse-canonicalize with provider-declared equivalence / canonical graph key / optional cluster prefix / tombstone), declarative namespace descriptors core proves disjoint, version-rotation schema, active-writer + legacy-reader switching with confirmation-by-presentation rewrite, graph-store startup requirement and no-active-until-commit, runtime failure matrix, adapter-capability matrix, four-adapter parity
permission-model-design Policy as [permissions] in trusted-server.toml (not build-time YAML), enforced-vocabulary rule, three-class signal taxonomy with regime-scoped grant evidence, normative normalization matrix preserving current conflict-mode/expiry/proxy semantics, exhaustive withdrawal triggers with a family revocation record, concrete raw-EC egress inventory (path → permission table), stored-provenance S2S authority with legacy fail-closed, regime-based auction dispatch matrix including the raw-TCF arm, fail-closed jurisdiction resolution with an acknowledged static-jurisdiction escape hatch
provider-migration-rollout-design Behavior-preservation matrix (16 rows incl. US grant-signal cases and declared hardenings; silent changes are defects), identity stability via deterministic HMAC-prefix vectors, dual-read config release with strict reader-first ordering, graph-schema expand-contract rollout, per-adapter committed preserving fixtures run through the decision matrix in CI, telemetry with retirement thresholds
client-cycle-ec-resolve-design Threat model and bar for the browser-resolve EC provider type #838 shipped undeclared: exact-Origin/CSRF authorization, session-bound replay handling with stated reservation states, identity-graph invariant, existing-identity semantics, bounded inputs. On hold until its open questions get a dedicated issue
integration-response-header-hook-design #782 sharpened: structured mutation operations core validates and attributes, three-stage ordering ending in an inviolable cache/privacy invariant pass, reserved surface incl. framing/hop-by-hop headers and cookie-name granularity, response-eligibility matrix, panic-is-fatal on wasm, lands only with a real consumer

Key decisions encoded

  • Policy lives in trusted-server.toml, validated at ts config push and startup; absent policy falls back to a compiled-in everything-requires_signal, regime = "gdpr" posture.
  • Opt-out signals always override a consenting TCF string and are honored globally; grant evidence is regime-scoped so US non-opt-out values cannot authorize identity under GDPR rules.
  • EC providers own their identifiers end to end (parse, keys, tombstoning); permission gating covers minting and identity use only — parse and revocation always run. Geo is ungated because it is circular (it feeds resolution); device is ungated by recorded decision (security classification authorized by operator selection, with fingerprint-derived buyer-facing row fields removed pending a vocabulary extension).
  • Every raw-EC egress is inventoried and gated (user.id, derived IDs, page bids, forwarding, identify, sync), not only EIDs; batch sync is authorized by stored provenance recomputed against current policy, and legacy rows fail closed until backfilled.
  • Withdrawal writes a family revocation record first — durable intent, sibling discovery, and fail-closed read marker in one key; the migration story is expand-contract for the graph schema and dual-read for config, with strict reader-first ordering.
  • PR Add pluggable Edge Cookie, device, and geo providers gated by a permission model #838's host-signals and client-fixed providers are deliberately not carried over; configs selecting them fail loudly.

The specs have been through three review rounds (an adversarial fresh-eyes pass plus two maintainer reviews); the commit history records each round's findings and fixes.

aram356 added 2 commits July 31, 2026 00:06
Five specs covering the work proposed in PR #838 (issues #777-#782), written
so the next implementation pass has a normative behavioral contract:

- Pluggable providers: identity lifecycle contract (mint/recognize/hash/
  tombstone), trait minimalism, adapter parity, validation table
- Permission model: signal precedence (opt-out over TCF), fail-closed
  jurisdiction resolution, policy file validation, decision-matrix testing
- Migration and rollout: behavior-preservation matrix, ID stability
  vectors, loud-failure requirements, operator recipes
- Client-cycle EC resolve endpoint: threat model and prerequisites; on
  hold until its open questions get an issue
- Integration response-header hook: #782 contract with ordering and
  collision policy, ships only with a real consumer
Self-review of the five specs (own pass plus an adversarial fresh-eyes
pass) surfaced fixes applied here:

- Policy location reversed per maintainer decision: the permission policy
  is a [permissions] section of trusted-server.toml flowing through the
  config-store pipeline, not a build-time-embedded YAML. Overrides name
  acquisition rules directly instead of +/- sigils, and a rules.default
  entry separates resolved-but-unlisted countries from geo default_country.
- Removed a circular requirement: geo and device providers are inputs to
  permission resolution and cannot be gated on its output; the enforcement
  gate is EC-only and the decorative required_permissions declarations are
  dropped from those traits.
- Fixed the identity-stability guarantee: the EC id has a random per-mint
  suffix, so known-answer vectors pin the deterministic 64-hex prefix,
  recognition of existing cookies, and hash-prefix semantics instead of
  full identifiers.
- Split the KV key contract into the verbatim graph-row key and the
  deliberately-colliding hash prefix that IP-cluster trust depends on.
- Declared previously silent behavior changes in the migration matrix:
  global opt-out honoring (including tombstones) and the fate of
  non-regulated countries, with a preserving recipe for the latter.
- Sequenced the geo neutral-default flip into the permission model PR so
  no intermediate step zeroes EC issuance under the current fail-closed
  gate.
- Resolved smaller contradictions: withdrawal triggers made exhaustive
  (including denied-baseline and policy-edit cases), jurisdiction
  consistency requirement now covers both legacy lists with explicit
  exceptions, ISO rule-key validation made decidable, response-header
  reserved surface defined at cookie-name granularity for Set-Cookie, and
  the host-signals provider's removal made explicit with config rejection.
@aram356
aram356 marked this pull request as draft July 31, 2026 08:02
aram356 added 4 commits July 31, 2026 01:41
…ssion specs

Blocking findings from review of PR #986, all addressed:

- Raw EC egress is now a first-class enforcement point with a mandatory
  egress inventory (user.id, derived request IDs, page bids, proxy/click
  forwarding, identify, pull/batch sync, graph access): bidstream egress
  requires both purposes, first-party identity operations require
  store-on-device, revocation is exempt, and no-provider mode never
  vacuously allows an existing cookie to egress.
- The behavior-preserving recipe carries the complete policy table plus a
  delta; a partial permissive-default-only policy is called out as the
  trap it is, and the exact recipe text becomes a CI fixture run through
  the decision matrix.
- The EC permission gate is split: it covers minting and identity use
  only; parse, canonicalization, and tombstoning always run, with a
  spy-provider test - a blanket gate would block withdrawal in exactly
  the state an opt-out produces.
- Provider switching gets active-writer/legacy-readers semantics
  ([ec] legacy_providers) so old identities keep resolving and stay
  withdrawable; unmatched cookies never egress.
- The lifecycle contract now distinguishes the canonical graph key
  (provider-owned canonicalization, equivalent envelopes collapse) from
  the cluster prefix, which must be a literal byte prefix of the graph
  key because cluster sizing is a KV prefix listing; cluster support is
  an optional capability with an explicit degradation policy.
- Device gating rationale corrected: only geo is circular; device is
  ungated by decision (security classification authorized by operator
  selection), with the boundary stated - uses beyond security
  classification need a vocabulary extension and a gate.
- Withdrawal triggers made consistent (TCF refusal withdraws under
  requires_signal or denied), and a withdrawal-durability contract
  added: tombstones first, cookie expiry only on success, browser-side
  durable signals as the retry queue, fault-injection tests.
- A signal-normalization matrix is now required (dual-TCF conflict
  modes, expiry, proxy mode, KV fallback, exact GPP fields), and
  malformed-but-present records fail closed for acquisition instead of
  degrading to absent.
- Auction jurisdiction class is an explicit per-group regime attribute
  (gdpr / us-privacy / none), never inferred from purpose flags, and a
  first-class enforcement point.
- The no-geo acknowledgment guard now keys on any enabled jurisdiction
  consumer, not only EC-provider selection.
- Policy validation additionally requires rules.default when the section
  is present, rejects empty sections and case-insensitive duplicate
  keys, and canonicalizes default_country.
- Resolve endpoint: exact Origin-allowlist membership or session-bound
  CSRF token (Sec-Fetch-Site demoted to defense-in-depth), real replay
  mitigation (session nonce or one-time consumption), and bounded-input
  requirements with 413 boundary tests.
- Response hook: structured mutation operations that core validates and
  attributes (no raw header-map access), framing/hop-by-hop headers
  reserved, and a normative response-eligibility matrix.
- Each spec now carries an explicit divergence table against its issue
  (#778, #779, #782) so there is one acceptance contract.
- Non-blocking clarifications folded in: geo lookup-failure residual
  declared and metered, deterministic entropy required in conformance
  tests.
…and rollout gaps

Blocking findings from the second review of PR #986:

- Signal taxonomy gains a grant-signal class (TCF consent, explicit GPP
  non-opt-out, US Privacy present-and-not-opted-out including N/A) so a
  requires_signal US rule reproduces today's no-signal-blocks /
  explicit-non-opt-out-grants behavior, which the two-class model could
  not express; migration matrix gains rows 3a-3c and the example US
  group changes to requires_signal.
- Auction dispatch gets a normative regime matrix (gdpr / us-privacy /
  none across consent, opt-out, malformed, expired, absent states), the
  compiled fallback gains regime = gdpr, and blocked dispatch means no
  outbound request at all.
- The normalization matrix now states outcomes instead of subjects:
  restrictive/permissive synthesize per purpose, newest selects whole
  records, expired records are absent entirely, valid-beats-malformed
  within a family, KV fallback is live-wins with TTL-bounded staleness
  and an exempt consent-state lookup, mirror mode loses to request
  records, and GPP fields are enumerated per section.
- The egress inventory is a concrete path -> permission table: proxy /
  click / Testlight forwarding assigned (both purposes, declared as new
  hardening in split row 11a/11b since those paths are ungated today),
  identify and pull/batch sync classified as partner exchange (both
  purposes), and S2S sync authorized by stored provider/version-tagged
  provenance re-validated against current policy.
- Withdrawal drops the false atomicity claim: revocation families are
  idempotent independent writes, readers fail closed on any present
  member, and fault-injection covers the Nth-write failure.
- Equivalence is provider-declared via fixtures (hmac: hex prefix
  case-insensitive, suffix case-preserved) instead of a universal case
  rule; the legacy HMAC grammar is formally reserved as the hmac
  namespace so verbatim row keys and provider namespacing coexist;
  global cookie-safe identifier bounds added; non-cluster providers get
  defined dedupe and redaction.
- Legacy readers get full semantics: first-match parse with
  namespace-overlap validation, recognizing provider's permissions
  govern, provider/version-tagged provenance, transactional linking
  rewrite with dual revocation, and provider = "none" as an explicit
  stateless state that keeps revoke-only legacy readers.
- Graph store required at startup when any provider can mint or read; a
  minted identity is not active until its row commits; a runtime
  failure matrix covers provider, graph, cluster, rewrite, and
  geo/device runtime failures.
- Rollout gains a dual-read release (N+1 accepts both config shapes,
  N+2 rejects loudly) since no config is accepted by both current main
  and a rejecting binary; the preserving recipe becomes one committed
  valid TOML fixture (the prose delta reopened [permissions.rules],
  which is invalid TOML); metrics extended with retirement thresholds.
- Resolve endpoint defines same-identity no-op / different-identity
  rejection and an atomic single-key reservation tying replay
  consumption to graph persistence.
- Hook ordering becomes core -> integrations -> inviolable cache/privacy
  invariant pass (an appended cookie plus replaced public Cache-Control
  can no longer produce a shared-cacheable cookie response); generic
  ops reject Set-Cookie; per-integration operation limits and
  erroring-mutator semantics defined; every eligibility row tested.
- Stale device-circularity wording removed from the permission spec;
  region-form default_country (US/CA) restored; the source-agnostic
  permission-source requirement of #777/#779 explicitly deferred in the
  divergence table; fail-closed and most-protective labels qualified
  with their stated exceptions.
…fecycle repairs

Blocking findings from the third review of PR #986:

- Grant evidence is now regime- and permission-scoped: gdpr rules accept
  only TCF consent for the specific purpose, us-privacy accepts TCF or
  explicit GPP/USP non-opt-out, none accepts any grant class - closing
  the hole where sale_opt_out=false in France would have authorized
  identity and partner egress with no TCF. Opt-outs and refusals stay
  regime-agnostic.
- The graph schema change gets an expand-contract rollout: reader/
  preserver release (unknown fields preserved through read-modify-write),
  fleet-convergence gate, then writer activation, with schema versions,
  lazy backfill, and mixed-version tests.
- S2S batch-sync authority is a full recompute of both permissions from
  stored per-permission, time-bounded evidence (grant basis, timestamp,
  jurisdiction, policy revision, provider/version) - failing closed on
  denied, tightened baselines without acceptable stored evidence,
  expired evidence, or regime-rejected grant sources. Legacy pre-epic
  rows are hmac-v0 with no grant evidence and fail closed until lazily
  backfilled.
- Pull sync split from batch sync: pull is browser-request-scoped and
  keeps using the live P1/P4 decision plus revocation state; only batch
  is provenance-authorized, and its gate is declared hardening (new
  matrix row 11c; row 11a corrected).
- Withdrawal centers on a family revocation record: a stable family ID
  in every member row, one record written first that is simultaneously
  the durable intent, the sibling-discovery mechanism, and the
  fail-closed marker; member tombstones become cleanup; degraded-graph
  mode fails S2S closed while writes fail; the healthy-graph residual is
  declared.
- Legacy rewrite is confirmed by presentation: both linked rows stay
  live until a later request presents the new cookie (the server cannot
  observe Set-Cookie acceptance); linked rows share the revocation
  family.
- The normalization matrix now preserves actual current semantics:
  whole-record selection by combined P1/P4 eligibility for restrictive/
  permissive, LastUpdated with freshness threshold and restrictive
  tie-break for newest, proxy mode skips decoding, one-valid/one-expired
  row added, and GPP section fields enumerated normatively.
- The auction matrix regains the raw-signal arm: a decodable TCF record
  applies the gdpr dispatch rule in every regime, so a P1 refusal on US
  or non-regulated traffic still blocks dispatch.
- Provider namespaces become declarative descriptors core can prove
  pairwise disjoint at startup (opaque parse cannot be); version
  rotation gets a schema (versions entries, mint_version, newest-first
  parse, retirement rules).
- Client resolve reservations get pending/committed/failed states,
  lease takeover, retention through token expiry, deterministic graph
  idempotency, adapter CAS capability - and duplicates never receive
  Set-Cookie unless the reservation is session-bound, closing the
  idempotent-replay fixation hole.
- The hook invariant pass preserves any pre-hook private/no-store
  classification (cookieless personalized HTML cannot be made publicly
  cacheable) and strips CDN directives; panics are declared forbidden
  and fatal on wasm32-wasip1 (panic=abort - recovery was
  unimplementable); a cumulative final-response header budget with
  deterministic rejection order added.
- Rollout ordering corrected to strictly reader-first with a
  convergence gate (the previous either-order claim was false against
  binaries that reject the new shape); mixed old/new config shapes
  rejected; rollback sequencing defined; one preserving fixture per
  adapter since device/geo selections are capability-gated.
- Device-provider authorization reconciled with persisted use: new rows
  stop carrying fingerprint-derived buyer-facing fields (declared
  change); a field-level graph contract table is a required
  implementation deliverable.
- Non-blockers folded in: adapter-capability matrix, assigned-
  subdivision region validation, retirement quiet period no shorter
  than max cookie/row lifetime plus skew, expanded telemetry, and the
  stale recognize/hashed/eligibility terms corrected.
…cs, and distributed contracts

P0: legacy identities can now enter the family-revocation protocol.
Rows lacking a family ID derive one deterministically from (record kind,
provider namespace, canonical graph key), so a first-post-upgrade
withdrawal is discoverable by every future reader even if the writer
crashes before touching the v1 row; random IDs are called out as
recreating the orphan the design exists to eliminate. Withdrawal never
depends on backfill; tests pin the first-request-is-withdrawal and
crash-between-writes cases.

P1 groups:
- US signal fields get a normative field x value x permission x
  destructive table (4.5): sale maps to P1+P4 and is destructive,
  sharing and targeted-advertising map to P4 only and never destroy
  identity, USP carries no targeted field, absent/N-A grants nothing,
  state sections override national, opt-out beats grant across sections.
  The regime evidence table now defers to this mapping.
- Normalization is a six-state machine (valid-grant, valid-refusal,
  opt-out, malformed-present, expired, absent) wired into precedence and
  the decision matrix (malformed blocks the granted baseline); proxy
  mode keeps a syntax pass so malformed is distinguishable, blocks
  record-derived grants, and is declared as a change from today's
  fail-open skip.
- Conflict resolution is deterministic: whole-record selection over the
  (P1, P4) tuple ordered lexicographically P1-first (split-purpose
  records decided), newest uses LastUpdated with the freshness threshold
  and falls back to restrictive; expiry drops sources before conflict
  resolution - a declared change from today's conflict-first ordering.
- The auction raw-signal arm triggers on raw TC-string presence or a
  GPP section-2 hint before decoding, so malformed raw TCF still blocks
  dispatch outside GDPR regions.
- Stored provenance ages: authoritative timestamp + valid_until per
  evidence class, re-presentation does not reset age, and every live
  resolution atomically replaces the full per-permission snapshot so a
  later refusal clears older positive authority; rewrite provenance is
  the fresh live resolution, partner mappings keep original expiries.
- A per-record-class consistency matrix: replay reservations need
  linearizable CAS with fencing (Durable-Object-class on Cloudflare,
  not Workers KV), family revocation records need strong reads plus a
  declared bounded visibility lag with read-failure failing closed,
  identity rows may be eventual; retention outlives every dependent
  lifetime (today's 24h tombstone TTL explicitly does not carry over).
- The US policy enumerates US/<state> rules for configured privacy
  states with country-level US non-regulated, preserving Wyoming-class
  traffic; regionless-geo degradation is declared; the states-list
  consistency test is region-shaped.
- The graph field contract is normative in-spec (providers 6.3): every
  v1 and new field with purpose, source, gating permission, TTL,
  rewrite, and revocation treatment - including discontinuing
  fingerprint-derived buyer-facing fields; releases unified as N/N+1/N+2
  with semantic (not byte) unknown-field preservation, a hard rollback
  floor at N+1 after writer activation, and stated mixed-version
  expectations.
- Identity boundaries are structural: core constructs physical graph
  keys with record-kind/provider/version prefixes (legacy hmac verbatim
  excepted) and an AuthorizedIdentity newtype - constructible only after
  parse, permission, graph, and family checks - is the only type
  outbound serializers accept.
- Legacy rewrite aliases to one canonical row via fenced CAS (no
  dual-write divergence), with confirmation by presentation and a
  finite retirement deadline.
- Client-cycle: session binding is required for production schemes
  (one-time consumption demoted to defense-in-depth; at-most-once only
  as an explicitly recorded posture with orphan cleanup); reservations
  carry owner hash and monotonic lease epochs with fenced transitions;
  owner-hash retry re-emits the cookie so lost responses do not orphan
  rows; resolve checks the family revocation record and loses races to
  revocation.
- The hook snapshots all pre-hook cache restrictions (origin-supplied
  included) and allows only equal-or-stronger privacy; Content-Encoding
  and Content-Range join the reserved surface; the test set covers
  origin-private and core-private cookieless HTML, cache hits, Vary,
  every CDN directive, and body encoding.

P2/P3: mint 'cookie write' clarified as scheduled-on-final-response with
egress eligibility at graph commit; degraded-health is a per-instance
in-memory state machine with hysteresis; HMAC versions resolve from row
provenance (untagged = hmac-v0), not parse; client limits are exact
(65,536-byte body, content-type allowlist, 256-byte identifier, 128-byte
reservation key, per-code statuses); migration matrix gains rows 3d-3g;
fixtures include the graph-store config; every rollout metric ships with
threshold, window, and action; batch-sync's coverage dip is
operationalized with the provenance-coverage metric; FR default relabeled
a protective opt-in fallback; device-selection authorization qualified to
the opt-in fingerprint provider; the illustrative policy example is
labeled as such.
@aram356 aram356 self-assigned this Jul 31, 2026
aram356 added 18 commits July 31, 2026 14:20
…ncy eligibility, and rollout closure

P1 fixes:

- Opt-out signals split into destructive (GPC, sale, USP - withdraw) and
  non-destructive (sharing, targeted-advertising - revoke P4 only, never
  tombstone) subclasses assigned by the 4.5 mapping, resolving the
  4.2-vs-4.5 contradiction.
- Proxy mode performs minimal opt-out extraction (the 4.5-mapped fields
  and USP only) so globally authoritative opt-outs are never suppressed;
  still no record-derived grants; declared as a change from today's
  opt-out-blind skip.
- GPP applicability is an ordered algorithm with a pinned jurisdiction ->
  section-ID map (usnat 7, usca 8, usva 9, usco 10, usut 11, usct 12):
  applicability from resolved jurisdiction, state-over-national per
  field, restrictive aggregation; foreign and non-applicable sections
  contribute nothing; N/A preserved as not-opted-out (declared,
  correcting the earlier contributes-nothing rule).
- TCF conflict selection reverts to today's algorithm - P1-and-P4
  conjunction comparison with standalone winning equal conjunctions
  (including split-purpose) - replacing the invented lexicographic tuple
  and keeping the Preserved label honest.
- S2S freshness is a per-evidence-class contract: TCF ages by
  LastUpdated, GPP/USP by first-seen with an equality digest
  (re-presentation keeps original first-seen), baseline grants re-derive
  from the current policy revision; clock-skew clamping.
- Degraded-mode protection is declared local-only with the cross-instance
  residual quantified (bounded by user return latency, metered), instead
  of implying fleet-wide fail-closed.
- Family revocation records require a strongly consistent primitive;
  Workers KV is explicitly ineligible ('60 seconds or more' is not a
  bound); alias/rewrite records join reservations in the linearizable
  CAS class and rewrite_legacy is rejected without it.
- The trait now really returns graph_key_suffix (a round-4 batch loss),
  core owns the 6.3 physical key grammar (id/, alias/, fam/, rwx/,
  resv/ prefixes + reserved legacy grammar) with wire schemas and TTLs
  per record class.
- HMAC version attribution really resolves from immutable row provenance
  (also a round-4 batch loss); parse identifies namespace only.
- AuthorizedIdentity is scope-parameterized (GraphOps vs PartnerEgress)
  so a P1-only identity cannot reach an ORTB serializer.
- The provider contract gains acquisition modes (ServerMint /
  ClientResolve carrying resolve_from_client and the JS module id); the
  resolve endpoint enforces the provider's full required_permissions.
- Revocation-wins at the resolve endpoint holds because the family check
  runs through the linearizable class client-cycle already requires.
- Rewrite is a persistent fenced transaction: pinned target on retry,
  reconciliation of updates that won the old-row CAS, orphan GC by
  absent transaction, and fenced alias retargeting keeping chains
  single-hop.
- Release protocol: rollback is binaries-first (N+2 -> N+1 keeping the
  new config); N+1 rejects provider/version selections it cannot encode;
  a pre-N+1 graph-store readiness step plus matrix row 12 covers
  graphless HMAC deployments (breaking, declared).
- The abstract capability list becomes a concrete adapter matrix
  (Fastly/Axum/Cloudflare/Spin) with honest cells - including that
  Cloudflare supports platform geo country-only (the migration text
  claiming it rejects platform geo was wrong) and that no CAS-class
  primitive is currently wired anywhere but the dev adapter.
- Integration cookies enter the permission model: registration-declared
  names with purpose and retention, persistent cookies gated on
  store-on-device, session cookies as the narrow exemption.
- Cache monotonicity is a defined lattice (no-store > no-cache > private
  > public, shrink-only ages, snapshot-gated stale directives, protected
  Vary union) in the contract, not the tests.

P2/P3: persisted-KV consent now flows through the full pipeline
(declared change); provenance transition and mid-replacement fault tests;
the recipe renamed minimal-divergence with its unavoidable divergences
enumerated; batch-sync coverage is a gated stage with thresholds and a
pause action; policy-revision activation defined (stamped revisions,
bounded mixing, no tombstone resurrection); representation surface
extended (Content-Type, ETag, Last-Modified, Accept-Ranges, digests);
append restricted to list-valued headers; exact budgets and snapshot
read semantics; reservation namespacing and per-state ownership
conflicts; media-type matching ignores parameters; the pass-through test
wording fixed; and a product-decision sign-off list (9 items) added to
the migration spec for explicit maintainer ratification.
… and storage-protocol coherence

P1 fixes:

- GPP applicability now gates grants only: mapped opt-out fields (either
  subclass) aggregate globally from any section on any request,
  resolving the section-4-vs-4.5 contradiction where a French visitor's
  usnat SaleOptOut was simultaneously mandatory and ignored.
- The section map covers everything current code recognizes (7-23,
  through usmn), not 7-12; Texas section 16 named as what the truncated
  map would have silently lost; states without a state section (MD, IN,
  KY, RI) use the national section.
- Destructive TCF-refusal withdrawal requires the refusal to be carried
  by the live request; persisted-KV records participate in acquisition
  only - closing the path where a years-old stored refusal tombstones on
  the first signal-less request after a policy tightens to denied, and
  repairing the mixed-revision safety claim.
- Negative authority gets its own record: a permission-exempt,
  strongly consistent suppression record (sup/<family-id>) with
  per-permission entries that every S2S recompute and partner-egress
  check consults - resolving the circularity where clearing P1
  provenance required the P1 the refusal just unset, and the
  eventual-row edge where a stale replica restored P4 after a
  targeted-advertising opt-out.
- The consistency requirement has one normative home (the providers
  matrix, strong read-after-write); the permission spec's bounded-lag
  leftover is gone.
- The never-returning-visitor residual is stated as unbounded and
  becomes sign-off item 11, replacing the false bounded-by-return-
  latency claim.
- Aliases live at the source identity key with a kind discriminator in
  the value envelope - a separate alias/ address could neither be found
  by old-cookie lookups nor installed by a single-key CAS.
- Identity keys drop the version segment (id/<provider>/<suffix>);
  version lives in the row envelope, killing the read-the-row-to-learn-
  how-to-read-the-row circularity. All HMAC versions stay on the
  verbatim key scheme, keeping the 64-hex cluster prefix a literal key
  prefix for every HMAC row; rotation-induced cluster splits are
  declared as inherent to rotation.
- Rewrite requires the row store itself to provide per-key CAS with
  read-your-writes (alias installs happen there); adapters with purely
  eventual row stores cannot host rewrite. Chains use bounded traversal
  (4 hops, cycle detection, fail closed) with opportunistic path
  compression instead of an undefined inbound-alias index.
- Revocation-wins at the resolve endpoint is an explicit linearization
  point: family records carry an epoch and the cookie-emitting commit
  is a CAS conditioned on it - linearizable reads alone lose the race.
- resolve_from_client takes a core-built ClientResolveContext (canonical
  audience, verified session owner, clock, bounded payload) and returns
  a verified identity with reservation id and expiry.
- The mutator snapshot is redacted: all Set-Cookie values and reserved
  identity/consent/privacy header values withheld, so the hook cannot
  leak the raw EC around AuthorizedIdentity<PartnerEgress>.
- The cache merge is over independent sticky directives per RFC 9111
  (no-cache and private are orthogonal; the ordered-lattice version
  could make a personalized response shared-storable), and the complete
  origin Vary set is preserved, not only core-required members.
- Hook cookie coupling acknowledged: the persistent-cookie gate is a
  listed enforcement point in the permission spec inventory; cookie
  operations activate only after the permission model lands; a typed
  cookie builder enforces declared lifetime/scope/security attributes;
  deletion cookies work when P1 is denied.
- N+1 is a full semantic reader and enforcer for every N+2 record kind
  (aliases, family revocation, suppression, provenance fail-closed),
  with rollback tests on N+1 against N+2 data; binaries-first rollback
  gains its precondition (converge to an N+1-compatible config first
  after N+2-only adoption, retaining new-provider secrets as legacy
  readers rather than reverting config).
- Adapters without revocation-eligible storage migrate with explicitly
  stateless fixtures (sign-off item 12) instead of invalid HMAC
  fixtures.

P2: explicit NotApplicable rows (grant-class, preserved) separated from
absent; per-permission first-seen digests over only applicable
aggregated fields; consent.us_states.privacy_states path corrected;
provider-switch rollback keeps the new provider as a legacy reader;
rewrite_legacy with a client-resolve writer is a startup error; client
parity redefined as identical startup rejection on ungated adapters; the
capability matrix distinguishes platform availability from wiring (Spin:
available, not wired); row 3e's effects classified in both directions;
and the sign-off list is a ratification table (owner/status per row,
implementation blocked while any row is open) extended with items 10-14.
…gs, add a review ledger

The recurring theme - browser-side acquisition, integration-owned
identifiers, rewrite, and pre-existing state generating blockers while
the core holds - is answered structurally this round:

Descope (sign-off item 15, ratify or veto):
- The client-cycle spec is demoted to a deferred informative draft: no
  production adapter has its CAS-class primitive, it has no consumer,
  and its findings no longer block core ratification. Within it: the
  ownerless first-presenter mode is removed outright (risk acceptance
  does not make a security invariant true, and its orphan cleanup was
  unimplementable - the server cannot observe Set-Cookie acceptance);
  the page leg is permission-gated before the module executes; the
  cross-key commit atomicity gap is recorded as open question 0.
- rewrite_legacy is cut from the epic into a recorded deferral carrying
  its open problems (retention lineage, eventual-store visibility,
  chain stranding, cluster inflation) as the entry bar for a future
  spec; provider switching is served by legacy readers alone; the key
  is rejected as unknown.
- The hook ships headers-only: the write-side cookie gate never modeled
  reading, using, forwarding, or withdrawing an integration cookie
  (or its P4 nature), so cookie operations defer to a follow-up spec
  with that full model as entry bar; sign-off items 9/10 updated.

Core fixes (new P1/P2):
- Recognized rowless legacy cookies (graphless deployments) get a
  permission-gated, race-safe adoption transaction; no egress before
  adoption; withdrawal needs no adoption (derived family ID); matrix
  row 13.
- Unreferenced [ec.providers.*] blocks are startup errors - a dropped
  legacy_providers entry must not silently strand identities.
- Physical key delimiters are backend-safe and validated per adapter
  (Fastly forbids / in prefix queries); cluster eligibility requires a
  queryable physical prefix, checked at startup.
- Cluster size means live identity rows: kind/liveness filtering,
  short-TTL tombstone inflation declared conservative.
- Validation split into structural (push + startup) and deployment
  (startup; optional push pre-check via a machine-readable capability
  profile) - 'same validation at push' was unimplementable.
- The rollback floor is N+2 writer activation itself, recorded as a
  durable schema-floor marker - not an unobservable first-row fact.
- Integration IDs are startup-unique.

Previously-open items closed:
- GPP map completed against the official registry: section 6 (US
  Privacy as GPP section) and 24-27 (MD/IN/KY/RI - the earlier claim
  they had no sections was wrong).
- State-over-national applies to grants only; a national opt-out can
  never be erased by a state field.
- Suppression records completed: full negative-state coverage,
  monotonic per-permission ordering, re-consent clearing, write-failure
  semantics.
- The raw-TCF dispatch arm triggers on TCF-sourced effective records
  including the persisted-KV fallback.
- N+1 writes the safety-critical record kinds (family, suppression) and
  accepts N+2-only providers as legacy readers, making the rollout
  boundary safe in both directions.
- Request-side integration views are identity-redacted; the legacy
  RequestFilterEffects.response_headers channel is folded into the hook.
- must-understand and friends join the sticky directive set; the Axum
  matrix cell is honest (in-process, non-durable, dev-only).

Process: docs/superpowers/specs/pr986-review-ledger.md records the
disposition of every finding from all seven review rounds (fixed /
reapplied-after-batch-loss / partial-refixed / superseded / deferred /
open), so coverage is auditable per finding rather than claimed in
summaries.
…cs, and descope propagation

P1 fixes:

- Suppression is realizable: writes require linearizable per-key CAS
  with the record's own version counter ordering transitions (strong
  reads alone let an older clear overwrite a newer suppress); coverage
  is every positive-to-unset delta regardless of cause (the
  refusal/opt-out-only list left malformed-present and applicable
  absence leaking stale authority to batch sync); timestamp-less
  GPP/USP sources get sticky opt-out - only an authoritatively
  timestamped newer grant clears, since opt-out/consent/opt-out(same
  value) is indistinguishable from replay (sign-off 16); and a failed
  suppression write is an unbounded residual for a never-returning
  visitor, sharing sign-off 11 - not 'transient'.
- N/A has one meaning everywhere: explicit Not Applicable is
  grant-class (preserving pinned USP tests and GPP NotApplicable
  handling), absent grants nothing; the P4-authorizing consequence is
  sign-off 17.
- Adoption authenticates: ServerMint providers gain
  verify(id, evidence); rowless cookies failing verification are
  expired, not adopted (including the declared roaming false-negative);
  adoption gates on the provider's complete required_permissions, needs
  an atomic create-if-absent capability (Workers KV ineligible),
  distinguishes read errors from not-found, and bounds adopted-row TTL
  by a migration cutoff instead of granting a fresh year (sign-off 21).
- N+1 has a valid write behavior: it mints v1 rows with today's
  semantics, and old-shape config runs the pre-epic consent gate
  unchanged (dual-read = dual-behavior), so neither the
  active-after-commit contract nor the compiled protective fallback
  fires mid-convergence; the new contracts activate with N+2/new-shape
  config (sign-off 20).
- Providers ship compiled-in dormant one release before selectability -
  there is no dynamic provider ABI, so 'N+2-only provider readable by
  N+1' was impossible as written; new providers get their own
  reader-first rollout.
- The hook's cookie deferral is contradiction-free: the operation list
  is headers-only, the reserved-surface and generic-op remnants are
  swept, and the cache test uses a core-owned queued cookie.
- The RequestFilterEffects.response_headers channel is NOT folded in -
  that would break DataDome's challenge/deny flows (headers + cookies
  on 200/301/302/401/403/429, response classes the hook never runs on).
  It stays a distinct core-owned security channel with core-mediated
  security cookies, adopting the shared validation and cache-invariant
  layers.
- Age, Date, and Expires are reserved: replacing Age:59 with Age:0 or
  extending Expires re-extends freshness in exactly the way the
  monotonic merge forbids.
- Client-cycle types (Acquisition/ClientResolve/reservations) are out
  of the normative trait surface per the spec's own minimalism rule;
  the epic's only acquisition mode is server mint.
- Request-side redaction is a specified boundary: typed
  RedactedRequestView with an enumerated strip set, same-PR migration
  of the raw filter/proxy inputs, and denied/withdrawn tests.

P2/P3: rewrite residue swept (tests, runtime row, metrics, retirement
gate; alias schema marked reserved); physical keys become one portable
delimiter-free fixed-width grammar (class tag + 4-char registry
provider code - Fastly rejects both / and : in prefix queries, and
per-adapter delimiters would fork physical keys across adapters); the
Axum matrix cell reflects UnavailableKvStore; stored cluster sizes
cannot outlive their inputs; GPP applicability leftovers reconciled
(MD/IN/KY/RI sentence removed, section-6 grants defined, regime-none
row aligned); mixed-revision divergence explicitly accepted (sign-off
19); the schema floor lives in write-once/CAS deployment metadata that
config rollback cannot erase; sign-off rows 16-21 added and rows 3/11
amended; duplicate integration IDs rejected at startup; GPP versions
enumerated with unknown-version-as-malformed; custom geo region
vocabularies require a canonical ISO mapping; and the review ledger's
overstated R7 dispositions are corrected (suppression, delimiter,
redaction, Axum, header-channel) with a full R8 section.
…g, complete suppression contract, closed security channel

Both review passes against ff1e113, dispositioned together.

Trait and rowless identity:
- generate is restored to the normative trait (lost in an R8 editing
  accident, making the required mint sequence unimplementable); verify
  returns VerifiedIdentity carrying the matched configuration version.
- Rowless legacy cookies are expired and re-minted, never adopted:
  prefix-only HMAC verification cannot authenticate the random suffix,
  so adoption would let H.aaaaab, H.aaaaac, ... each mint a durable
  row/family. The rowless family ID derives from the authenticated
  64-hex prefix only, collapsing all suffix variants into one
  withdrawable family; matrix row 13 and sign-off 21 updated; the
  migration cutoff disappears with adoption.

Suppression, completed:
- Creation is cause-aware and read-free for signal causes (refusal,
  opt-out, malformed) - conditioning on observing positive provenance
  through an eventual row loses the stale-replica race; absence uses a
  narrow permission-exempt suppression-decision read (the P1-gated read
  circularity); policy-only tightening writes nothing.
- CAS fences writes; authoritative evidence recency decides semantics,
  with a per-cause transition table: sticky clearing only for opt-out
  causes, malformed/absence clear on any newer valid grant (sign-off
  24), delayed older grants never clear, policy never clears.
- Anti-replay: beyond-skew future timestamps are malformed; a digest's
  first normalized timestamp is pinned and never advanced by
  re-presentation; digests cover the canonical per-permission semantic
  result, so equivalent encodings cannot renew authority.
- Suppression is inside both AuthorizedIdentity constructors, reads
  fail closed, retention outlives masked authority, and clearing is
  fenced on visibility of the matching provenance generation.

Storage and capabilities:
- New capability rows with per-adapter values: linearizable per-key CAS
  (suppression), generation-CAS row mutation (rows are heavily mutable,
  not accretive - Fastly generation markers eligible, Workers KV
  last-write-wins ineligible), atomic create-if-absent, write-once
  deployment metadata; revocation reads require globally observable
  strong consistency, not writer-session read-your-writes; per-class
  durability and maximum-retention proofs at startup.
- Rows carry an absolute expires_at pinned at mint (updates write
  remaining lifetime - cluster refresh can no longer immortalize an
  identity) and an immutable mint tag split from the replaceable
  evidence snapshot; network evidence split from refreshable derived
  cluster state; cluster_trust_threshold validated against listing
  caps.
- The key grammar's class tags are all non-hex (family tag f -> r) so
  legacy-grammar disjointness is provable, and provider codes come from
  a checked-in append-only never-reused registry; the identifier bound
  is numeric (256 bytes) in the normative contract.

Migration:
- Once new-shape config is active, N+1 batch sync fails closed on
  provenance-less rows - the fail-closed rule cannot activate later
  than the model it protects.
- The schema floor has a protocol: dedicated deployment-metadata
  primitive, create-or-CAS with read-back before enabling writes,
  startup enforcement, fail-closed on unreadable.
- Provider rollback is config-first (writer back, retain as legacy
  reader, then binaries), distinct from binaries-first schema rollback.
- Irreversible artifacts enumerated (revocation, floor, sticky
  suppression) with recovery/administrative procedures; fixtures branch
  on capability eligibility; sign-off rows 22-24 added.

Hook and security channel:
- CDN cache fields reserved outright; stale-* durations shrink-only;
  the last Set-Cookie contradiction removed; append/replace legality
  from a core-owned field registry with unknown-fields-reject-append;
  operations are attributed batches, validated and budgeted atomically
  (a security 302 can never keep its cookie but lose Location).
- Section 4a closes the security channel: typed owned-name cookie
  operation (ts-* rejected, sign-off 23 for the identifier lifecycle),
  direction-scoped request-header allowlists applied to a scoped
  upstream overlay (no credential/identity/routing injection),
  decision-scoped representation (a challenge owns its body; Continue
  cannot touch publisher bytes), one global order with the invariant
  pass unconditionally last.
- Eligibility matrix gains HEAD (header parity with GET mandatory) and
  explicit 1xx/204/205/206 rows.

Deferred drafts: the client-cycle page leg gains a pre-vendor-contact
live permission check with BFCache abort (TOCTOU); its stale references
to the removed acquisition API and old key grammar are marked for
renormalization.

Ledger: the five overstated R8 dispositions are reopened and corrected,
and a Rounds 9-10 section records every finding above.
…handling, and the closed DataDome contract

P1 fixes:

- Suppression cannot deadlock its own recovery: AuthorityRefresh is a
  permission-exempt write path strictly scoped to committing provenance
  from the current live resolution while suppression stays effective;
  the clear then references that provenance's application-level
  monotonic revision (backend generation markers detect change and
  carry no order - per Fastly's own contract - so revisions are
  app-level counters).
- Rowless classification is safe: it activates only under a
  deployment-metadata graphless-migration flag with a strongest-read
  existence check; otherwise (and on any read error) the state is
  indeterminate - no identity use, no mint, no cookie expiry.
  Rowless withdrawal writes nothing: there is no server-side state to
  revoke, and the prefix-derived family record is removed - it let
  unauthenticated suffix variants mint records and, because the HMAC
  prefix is per-IP, would have revoked every identity behind one IP.
  Family derivation is now one rule everywhere (full graph key,
  row-backed identities only).
- The absence decision reads a strong record: the per-family record is
  now the authority-state record, carrying a per-permission
  positive-authority summary CAS-updated by every provenance write -
  never the eventual identity row, whose stale not-found loses the
  fresh-grant race.
- The concrete adapter matrix gains cells for every mandatory
  capability (suppression CAS, row-mutation CAS, create-if-absent,
  deployment metadata, durability/retention) across all four adapters;
  the accretive claim is deleted (eventual visibility only after a
  generation-CAS mutation); Axum storage-dependent cells read
  Unavailable until a store exists.
- The suppression wire schema is complete (state, cause, source class,
  evidence/observation timestamp, referenced provenance revision,
  positive summary, CAS version, schema version).
- generate returns GeneratedIdentity { id, mint_version } - core
  cannot otherwise record the immutable mint tag; provider/version is
  removed from the mutable snapshot in both specs.
- The DataDome contradictions are resolved: X-DataDome-ClientID is
  positively enumerated (with the documented X-DataDome-* set) and
  applies to an owner-scoped upstream overlay, never the shared view,
  egress under sign-off 23; the cookie carve-out has a concrete
  lifecycle (name datadome, apex scope, Secure/SameSite=Lax, 13-month
  ceiling, 4 KiB, owner-only read, deletion always, withdrawal
  semantics = the open half of item 23, which is pending ratification,
  not ratified); ordering is one global order (core -> ordinary
  mutators -> security effects -> invariant pass) with the older
  DataDome doc marked superseded; challenge batches validate and
  budget before Respond commits, so rejection can still fail open to
  Continue.
- Headers-only v1 is permission-neutral by construction: the field
  registry admits inert fields only - Link preload, Reporting-
  Endpoints/NEL, CSP reporting, and Refresh cause vendor contact and
  are rejected until permission-declared mutation exists; unknown
  fields are rejected entirely.

P2/P3: exempt-read and AuthorityRefresh rows added to the enforcement
inventory with field enumeration; TCF digests include LastUpdated while
GPP/USP digest semantics only (genuine CMP renewal refreshes, replays
do not); malformed/absence causes get observation timestamps with
cross-source comparison rules; the 4.1 matrix gains the suppression
condition (one malformed request denies later no-signal requests under
granted - sign-off 24 expanded); graph-read errors are indeterminate,
not absent; the rewrite/backfill retirement alternative is removed
(full-lifetime quiet period only); GPP versions pin to a vendored
registry snapshot file; 304 for processed representations gets a
304-safe metadata pass; CDN cache fields are enumerated by name
(Surrogate-Control, CDN-Cache-Control, Cloudflare-CDN-Cache-Control,
Edge-Control); deferred reservation/CAS material is bracketed
informative; the verbatim-key comment covers every hmac version;
Content-Language replaced by true singletons; docs/superpowers/specs/
decisions/ created as the sign-off table's decision-record home.

Ledger: R9-3, R10-2, R10-4, and GPP-pinning dispositions corrected to
partial-then-refixed; full Round 11 section added.
…ess proof, and bounded DataDome surfaces

P1 fixes:

- The two-record authority protocol has an explicit commit point: the
  identity row commits at revision r (generation-CAS), then the
  authority-state record CAS-updates to r, and r is usable - by S2S,
  by the absence decision, for egress eligibility - only when the
  strong record reports it. A crash between the writes is a recoverable
  intermediate (AuthorityRefresh re-runs step 2), never divergence.
  Mint eligibility begins at the authority-state commit, not the row
  commit.
- Authority-state requires globally observable strong reads AND
  linearizable CAS - a stale successful read would authorize egress
  after a committed suppression, and read-failure-fails-closed does not
  cover stale successes; matrix and Fastly cells updated (read
  semantics to verify).
- Rowless classification is proven from the strong class: every
  post-upgrade identity has an authority-state record under its
  derivable family ID, so rowless = graphless-migration flag AND no
  such record on a strong read - no eventual storage participates. The
  flag has a defined wire key, setter, capability requirement, rollback
  survival, and explicit operator clearing.
- Rowless withdrawal is one contract everywhere: an exact-cookie family
  record (full-graph-key derivation - no per-IP blast) written by
  prefix-verified cookies only (attackers can spend withdrawal only on
  their own prefix), then cookie expiry - aligned with the
  family-record-first rule and migration row 13; the prefix mechanism
  is gone from every document, and cookie-only best-effort withdrawal
  (lost response = live cookie) is rejected.
- N+1 neither creates nor clears authority-state records: clearing
  requires the AuthorityRefresh fence over revision-bearing rows a v1
  writer cannot produce. N+1 reads fully and fails closed; suppression
  persists through rollback and recovery waits for roll-forward - a
  declared protective limitation. N+1 still writes family revocations.
- The graph-row table gains the provenance-revision field (init 1,
  monotonic u64, overflow is an error, CAS'd with row generation) and
  loses the provider/version leftover from mutable provenance.
- The positive-authority summary carries kind (user evidence vs policy
  baseline), grant basis/source class, policy revision, and
  valid_until - the absence decision distinguishes vanished user
  evidence from policy-only change without touching the eventual row.
- The DataDome request-header allowlist is a checked-in file
  (datadome-header-allowlist.md) pinned to X-DataDome-ClientID alone;
  the cookie strip inventory is exhaustive (origin forwarding,
  proxy/click/Testlight upstreams, auction serialization, logs - each
  a tested row), not just integration views.
- The 304 pass re-emits the persisted final post-hook header set stored
  with the cached representation; absent metadata means cache miss -
  the where-does-the-200-come-from gap is closed.

P2/P3: gpp-registry-snapshot.md vendored (sections 6-27, versions,
ratification re-verification note); the v1 field registry is enumerated
in-spec; the datadome cookie pins PSL-computed registrable Domain and
Max-Age <= 34,214,400 s; the clock-skew window is a normative 300 s
constant; sign-off 23 is an open question enumerating observers, not
'ratified'; the permission spec repeats the globally-observable
revocation wording verbatim instead of paraphrasing; rewrite/reservation
wire schemas are bracketed informative and rewrite links leave the
migration expansion; the dangling duplicate sentence, the reserved-
cookie-name phrasing, and the negative-authority-only key-table
description are gone; and the ledger adds Round 12 with a
mechanical-anchor rule so closure claims are greppable rather than
trusted.
…ess prerequisites, honest failure semantics

P1 fixes:

- Suppression entries carry their evidence class's valid_until and go
  inert at expiry (lazily GC'd) - resolving the contradiction where an
  expired TCF refusal under a granted baseline would deny forever while
  the normalization table promised the baseline grant; normalization
  wins.
- Rowless classification gains real prerequisites: the flag may be set
  only after full N+2 convergence (an N+1 fleet still minting v1 rows
  never classifies rowless) and an idempotent stub-backfill has stamped
  an authority-state existence stub on every existing row - only then
  does 'no record' actually mean graphless-era. A per-deployment flag
  alone cannot prove a per-cookie fact.
- The mint-path recovery claim is retracted: a failed authority-state
  commit after the row commit leaves an orphan no later request can
  find (no cookie was emitted); it authorizes nothing, expires by TTL,
  is counted, and has its own runtime-failure-matrix row. The
  eligibility-at-graph-commit leftover is swept.
- The authority-state wire schema now carries every field the
  permission protocol consumes: negative entries with cause/source/
  timestamps/valid_until/referenced revision; the positive summary with
  kind, grant basis, policy identity, valid_until, revision, evidence
  timestamp, and the semantic digest with pinned first-seen; plus the
  backfill stub marker.
- Negative-record creation is admission-controlled (existing family on
  a strong read, or a verified identifier - fabrications write
  nothing), and rowless withdrawal collapses to one capped per-prefix
  record (8 suffix hashes; saturation escalates to prefix-wide rowless
  revocation as the declared abuse response, harming only the abuser's
  own same-IP graphless cohort) - closing the storage-amplification
  surface that per-variant exact-cookie records opened.
- Embedded GPP GPC is mapped: Gpc=true in any section is the same
  destructive global opt-out as the header (OR-aggregated);
  GpcSegmentIncluded=false/absent contributes nothing; malformed GPC
  segments render the section malformed-present. Sign-off 26.
- Batch S2S jurisdiction ages: stored jurisdiction older than the
  consent-TTL horizon fails closed pending a live refresh; the horizon
  and its two-sided trade-off are sign-off 25.
- Observability sinks join the egress inventory: raw EC values never
  reach logs/traces/metrics/errors, logging boundaries take hash-only
  types, the existing PR #838 logging site is cited, and a log-schema
  denylist test enforces the row.
- 304 persisted metadata is versioned by (integration-registry, config,
  invariant) revisions with mismatch = cache miss, so normal and
  conditional hits cannot serve different policy metadata;
  cache-relevant fields are defined.
- The DataDome contract aligns with documented vendor behavior where
  hardening was not intended (configurable SameSite, one-year
  31,536,000 s cap replacing the over-vendor 396-day figure, 512-byte
  size per the current Fastly module, Domain per vendor guidance
  PSL-validated); the deliberately reduced pointer allowlist requires
  product AND vendor acceptance (sign-off 28); and the publisher origin
  is named in sign-off 23 as a ClientID observer - the overlay is the
  mechanism, the row now names the recipient.

P2/P3: deployment-metadata 'm' key class with the graphless flag's full
wire lifecycle (N+2 + backfill attested in the value, operator CAS
clearing); N+1 rollback tests aligned to read-and-fail-closed for
authority state; AuthorityRefresh's access set enumerated (row CAS +
authority-record CAS - the old wording forbade a read its own protocol
needs); expired-live-plus-persisted fallback decided (expired live does
not suppress fallback); the skew constant became an algorithm
(beyond-window malformed, no clamping, expiry grace, within-window
equality routing to the restrictive tie rule); policy revisions have
canonical identity (content digest + activation generation);
provider-code-registry.md (hmac allocated) and psl-snapshot-ref.md
(ICANN+private, IDNA, IP/single-label host-only) created; the GPP
decoder gap (sections 24-27, usnat-v2-decodable-but-unpinned) is an
explicit prerequisite; sign-off rows 25-28 added; adapter qualification
is a pre-ratification prerequisite; telemetry residue removed; the hook
fragment, provenance comma, test host-equivalents, and client-draft
Axum claim are fixed.

Ledger: Round 13 adopts the added-vs-verified vocabulary - rows are
'text-added' until a subsequent review declines to reopen them;
all R12 rows retroactively so marked.
…out lifetime, and closed DataDome directions

P1 fixes:

- The authority-state transition rejects revision regression (incoming
  < stored refused; equal is idempotent and payload-equivalent, with a
  named delayed-r2-after-r3 test) - CAS alone let a delayed older
  commit restore older authority.
- Anti-replay keeps a bounded replay history keyed by (source class,
  semantic digest) - horizon-retained, capped at 16 per
  permission-source, saturation fails restrictive - because a single
  current digest forgets grant A the moment refusal B displaces it,
  and replayed A would then look novel.
- Timestamp-less opt-out has one lifetime contract, chosen explicitly:
  TTL-sticky (inert at consent-TTL valid_until; within lifetime only a
  newer timestamped grant clears; administrative clear is an optional
  early exit). The migration spec's 'irreversible artifact requiring
  administrative clear' wording is superseded; sign-off 16 rewritten.
- The stub-backfill invariant is establishable: N+2 convergence, the
  backend's documented listing settle window (listing completeness is
  a declared capability - an unboundable backend cannot host this
  migration), repeated scans to two consecutive zero-discovery passes,
  attestation in the flag value - and misses reconcile rather than
  fail: a late-surfacing real row for a withdrawn suffix promotes the
  prefix entry to a full family revocation at first sight.
- The rowless withdrawal record is one class end to end: 'w' + provider
  code + prefix in the physical grammar, wire schema (capped list of 8,
  saturation flag, CAS version, valid_until >= max cookie lifetime),
  linearizable CAS in the capability matrix, N+1 fail-closed reader,
  and migration row 13 aligned (exact-cookie records superseded).
- Roaming unverifiable cookies: cookie-only expiry is a disclosed
  residual with re-attempt on every re-presentation - sign-off 29.
- Verified suffix variants cannot amplify suppression: durable
  per-family negative records require row-backed families; rowless
  negative state is exclusively the capped prefix record.
- DataDome's browser-response direction is a decision-scoped positive
  allowlist (Respond: Location/Content-Type/Cache-Control/Pragma +
  typed cookie + enumerated vendor headers from the allowlist file's
  new response section; Continue: cookie + vendor headers only) - the
  atomic-302 example's Location is admitted, not assumed.
- Incoming X-DataDome-ClientID is owner-only like the cookie: extracted
  into the DataDome-only view, removed from the shared request and
  upstream routing, added to RedactedRequestView's strip set - DataDome
  itself prioritizes the header over the cookie.
- Normal and conditional cache hits serve the same persisted post-hook
  finals captured at cache fill (revision-versioned; mismatch = miss);
  metadata identity holds by construction with no mutator-purity
  assumption.

P2/P3: absence suppression is one-shot per positive summary (writing
retires the summary; entry valid_until capped by the retired horizon);
policy activation uses the config store's globally assigned push version
(per-instance monotonicity ordered nothing across a fleet); unknown GPP
section IDs contribute nothing - with the honest bound that embedded
GPC in an unparseable section is undetectable; the physical grammar has
complete byte-level constructors (i/r/s/x/m/w, family-id = 64-hex
SHA-256, m-names 16-char padded, 128-byte cap, per-class parsers);
DataDome cookies must RFC 6265 domain-match the request host with
Expires-to-Max-Age normalization (both present: Max-Age wins; ceiling
violations reject the batch); Cache-Control/Vary parsing is one shared
fail-closed core parser with fixtures (Vary: * = shared-uncacheable);
NAT saturation collateral is sign-off 30; PSL fill, decision records,
and adapter qualification are named ratification gates; the provider
section 5 lead now states the authority-state commit point, section 4's
'cannot be gated at all' distinguishes geo (cannot) from device
(deliberately not); typo/punctuation/metric residue swept; the client
draft's resv notation is marked superseded; and the old DataDome spec
carries an explicit supersession banner so 'applies last' cannot be
read as current.

Per maintainer direction, the sign-off table is decision-centric: the
Owner column is replaced by a Decision-record link column - the table
tracks decisions; the records capture deciders.
…observed rows, and vendor-faithful DataDome cookie handling

P1 fixes:

- N+1 rollback can no longer invalidate the rowless proof: any N+1
  startup that observes the graphless flag active CASes it to
  suspended (rowless classification stops fleet-wide); re-activation
  after roll-forward requires complete re-attestation over the gap
  window; the N+2 -> N+1-mint -> N+2 schedule is a named test.
- The admission rule gains the observed-row arm, adopted verbatim from
  the review: a successful row read is safe admission evidence (an
  eventual not-found is not), and the permission-exempt sequence -
  derive family ID -> create-if-absent minimal stub with no positive
  authority -> commit family revocation -> deny all use in between ->
  only then expire the cookie - unblocks both first-upgrade withdrawal
  of untouched v1 rows and promotion of late-surfacing rows.
- The w record protocol is enforceable end to end: abstract capability
  row (globally strong reads + linearizable CAS + the bounded
  listing-visibility window, all graphless-migration eligibility
  gates, retention >= max cookie lifetime), four runtime-failure rows
  (read fail = indeterminate; write fail = cookie retained, client
  retries; saturation semantics; promotion failure = deny until
  commit), and the migration-window rule that every HMAC row discovery
  consults w state before use. Saturation promotes listed suffix-hash
  matches only - it never blanket-revokes row-backed identities, and
  the beyond-cap overflow residual is declared.
- Replay-history saturation is a serialized state machine: 16
  semantic-state slots where a same-semantics TCF renewal with newer
  LastUpdated updates its slot in place (ordinary renewals never
  consume capacity - the 16-genuine-renewals exhaustion is gone), plus
  a rolling restrictive slot outside the cap so a seventeenth refusal
  is stored under normal recency rules rather than dropped or
  replay-advanceable; saturated/saturated_until serialized, automatic
  slot-expiry recovery, first-class metric, and the cap's denial
  behavior is sign-off 31.
- The migration spec's irreversible-artifact list is corrected:
  timestamp-less suppression is TTL-bounded and goes inert
  automatically - the 'irreversible, administrative clear required'
  entry contradicted the chosen TTL-sticky rule.
- GPP sections 24-27 are demoted to reserved-pending-official-schema
  in both the map and the vendored snapshot: the official registries
  publish layouts only through 23, so IDs without binary layouts are
  not reproducible - those states behave national-only, and the
  official-coverage claim is retracted.
- Both DataDome cookie-return forms (Set-Cookie and the
  session-by-header X-Set-Cookie field) lower into the one typed
  datadome cookie operation with identical validation; the X-Set-Cookie
  field itself is never forwarded - previously it would have forked
  implementations or failed the whole challenge open.
- The Respond representation rule narrows to Content-Type only;
  encoding and validator fields stay reserved even for challenge
  bodies, with vendor needs arriving as reviewed allowlist-file
  additions - the prior wording could flip enforcement to fail-open on
  an ambiguity.
- The cache merge preserves unknown snapshot directives verbatim
  (mutations still drop unknowns), folds Expires into the freshness
  bound, and forbids mutations introducing max-age/s-maxage where the
  snapshot had no upper bound (RFC 9111 5.2.3/5.3).

P2/P3: HEAD serves the persisted GET artifact (parity by construction
under RFC 9111 4.3.5); the cache revision tuple has fleet-stable
identity (registry content hash, global config push version, build-time
invariant constant); the 300-second tie preserves the winner's complete
tuple (no near-window grant ratchet); the physical grammar is
consistent (suffix <= 123; m + 2-digit registry index replaces padded
names and their foo/foo- aliasing; the tag enumeration includes w and
m; the provider-registry note acknowledges hmac codes in w keys;
family-ID derivation is defined with domain tag tsfam1| and
cross-language vectors); the graphless migration has an operational
runbook with the correct section pointer, abort/suspension,
re-attestation, and quiet-period clearing; the policy digest is defined
(tspol1|, canonical JSON, vectors); the DataDome pointer protocol has a
total parser contract (case, OWS, duplicates, count/byte limits,
cookie-source priority, fail-open semantics); Pragma is dropped from
the response allowlist (no standardized response meaning); the
wire-schema sentence is restructured; row 3e's phrase is completed; and
the sign-off rows are renumbered into order with row 31 added.
…e suspension, and representable replay state

P1 fixes:

- Suppression admission has one coherent contract with two observed-row
  sequences: destructive (stub -> family revocation -> expiry) and
  non-destructive (stub -> per-permission suppression CAS ->
  deny-while-incomplete, family and cookie retained) - the single
  revocation-shaped sequence either destroyed identities for
  SharingOptOut/TargetedAdvertisingOptOut or dropped their suppression.
  'Unconditional' now explicitly means independent of prior positive
  authority, never of family admission, closing the fabricated-suffix
  strong-record path the permission wording had reopened.
- The graphless flag is a globally-strong state machine (absent ->
  active -> suspended -> re-attested -> cleared, epoch per transition;
  globally observable strong reads are a capability cell for this key):
  N+2 proves active through a bounded lease revalidated by strong read;
  N+1 CASes suspension, reads it back, and waits one full lease window
  before minting - no unstubbed cookie can exist while any active
  lease survives, closing the stale-instance misclassification race by
  construction. w consultation continues under suspended.
- GPP 24-27 are removed from decoder work items and accepted versions:
  reserved and inert, national-only for MD/IN/KY/RI (sign-off 32); the
  'Maryland opt-out must not vanish / extend the decoder' phrasing is
  withdrawn as incompatible with reserved status.
- Replay slots are keyed by a timestamp-independent state_key (semantic
  result excluding LastUpdated) distinct from the evidence digest -
  renewals genuinely update one slot; replay defense derives from
  recency comparison, needing no per-value history - and the
  unrepresentable rolling slot is replaced by a fixed saturation_epoch
  (saturated_until = entry + consent TTL, never extended by overflow
  values; overflow evidence affects the live request, is not stored,
  and advances nothing on replay).
- Mutation-introduced must-understand is rejected: under RFC 9111
  5.2.2.3 a cache understanding the status may ignore an accompanying
  no-store, so 'adding' it can weaken a stored no-store - it is not an
  additive restriction; snapshot-present survives.
- The Pragma contradiction is resolved with a drop-individually list
  (known-harmless standard vendor fields are dropped and logged, never
  batch-invalidating; unknown/active fields still invalidate to
  Continue), the allowlist file corrected and retitled for both
  directions, DataDome's documented response (Set-Cookie, Pragma,
  X-DataDome, Cache-Control) becomes a verbatim test fixture, and the
  batch-invalidation fail-open consequence is inside sign-off 28.
- Identifier derivation is fully assigned with computed known-answer
  vectors embedded: record-kind byte = class-tag ASCII; family-ID
  vector for tsfam1|i|hmac|{64xa}.AbC123 =
  e90616c381f64965b8326f17108c3c481cee932b2d7f8af783c7bdc2e21591ef;
  w suffix hashes = tswsx1|-tagged SHA-256 truncated to 16 bytes
  (AbC123 -> 08cb55acf42929772862e82b0960c134); the permission spec's
  stale fam:v0 example is replaced.

P2/P3: registration versions gain a bump contract (content-hash where
declarative, review-checklist otherwise; invariant revision bumps with
parser/merge changes); head-only artifacts never satisfy a later GET
and validator mismatches invalidate rather than update (RFC 9111
4.3.5); w retention >= max(cookie lifetime, row/S2S horizon) with the
overflow-resurrection residual added to sign-off 30; the Fastly listing
cell requires a cited platform completeness bound, pagination, and
failure semantics; the suffix limit is 123 everywhere with boundary
fixtures; Expires participates via RFC 9111 4.2.1 with conservative
invalid-date handling; the pointer list has a tokenization grammar
(repeated fields joined by SP, SP/HTAB runs, empty tokens ignored,
ASCII lowercase before duplicate detection); the ledger's R15
malformed rows and false Pragma claim are corrected; the wire-schema
grammar break, stale readiness-step pointer, allowlist title, and
migration tombstone fragment are repaired; sign-off row 32 added.
…revision-matched S2S, and fully closed DataDome/GPP boundaries

P1 fixes (second-order interactions the prior repairs left open):

- N+1 has one contract: it may create s-class stubs and write negative
  suppression entries, but never commit or clear positive authority
  until N+2. A first post-upgrade GPC or SharingOptOut now executes
  fully on N+1 - the earlier 'neither creates nor clears' wording made
  first-upgrade withdrawal unexecutable.
- Graphless suspension is crash- and concurrency-safe: the suspended
  epoch stores a fleet-stable not_before = suspend + L (L = 120 s,
  assigned and stored) that every N+1 observer honors, not just the
  suspending instance; the state chain is cyclic (re-attested-active ->
  suspended for a second rollback) with CAS-loser retry; and the
  graphless flag requires globally observable strong reads, now a
  capability cell rather than write-once/CAS alone.
- One w saturation outcome: any real row surfacing under a saturated
  prefix is denied and its family revoked (listed or overflow),
  closing the resurrection where a completed rowless withdrawal became
  usable the instant its row appeared; w consultation keys on the
  record's valid_until, not the rowless flag, so a late row after the
  flag clears still finds a live w.
- Restrictive replay overflow is recorded under an epoch-pinned marker
  (timestamp and valid_until fixed at saturation entry), so replaying
  the same overflow value can no longer advance observation time and
  renew denial indefinitely; sign-off 31 text updated.
- Two-record use requires row.provenance_revision ==
  authority.summary_revision, failing closed both ways, and the strong
  summary now carries resolved jurisdiction - so a newer committed
  summary can never be combined with a stale row's jurisdiction to
  authorize egress under the wrong rule.
- The cache invariant includes request-side authority (RFC 9111 3.5):
  on a response to an Authorization-bearing request the origin did not
  mark shared-cacheable, integrations may not introduce public,
  must-revalidate, or s-maxage; the invariant forces private, no-store.
- The 304 contract splits the two cases: a local conditional hit
  re-emits persisted finals, while an origin-revalidation 304 updates
  the stored base from the 304 first (RFC 9111 4.3.4) and reruns
  processing or refetches a full 200 if cache-relevant fields changed -
  a cached public response can no longer survive an origin private,
  no-store revalidation.
- DataDome sessionByHeader is unsupported by default (TS never requests
  it); enabling it forwards typed X-Set-Cookie/X-DD-B and accepts a
  JS/local-storage observer as an explicit sign-off-23 opt-in.
- Every documented DataDome pointer has exactly one assigned outcome
  (outcome table in the allowlist file: cookie / forward / merge /
  drop / invalidate), the documented vendor response is a
  Respond-asserting fixture, and every security Respond ends
  Cache-Control: private, no-store with CDN fields stripped
  regardless of status or pointers.
- GPP sections 24-27 are fully reserved: removed from the accepted-
  version table into a separate reserved table (no accepted version),
  reserved-vs-unknown difference defined (logging only), and supported
  sections pin to an immutable registry commit with vendored encoded
  vectors.

P2/P3: HEAD-only artifacts are a distinct type that never satisfies a
GET, with validator/Content-Length update rules; Vary ordering reaches
TS's own cache key (mutation -> Vary -> key -> commit); state_key and
evidence digests have canonical construction (tsstk1|/tsevd1|) with a
vector; a non-HMAC family vector disambiguates the graph-key bytes; the
suffix alphabet is the portable [A-Za-z0-9._~-]; revision hashes are
domain-separated and adapter-independent (tspol1|/tsreg1|/tscfg1|); the
must-understand clause is scoped to the six sticky directives; Respond
transport is size/deadline/encoding bounded; sign-off rows 9/10/31 read
open per the decisions README; and the 'and the and a' fragment, the
providers/hook stale section-4.2 pointers, and the malformed R15/R16
ledger tables are corrected (the ledger's false 'Pragma removed from
allowlist' claim recorded, tables rebuilt as prose).
…te table, and single-source S2S authority

P1 fixes:

- The N+1 rollback tests mirror the one contract exactly: stub creation
  and negative suppression entries are tested read AND write (a
  rolled-back N+1 receiving SharingOptOut persists the suppression),
  positive-authority commits and clears are asserted forbidden - the
  required-and-forbidden contradiction between contract and tests is
  gone.
- The graphless migration has a total state table (semantics x flag x
  strong record x row read x w -> one outcome each, with an
  indeterminate default): the v1 exception is declared, not hidden
  (v1 semantics use recognized cookies row-lessly until the new model
  activates - the pre-epic posture persisting is matrix row 14, not an
  undeclared outage); strong-record-present + successful stale
  not-found is visibility lag -> indeterminate (a stale replica must
  not fork a just-minted identity); and the 'while the flag is active'
  w-consultation leftover is replaced by valid_until keying.
- The 120 s suspension barrier is skew-safe: not_before comes from
  store-issued time where available, else committer time + L + S
  (S = the normative 300 s bound), and both not_before and L are
  serialized fields of the metadata value with clock-skew and
  suspender-restart tests named.
- S2S authority has one source: the strong summary is the sole decision
  input (jurisdiction included); the permission spec's authority
  paragraph now recomputes from the summary alone, and the row-schema
  provenance row is demoted to 'audit mirror only' with identity/
  partner data released only behind the revision fence.
- The saturation restrictive marker pins to the FIRST restrictive
  overflow's own timestamp with a full consent TTL - epoch-entry
  pinning back-dated genuinely new opt-outs and expired them early;
  later overflows inherit the marker (bounded shortening), and
  sign-off 31 is rewritten to ratify exactly that plus fresh-consent
  denial.
- Revision identity is one pair everywhere - (tspol1| content digest,
  deployment-metadata activation ordinal): digest for identity (A->B->A
  yields A's digest at a new ordinal), ordinal for fleet-wide order
  CAS-incremented in the metadata primitive; the hook's push-version
  and the wire schema's digest-plus-generation variants are superseded;
  section 5.5 was rebuilt in the process (also repairing a paragraph a
  scripted edit had corrupted).
- Cache stickiness is form-preserving: an unqualified private/no-cache
  never becomes field-qualified and qualified field sets never shrink
  (private="Set-Cookie" authorizes shared storage of everything but
  one field); the contradictory must-understand tail is removed.
- Overlay cache identity: matching uses the exact final publisher
  request (redacted-view keying collapses personalized variants),
  sensitive Vary-nominated values persist only as keyed digests, and
  responses derived from identity-bearing TS overlays force private,
  no-store.
- sessionByHeader is startup-rejected in v1 - one state: never
  requested, X-Set-Cookie unclassified, incoming header ClientIDs not
  forwarded to the vendor; the R16 translation into an ordinary cookie
  is retracted as not equivalent (header-session JS expects
  X-Set-Cookie/X-DD-B and outranks cookies); the old DataDome spec's
  banner supersedes its header-mode requirement; full header-mode
  support is the enumerated sign-off-23 opt-in.
- One pointer contract in one place: the decision x session-mode x
  pointer matrix lives in datadome-header-allowlist.md (hook duplicates
  deleted), X-DD-B is enumerated (drop-individually in cookie mode),
  no wildcard remains, and both documented vendor responses (challenge
  and Set-Cookie X-DD-B allow) are decision-asserting fixtures.
- The browser trust boundary enters the sign-offs: the cookie cannot be
  HttpOnly per vendor guidance so every same-origin script observes it,
  Respond serves vendor HTML with publisher-origin access, CSP cuts
  both ways - sign-offs 23/28 rewritten to ratify these observers,
  vendor code, CSP behavior, and challenge redirects.
- HEAD/304 preserve transformed-artifact integrity: origin-side and
  processed-side metadata are stored separately, updates touching
  byte-coupled representation fields (Content-Encoding, Content-Type,
  validators, digests) invalidate and refetch a full 200 per RFC 9111
  3.2, and a changed Vary evicts or rekeys.

P2/P3: w entries carry per-entry horizons (a single record lifetime
either shortchanged late entries or rolled forever); the replay hash
prose is aligned to its own vector - ASCII source token, not enum
bytes, slots per source - with the evidence-digest vector embedded
(tsevd1|...|lu=1690000000000 -> 67259c02...); the schema-floor value is
encoded (writer version + minimum-reader, numeric order) making
N+1-after-floor decidable; the DataDome complete-response deadline is
3000 ms monotonic with 1500 ms first-byte retained and encoded
responses batch-invalid; the cookie parser is total (repeated Cookie
joined, Set-Cookie never combined, strict attribute/Expires rejection,
512-byte serialized measure); adapter header ceilings are enumerated
capability cells validated against core's budget at startup; the
ledger's GPP/PSL 'pinned' claim is corrected to
placeholder-until-ratification; the section 5.5 grammar dangler, the
remaining 'and the and a' fragment, and the duplicated
Set-Cookie-reserved sentence are fixed.
…overage, and single-source activation identity

P1 fixes:

- Negative gates are release-, config-, and flag-invariant: the total
  state table opens with that rule and splits the v1 catch-all into
  three rows - positive relaxation only, revocation/suppression
  present -> denied, live w -> withdrawn - so an N+2-written rowless
  withdrawal binds a rolled-back N+1 (the reproducer lands on the w
  row, not the catch-all); the migration rollback contract states the
  same invariance.
- The state table covers ordinary operation and legacy backfill:
  row-backed use with matching authority is normal use in every flag
  state (the flag governs rowless classification only), and a found
  row with a stub-only authority record routes to the live
  AuthorityRefresh backfill - which admits on the observed row plus
  live resolution, never a prior summary, so the legacy recovery path
  is reachable by construction (permission spec states it too).
- The error default no longer suppresses admitted destructive
  withdrawal: 'no negative writes' became 'no writes whose admission
  depended on the failed read' - a successfully read strong family
  record proves admission, so live GPC commits the family revocation
  (and SharingOptOut its suppression CAS) despite a row read failure;
  only the cookie expiry waits for the commit.
- The suspension deadline has a clock contract end to end: the
  store-time branch re-reads store current time at every not_before
  check (one clock domain); the fallback subtracts S_fleet - the
  maximum pairwise fleet clock skew, a declared and monitored
  infrastructure bound distinct from the evidence tolerance S - at
  comparison as well as at write; the clock domain is serialized in
  the metadata value; fastest-observer/slowest-committer and
  suspender-restart tests are named; a backend with neither store time
  nor a skew bound cannot host the migration.
- The activation ordinal is a real register: deployment-metadata name
  02 holds linearizable {source_version, policy_digest, ordinal} with
  idempotent same-activation reuse (the CAS winner assigns, everyone
  else adopts) and stale-source_version rejection; digest-only
  backends are declared unable to detect staleness (safe, visible in
  the metric); the hook's 'globally assigned push version' revision is
  superseded by the tscfg1| digest plus the section 5.5 pair.
- The strong summary can enforce jurisdiction expiry: it gains
  jurisdiction_observed_at, written only by live geo resolution -
  evidence timestamps are disqualified as proxies (TCF LastUpdated
  predates lookups; policy baselines have no wall clock) - and
  decision 25's S2S age gate measures against it.
- The saturation shortening is a declared product choice, not a buried
  contradiction: permission 4.3's TTL-sticky rule carries the
  exception (a restrictive overflow during a saturated epoch inherits
  the first-overflow marker, down to nearly zero lifetime), decisions
  16 and 31 both state it with the rejected alternatives
  (per-overflow state: unbounded storage; marker refresh: replay
  extension), and epoch expiry is a complete transition - lazy slot
  GC, re-saturation opens a new epoch pinned to its own first
  overflow, no cross-epoch timestamp inheritance.
- The pointer matrix has no predecessors: the allowlist file is
  rewritten with the matrix as the only browser-response contract
  (old response-direction table and cookie-translation notes deleted),
  the X-Set-Cookie cell terminates in one exact outcome (invalidate
  the batch -> Continue, mode mismatch), and the hook's inline
  decision list is physically replaced by a deferral - its
  'unclassified -> batch handling' phrase aligned to the cell.
- Incoming header ClientID has one v1 path: stripped from the shared
  request and never used for the vendor payload (ClientID derives
  only from the datadome cookie; forwarding a header ClientID would
  misdeclare the session mode per the vendor contract); the
  unreachable 'header form wins' priority rule is deleted; a
  both-sources fixture pins cookie-only derivation.
- The 304 flow is staged then atomic: metadata diffs off-record
  against stored origin-side metadata; byte-coupled fields gate on
  changed, not present (a 304 routinely repeats the matching
  validator); safe changes publish in one atomic cache commit so a
  concurrent hit never sees old transformed bytes under new policy
  metadata; unchanged -> local-hit re-emit.
- Artifact recovery is unconditional: absence or revision mismatch
  strips every conditional field - the client's and TS's own - fetches
  and processes the full 200 under current revisions, then evaluates
  the client's condition against the new processed validator.

P2/P3: the N+1 interim is one release x config-shape matrix (policy
resolution, live gating, negative gates, row writes, positive commits,
batch egress - a new-shape denial on N+1 is telemetry, never gating);
GPP/USP evidence digests omit |lu= entirely with an embedded vector
(tsevd1|gpp|... -> 89b08580...), and replay slots carry a
per-permission observed_at map refreshed only for changed tokens (a
P4-only change cannot refresh P1's age); w capability retention is the
per-entry max(cookie, row, S2S) horizon with a permanent strong-read
obligation for HMAC row discovery; HEAD updates compare against
origin-side metadata only and never touch processed-side headers; the
sensitive-Vary rule became a reproducible contract (HMAC-SHA-256,
tsvry1| grammar, every nominated value digested, secret-store key with
versioned id, zero-key vector 60fdeb3a...); the adapter ceiling matrix
now exists (Axum fixed at/above budget; Fastly/Cloudflare/Spin
qualification-pending, fail-closed at startup) with the exact
counted-bytes formula and over-budget-snapshot behavior; the DataDome
cookie parser defines the '; ' join, duplicate-datadome ambiguity,
foreign-name and HttpOnly rejection, floor-based Expires conversion,
and normalized-form size; pointed-field multiplicity is closed
(singleton duplication invalidates atomically, list fields join per
RFC 9110 5.3); Respond on HEAD validates the body but emits none; the
3000 ms deadline has measurement points (pre-acquisition to final
byte, async cancellation); malformed Cache-Control has an enumerated
result (uncacheable + merging batch rejected); sign-off 28 names the
exact X-DD-B divergence; the GPP snapshot joins PSL as a named
ratification gate and states its own placeholder status; the state
table's stray column and absorbed prose are gone; the metadata row is
a proper table cell; the authority row reads 'not read for gating';
'ratified in sign-offs 23/28' became 'enter sign-offs 23/28 for
ratification'; the two R18 ledger overclaims (matrix single-sourcing,
ceiling cells) are corrected as text-added-not-verified; and the old
DataDome spec's superseded steps carry inline supersession notes at
each site, not only the banner.
…l activation register, and collision-free cache identity

P1 fixes:

- The classification contract is an ordered first-match-wins procedure
  replacing the five-column table that was neither total nor disjoint:
  negative gates evaluate first (w, then family revocation, then
  suppression - the w/revocation overlap has one outcome because
  promotion onto an already-revoked family is an idempotent no-op),
  the v1 exception runs after them, and the rowless step keys on the
  strong-class read's authoritative absence - the old row's
  'authoritative not-found' eventual read is named as a state the
  store cannot produce. The missing states have steps: an absent (not
  just stub) authority record on a found row routes to the
  AuthorityRefresh backfill, and nonmatching revisions fail the fence
  (row data withheld, summary-only posture, live refresh realigns).
- The policy-activation register keeps a bounded 16-entry history:
  exact (source_version, digest) pairs adopt their historical ordinal,
  closing the interleaved-eviction scenario that minted two ordinals
  for one activation; the same source_version with a different digest
  fails closed as mixed-binary parse divergence, never a novel pair;
  stale and window-expired versions are rejected; the digest-only
  fallback is deleted - source_version must be ordered and assigned
  exactly once upstream (backend push version, or the monotonic push
  sequence the ts config push envelope stamps); and the register is a
  named capability in both provider matrices.
- One S2S authority source, verified this time: the provider-switching
  paragraph claiming S2S 'recomputes from' row provenance is deleted
  (audit-mirrored only), and the permission spec's summary enumeration
  declares itself a reference to the one normative schema (providers
  6.3 wire record) with jurisdiction_observed_at included.
- The restrictive marker is marker-scoped, not epoch-scoped, in
  lifetime: it lives to its own valid_until, outliving the epoch when
  the first overflow arrived late (discarding it at saturated_until
  would deny that overflow its promised TTL); the record holds at most
  one live marker per source so overlap never needs representing;
  later overflows inherit it across epoch boundaries (the anti-replay
  rule and the declared shortening); a fresh marker pins only after
  expiry; and 'novel values cannot grant' is narrowed to the epoch.
- The Vary digest grammar is collision-free: presence, instance count,
  and length-prefixed member octets (absent no longer hashes like
  present-but-empty, per RFC 9111 4.1; ['a','b'] no longer collides
  with 'a,b'), with new zero-key vectors for the present and absent
  forms.
- Origin-304 're-derived finals' has a defined derivation: replay the
  persisted mutation IR (the accepted operation batches, whose
  append/replace/merge semantics are deterministic core functions)
  over the updated origin metadata plus the invariant pass - mutators
  never re-run; IR-less entries force a full refetch; publication is
  one atomic entry commit, with changed-Vary rekeying ordered
  insert-new-entry-then-update-index so torn states miss; both are
  adapter capability cells.

P2 fixes: the per-source replay slot has a defined transactional
algorithm (current_state_key pointer; comparison against the current
slot's vector, never the revisited slot's; per-permission copy-forward;
full map replacement on A-B-A; named vectors); malformed/absence
suppressions gain a narrowly scoped recovery-observation clearing rule
(a valid grant presented after the suppression clears it without
refreshing the grant's pinned age; opt-out stickiness untouched); the
suspension named test states the branch-appropriate comparison and the
store-clock/S_fleet branches are capability cells; every DataDome
matrix cell is terminal (non-3xx Respond Location invalidates the
batch; Continue Location/Content-Type/Cache-Control invalidate with
effects dropped); Respond on HEAD omits Content-Length (RFC 9110
9.3.2 - HEAD bytes cannot establish the GET length absent a vendor
equivalence guarantee); the fleet Vary key is a deployable contract
(setting name, 32-byte CSPRNG floor, key-id grammar, fail-closed
startup, overlap rotation, capability gate); and the 304 row counts
four revisions, evaluates the complete RFC 9110 section 13
precondition set after recovery (If-Match/If-Unmodified-Since can
yield 412), and states the safe-update field set once as the
'cache-relevant fields' definition.

P3 fixes: the response-eligibility table is structurally valid again
(the unescaped tscfg1 pipe had split the 304 row into a third column;
the row is rebuilt with zero content pipes and the separator
normalized); the GPP snapshot cites migration section 4; the ledger
corrects three prior overclaims as text-added-not-verified (R18 S2S
sole-source, R19 Vary full-contract, R19 no-cross-epoch-inheritance)
and adds the round-20 section; the broken _classification_ emphasis is
repaired; and cookie citations reference RFC 10025 (obsoleting RFC
6265).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant