Skip to content

Commit 912412e

Browse files
dzhelezovclaude
andcommitted
docs+build(portal): INVARIANTS honesty pass (INV-15), biome gate, PORTAL_CHECKS docs
- INVARIANTS.md: every row's enforced-by/checked-at/tested-by now cites real, passing tests; INV-2 marked enforced-by-construction (vacuous assert removed); INV-3 cites the interleaving + randomized properties; INV-9 cites the runtime assert + delegation matrix; INV-10 cites the real link tripwire; INV-13 rewritten for progress-by-construction + the frontier EXTEND; INV-1 noted as a future-regression tripwire; NEW INV-15 row (child persistence, ported from PR #2). Fixed-gaps section lists the found-and-fixed head-boundary bug + mutation evidence. - scripts/sync-upstream.sh: repo-side Biome gate (pinned 2.5.2, repo biome.json, --diagnostic-level=error) runs before the copy under --test — an unformatted or lint-error portal file fails the build. - INTEGRATION.md: PORTAL_CHECKS row in the env-knob table. - PUBLISHING.md/HOW-IT-WORKS.md: stale prose fixed; portal/config.ts restored as the repo-side compat-harness utility (= origin/main's copy). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent f88a1d0 commit 912412e

5 files changed

Lines changed: 36 additions & 22 deletions

File tree

HOW-IT-WORKS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ Read-ahead raises an obvious question on a multichain app: how deep, and across
7070

7171
**The naive answer breaks.** Give each of 15 chains its own private read-ahead and you get one of two failures. Either the buffers are unbounded and the process **runs out of memory**, or 15 chains each fan out concurrently against the one shared endpoint and the app **rate-limits itself** into 429s. A per-chain design fights itself.
7272

73-
The fix follows from a fact the naive design ignores: every chain streams from the **same** Portal endpoint, so request concurrency and buffered memory are *one shared budget*, not fifteen private ones. The fork routes all chains through a single module-scope controller (`portalGate`) with two self-tuning, zero-config controls.
73+
The fix follows from a fact the naive design ignores: every chain streams from the **same** Portal endpoint, so request concurrency and buffered memory are *one shared budget*, not fifteen private ones. The fork routes all chains through a single shared, lazily-created controller (`portal-gate.ts` — a pure AIMD/row-budget reducer behind a process-shared shell) with two self-tuning, zero-config controls.
7474

7575
**Adaptive concurrency (AIMD).** The client cannot know the endpoint's live capacity — it varies by Portal, by load, and over time — so the controller *discovers* it. It starts at 16 in-flight requests, adds 2 after every 8 clean responses up to a ceiling of 48, and **halves** — down to a floor of 8 — on any back-pressure signal: an HTTP 429, any 5xx, a 409 on the finalized stream, a `retry-after`, or a dropped or timed-out connection. This is the same additive-increase / multiplicative-decrease discipline as Ponder's native RPC limiter, but **global**, because the endpoint is shared. Transient failures retry with exponential back-off; only a genuinely unrecoverable response fails the run.
7676

portal/INTEGRATION.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ The defaults run well without configuration. These environment variables overrid
128128
| `PORTAL_FINALIZED_HEAD` | unset | Pin the Portal finalized head (testing/ops); overrides the `/finalized-head` probe. |
129129
| `PORTAL_METRICS_FILE` | unset | Write per-chain JSON metrics to `<path>.<chainId>`. |
130130
| `PORTAL_GATE_LOG` | unset | Set to `1` to log the shared controller every 20 s. |
131+
| `PORTAL_CHECKS` | `on` | Runtime invariant checks ([INVARIANTS.md](INVARIANTS.md)): `on` runs the O(1) checks (a violated invariant crashes loud instead of corrupting silently); `strict` adds O(n) whole-structure checks (CI/tests); `off` disables all checks — only as a last-resort perf escape hatch or to bypass a false-positive crash while a fix ships. |
131132

132133
Raising concurrency does not help when indexing is the bottleneck, which for a full-history resync it usually is — the fetch is already ahead. The knobs that matter most in practice are the memory budget (`PORTAL_MAX_ROWS_IN_MEM`) on a constrained box and, for a keyed deployment, `PORTAL_API_KEY`.
133134

0 commit comments

Comments
 (0)