Skip to content

docs: reconcile demand-driven-hosting design doc to settled model#4736

Draft
sanity wants to merge 2 commits into
mainfrom
docs/reconcile-demand-driven-hosting
Draft

docs: reconcile demand-driven-hosting design doc to settled model#4736
sanity wants to merge 2 commits into
mainfrom
docs/reconcile-demand-driven-hosting

Conversation

@sanity

@sanity sanity commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Problem

docs/design/demand-driven-hosting.md (merged via #4659, marked "LOCKED 2026-07-01") had drifted behind a week of confirmed design decisions (2026-07-02 through 07-08) and carried one outright-wrong claim. It is the canonical WHY-and-HOW reference the invariants file points at, so a stale copy misleads future agents touching hosting/eviction/subscription code.

Approach

Reconcile the doc against the newer ground truth, in priority order:

  1. .claude/rules/hosting-invariants.md (updated all week with Ian's confirmed decisions)
  2. the build spec (hosting-spec.md)

This is a reconciliation, not a rewrite: sections 2, 3, 4, 5a-5c, 6, 7, 8 (the subscription-chain / computed-upstream / renewal / collapse mechanics) are still current and untouched. Fixes concentrate in Section 9 (the redesign-piece references, where the stale eviction/admission/wire claims live) plus light touches on Sections 1, 5d, 10.

Corrections

  • Wire-change error (Piece H, was ~L203). Fixed the wrong claim that the redesign is "behavioral, not a wire-format change." R3/R4 add version-gated wire changes (summary-first PUT message, a subscribe flag on GET/PUT, and the standalone SUBSCRIBE variant repurposed as Renewal). They are engineered non-breaking (variants kept + repurposed, new surfaces version-gated), which is not the same as no wire change.
  • SUBSCRIBE retirement added (new Piece in Section 9). The doc did not describe it at all. Added: subscribe rides on GET/PUT via a subscribe flag; standalone SUBSCRIBE deprecated-not-deleted and repurposed as Renewal (variant kept for bincode/wire-compat); client ContractRequest::Subscribe preserved via a deprecation adapter to GET-with-subscribe; it IS a version-gated wire change.
  • Eviction model (Piece A). Replaced the stale (subscriber_count, last_get_seq) ranking and the "gradation bites at admission + OOM valve" framing with the settled model: ONE demand-ordered decision over ascending (local_subscription_count, downstream_subscriber_count, recency, key bytes); no separate admission, no OOM valve, no displacement-chain cap; a subscribed contract IS evicted when nothing ranks lower; min_ttl dropped.
  • Reads-as-demand (Piece A, Section 1, Section 10). Reversed the old "heavily-read but never-subscribed = transient cruft, superseded by subscribe-implies-pinned" framing. Reads and PUTs are now a permanent demand signal: the River UI / web-UI container contracts (read but never subscribed) must stay hosted and renewed. Subscriptions outrank reads (the recency clock starts only at subscription termination).
  • Every-hop / feat: remove relay-caching + GET-auto-subscribe (piece E, #4642) [BLOCKED: PUT half needs D fetch hardening] #4664 (Pieces B/E, Section 1). Every-hop placement is already live (v0.2.93), governed by the demand gauge; feat: remove relay-caching + GET-auto-subscribe (piece E, #4642) [BLOCKED: PUT half needs D fetch hardening] #4664 removes the durable-regardless-of-demand copy, not the every-hop store. No separate admission decision (emergent from the single eviction).
  • Section 5d degradation. Reconciled the "closer peers refuse the subscribe (admission)" framing to the emergent single-eviction model (no NACK).

Testing

Docs-only change; no code touched. Style checked: no em-dashes and no "not X, but Y" antithesis in added prose.

Flagged as uncertain (left unchanged)

The interaction between reads-as-demand and the Section 5a/6 contract_in_use renewal gate is underspecified in the ground truth: the build spec still defines interest-gated renewal as (local client OR downstream subscriber), while the newer invariants say reads/PUTs also keep a contract "renewed in the update mesh." I did not invent a resolution, so Sections 5a/6 (and the corresponding clause in the Section 10 summary) are left as-is. This should be pinned before the renewal code is touched.

Refs #4642, #4659.

[AI-assisted - Claude]

sanity and others added 2 commits July 8, 2026 12:08
The design doc (merged via #4659, "LOCKED 2026-07-01") predated a week of
confirmed decisions (2026-07-02..07-08) and carried one outright-wrong
claim. Reconcile it against the newer ground truth in
.claude/rules/hosting-invariants.md and the build spec.

Corrections:

- Section 9, Piece H: fix the wrong claim that the redesign is
  "behavioral, not a wire-format change." R3/R4 add version-gated wire
  changes (summary-first PUT message, a subscribe flag on GET/PUT, and
  the SUBSCRIBE variant repurposed as Renewal), engineered non-breaking
  (variants kept, new surfaces version-gated), not zero wire change.

- Section 9: add the SUBSCRIBE-retirement piece (R4, step 10) that the
  doc omitted entirely: subscribe-flag-on-GET/PUT, standalone SUBSCRIBE
  deprecated-not-deleted and repurposed as Renewal, client
  ContractRequest::Subscribe preserved via a deprecation adapter to
  GET-with-subscribe.

- Section 9, Piece A: replace the stale eviction model. Eviction is now
  ONE demand-ordered decision over ascending (local_subscription_count,
  downstream_subscriber_count, recency, key bytes); no separate
  admission, no OOM valve, no displacement-chain cap. Reads and PUTs are
  a permanent demand signal (River UI / web containers, read but never
  subscribed, must stay hosted), reversing the old "heavily-read =
  transient cruft, superseded by subscribe-implies-pinned" framing.
  Subscriptions outrank reads (recency clock starts at subscription
  termination); a subscribed contract IS evicted when nothing ranks
  lower; min_ttl dropped.

- Section 9, Pieces B/E: no separate admission (emergent from the single
  eviction); every-hop placement is already live (v0.2.93); #4664
  removes the durable-regardless-of-demand copy, not the every-hop store.

- Section 1 and Section 10: routed GET/PUT reads are a third demand
  source (every-hop placement under the gauge), reconciled with the
  no-durable-regardless-of-demand rule.

- Section 5d: reconcile the admission-refusal framing to the emergent
  single-eviction model (no NACK).

Refs #4642, #4659.

[AI-assisted - Claude]

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0199hK8Vdn5DAWcXJsxZHk8H
Fold two follow-ups into the demand-driven-hosting reconcile.

1. Move the authoritative build spec into the repo. It lived in an
   ephemeral ~/code/tmp path that gets deleted, so copy it to
   docs/design/hosting-spec.md, add a header marking it the build-spec
   companion to demand-driven-hosting.md and noting that
   .claude/rules/hosting-invariants.md (the most-current synthesis)
   wins on any conflict, and reference it from the design doc's header
   plus the "build spec" mention in section 9.

2. Resolve the section 5a/6 renewal-gate underspecification. Decision 1
   (GET/PUT reads are a permanent demand signal) plus the shipped #4725
   renewal-gate fix make interest-gated renewal include recent real
   GET/PUT access. Update the renewal/keep-hosting predicate from
   "local client OR strictly-farther downstream" to
   "local client OR strictly-farther downstream OR recent real GET/PUT
   access" in sections 5a, 5d(a), 6, and the section 10 summary of
   demand-driven-hosting.md, and in the interest-gated-renewal
   definition of hosting-spec.md. The stop-hosting gate stays the exact
   inverse. A read-only/PUT-only never-subscribed contract (River UI /
   web containers) is kept renewed by its GET/PUT recency, per
   invariant 3.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0199hK8Vdn5DAWcXJsxZHk8H
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