Skip to content

Architecture Decision Rulings — R43

Architecture Decision Rulings — R43

Date: 2026-08-13 · Produces: ADR-177 · Findings: F-R43-01F-R43-06

The brief called this round “R42 Phase 1” and that name was already taken. Architecture_Decision_Rulings_R42.md defines R42 Phase 1 as “the Actions-budget work, PRs #85, #88 and #89”, which “produced no ADR by design”. Two documents both claiming to be R42 Phase 1, describing different work, is the drift P6 names arriving through a label. Filed as R43 instead — the R sequence is a filing convention rather than a contiguous range, as docs/README.md records about the absent R10 and R42’s own header records about R41. The earlier record is left untouched (F-R43-01).

This is a build round and it is the first one whose product is a number. Every other round in this corpus produced decisions, code or corrections. This one produced an instrument, and the decisions below are what the instrument said.


0. Findings

FindingEvidence
F-R43-01The round label collides. The brief’s “R42 Phase 1” is already defined, differently, in Architecture_Decision_Rulings_R42.mdThat file’s header
F-R43-02The brief names a document that does not exist. It says the acting-player / spectator figure “is the input 16_room_capacity_and_spectators.md needs and cannot obtain for itself”. No such file is in this repositorygrep -r across docs/; doc_reference_lint.py scans docs/ and would have caught a citation, which is why the figure went into the registry and no document was created to hold it
F-R43-03Three of §5.2 Segment B’s seven derived terms cost work that does not exist. Per-silo grouping, the §10.4 per-viewer digest and §9.6.3’s allowlist recomputation are ⊙0.09 ms of a ⊙3.04 ms derivationSearch across apps/vtt-backend-silo and modules/: no bucketing step in RoomSnapshotDispatcher, no XxHash3 or digest field anywhere in the silo, no pairwise mask
F-R43-04§5.2’s “the dominant cost is viewers, not world complexity” is false, and half of the evidence has been in the corpus since 2026-08-02That measurement refuted Q-015b’s ⊙3 ms and put the fog ladder at 293.4 ms per room-second; BENCH-03 now puts Segment B at 13.5 ms per room-second at 50 viewers. Nothing re-read the sentence resting on the first
F-R43-05The supply-chain ledger points at a BASELINE.md that does not exist, and two artefacts from R42 Phase 2 tell a reader to consult it before editing themosv-ledger.json’s _comment: “READ .github/supply-chain/BASELINE.md BEFORE EDITING THIS FILE”; osv-ledger-gate.mjs’s docstring: “BASELINE.md beside the ledger carries the per-entry reasoning”. doc_reference_lint.py scans docs/ only, so it cannot see this
F-R43-06Four of this round’s own benchmarks were wrong on their first run, and every one was plausibleBENCH-01 refused its own headline case (EffectsPerBatch=1000 exceeded the reference room and the guard threw, so the table read NA for the one figure Q-007 is about); the FFI round-trip arm waited for any published mask rather than the one it dispatched and reported 440–515 ns for something whose own arithmetic says tens of milliseconds; the browser suite navigated to about:blank, where crypto.subtle is undefined because an opaque origin is not a secure context; and its first working run reported crypto.subtle at exactly 0.100 / 0.200 / 0.300 / 0.400 ms — four multiples of the 100 µs performance.now() clamp, a distribution describing the clock

F-R43-06 is the one worth dwelling on, because it is this round’s subject applied to itself. Not one of the four announced itself. The first produced a complete-looking table with one cell reading NA; the second produced a number that was wrong by five orders of magnitude and looked entirely reasonable next to the row above it. What caught the second was not an assertion — it was that two benchmarks measuring different things agreed to within a hundred nanoseconds, which does not happen. The fourth was caught by its own numbers being too round. A rig is not a source of truth by virtue of being a rig, and all four corrections are recorded in the code and in the registry rather than quietly applied.

Two of the four would first have failed on a Sunday. The browser suite was written to be run by a weekly scheduled job; had it not been run by hand before merging, its first real execution would have been on a schedule nobody watches, and the timer-quantisation defect would have published a figure rather than failing. That is an argument for running a T4 suite once locally before wiring it, and it is the reason perf.yml carries a report_only dispatch input.

Three filenames above are written as prose rather than in backticks, and that is typography rather than a suppression. doc_reference_lint.py treats a backticked name as a document this corpus points a reader at; all three are the subject of a sentence saying they are absent, which is the opposite of a pointer. They are deliberately not added to doc_reference_allowlist.txt, whose own header states it is not a place to park a genuine dangling reference — F-R43-05 is corrected at its source instead, in the two artefacts that name it.

F-R43-02 is left open on purpose. The obvious response is to create 16_room_capacity_and_spectators.md and put the figure in it. P6 forbids exactly that: a second document holding a number the registry owns is the failure this corpus records most often, and Quantity_Registry.md’s own entry for the fog ladder still carries the scar of citing epic_frontend_prediction_proposal.md, “no such document has ever existed in this repository”. The acting-player / spectator answer is in Q-110 and Q-112 where a Q-ID can be cited, and nothing was created to hold it.


1. ADR-177 — The measurement rig, and what BENCH-01…05 decided about §5.2

Status: Accepted · Date: 2026-08-13

Context

Quantity_Registry.md has named BENCH-01…05 as the blocker on sixteen rows since the day it was created, and docs/README.md has listed “5 benchmarks. BENCH-02 and BENCH-03 are the priority — they decide whether §5.2’s Segment B budget holds, which is the real capacity bottleneck” under Blocked on measurement — cannot be advanced by document work for as long.

There was no rig. No BenchmarkDotNet reference outside the vendored core/Descent.Sandbox, no criterion in any Cargo.toml, no load generator. tools/bench-asset-decoder.mjs and tools/bench-hub-envelope.cs are two one-off probes, and every geometry figure in the registry — the curve that reduced Q-012 from 512 to 256, Q-058’s path-budget table, Q-075’s permeable multiplier, ADR-143’s 5.32× — was taken once, by hand, on one machine. That is how a measurement becomes folklore: the number survives, the method does not, and nobody can tell a regression from a different laptop.

Decision

1. Three rigs, at the tier §14.8 puts them.

WhereSubject
.NETtests/core/Descent.BenchmarksBENCH-01, 02, 03, 05, and the interop hop Table A has no row for
Rustcore/Descent.Geometry/benchesthe fog curve, Q-075’s ratio, Q-058’s worst case, ADR-143’s two hosts
Browserapps/vtt-frontend-client/benchQ-099 / Q-100 in a real browser, and what a browser cannot say about Q-033

The .NET project is in Full.slnx so a PR compiles it, and is not a test project so dotnet test never runs it — verified rather than assumed. The criterion targets carry test = false for the same reason: a criterion bench under cargo test executes every benchmark in single-shot mode, which is fast and is still a benchmark inside a correctness suite.

2. The rig measures §5.2 Table A’s terms one by one, and says so where a term has no code. Table A’s whole purpose is to show its own arithmetic. Three of Segment B’s seven terms are F-R43-03 and are left unmeasured rather than approximated: a stand-in would publish a figure describing code nobody has written and the substitution would be invisible in the table.

3. Segment B is reported TWICE, because Table A budgets mailbox occupancy and the derivation costed the work. ReplicateToViewers ends at _ = SendDeltas(…); the wire encode runs inside the transport sink. 0.675 ms of work, 0.332 ms in the tick’s own frame. Whether the fire-and-forget continuation still occupies the activation is not settled — Orleans resumes a grain’s continuations on its own scheduler — and it is recorded as open rather than assigned to a segment by assumption.

4. Q-110, Q-111 and Q-112 are registered, and Q-111 is registered with its entity bound because a seat count without one is not a bound. Q-112 gets a status this table has not used before — Normative as a lower bound — because the application half of an intent is measured and Orleans’ dispatch is not, and neither Normative nor would say that.

5. The T4 job gates RATIOS and publishes ABSOLUTES, weekly, never on a pull request. The argument is Q-075’s own: it measured the impermeable advance at 12.99 ms where the reference figure was 29.37 ms and wrote only the multiplier into the ladder, because “what transfers is the multiplier, because both arms run on the same box against the same geometry”. A hosted runner is shared and unpinned; an absolute-millisecond threshold on one is a threshold on the fleet’s scheduling, §14.9 forbids retrying the flake away, and the only remaining move is to widen until it catches nothing. The ratchet is two-sided in all three directions the corpus already uses.

What the measurements decided

Criterion, pre-registeredResult
BENCH-03within 0.5×–3× of ⊙3.04 ms confirms; > 3× reallocates Table A0.675 ms = 0.22× — the budget holds with 7.4× headroom, and the result is outside the band on the side the criterion did not anticipate
BENCH-02predicted 5×–25×; < 3× demotes D-2 to a recommendation3.62×–4.57× — D-2 stays normative, by a 21% margin, and the prediction was optimistic by roughly 2×
BENCH-01> ⊙3 µs per Effect lowers Q-007’s 1,0000.67 µs — 1,000 holds, at 0.670 ms of Segment A’s 3 ms
BENCH-05the derivation basis for Q-008, against D-4’s ⊙0.3 ms0.394 µs application-layer — 760× cheaper, and a lower bound
BENCH-04demoted to a verification in 2026-08-01; not re-promotedthe FFI round trip is exercised end to end (0.264 ms empty, 24.24 ms at Q-012), which is what makes the crate’s own figures checkable from the host side

Every budget in §5.2 Table A holds, and that is a result rather than a formality. This corpus’s R34, R35 and R38 records exist because a round that only says yes is advocacy, so it is worth being precise about what “holds” bought: not one of the four measured terms was within a factor of two of its estimate, the total was out by 4.5×, and the two normative requirements the derivation exists to justify were both confirmed — one narrowly.

Consequences

The negative ones first, because a round that confirms its own budgets must be read carefully.

  • §5.2’s “dominant cost” sentence is false and is corrected here (F-R43-04). Measured, geometry outweighs per-viewer assembly by roughly nineteen times, in the opposite direction to what the whitepaper claimed. Q-016’s composition is refuted: it attributed ⊙95% of a 50-viewer room’s expected cost to Segment B, and Segment B measures at 17% of that total.
  • Q-016’s total is NOT re-derived, and the refusal matters more than a number would. The geometry term is an admitted allowance — 6 advances per room-second at Q-075’s 44 ms — not an expected cost, and turning it into one needs a fog-budget utilisation from real play that does not exist. Publishing a figure derived from the bucket’s ceiling would repeat the exact error that entry already records: a budget ceiling is not a capacity unit. So §4.4’s admission weight and §10.2’s cost model remain unsettled, and this round makes their composition wrong rather than making them right.
  • The benchmark most people would want is the one that is missing. Every figure here is the work AdvanceTickAsync and its callees perform. Orleans’ own message dispatch is unmeasured, and at Q-111’s seat counts it is the term most likely to bind — ~3,980 messages per second into one single-threaded activation. Q-111 is therefore not a seat ceiling and says so in its own row. Closing it needs a load generator against a real silo, which does not exist.
  • A garbage rate nobody had. Segment B allocates 1.42 MB per tick at 50 viewers — 28.4 MB/s per room — and a full Q-007 batch allocates 1.66 MB. Nothing in §5.2 budgets allocation, and a Gen0 collection landing inside a tick is precisely the jitter Table A’s ≥22 ms Reserve row exists to absorb. Recorded, not acted on.
  • The published fog-cost model does not transfer between machines. cost ≈ 0.26 + 1.81·√n is documented as safe to interpolate with; its shape reproduces and its coefficients do not, and its intercept is an order of magnitude away from this box’s zero-occluder reading. Narrowed to interpolate within a machine, never across one.
  • Q-075’s multiplier re-measures at 1.25× against the recorded 1.4×, and the ladder is not re-derived. At 1.25× the fog allowance would be 8 advances per room-second rather than 6, and a third of the presentation cost §9.2 and epic_presentation_smooth_fog.md paid could be given back. Two measurements 12% apart do not move a shipped ladder; the disagreement is in the conservative direction, the ratio is now banded at 1.10–1.60, and repetition is what will settle it.
  • “One LOS ray is 0.4 µs at any occluder count” is withdrawn. 2.8 ns empty, 871 ns at Q-012. The comparison it supports survives by four orders of magnitude; the independence claim does not.
  • The rig itself is now a dependency surface. criterion adds 23 crates to a proprietary, statically-linked engine’s dev graph. cargo deny and every crate’s RustSec record were checked before adoption rather than discovered on a red CI run, and the exit cost is written into Cargo.toml as P8 requires — but the surface is real and Directory.Packages.props gained two Roslyn pins for a package no project references, which is ADR-176’s stated consequence arriving in practice.

And the positive ones.

  • Three registry figures reproduce: Q-075’s 12.99 ms lands at 13.065 ms, Q-058’s 6.1 ms worst case at 5.21 ms on a faster box, and ADR-143’s 5.32× at 5.47×. The decision that declined the universal WebAssembly geometry core now rests on something a scheduled job re-takes rather than on one afternoon.
  • D-3 has a price for the first time: 50.7× at 50 viewers, saving 1.67 ms per tick — a third of Segment B’s budget, and more than every implemented term of Segment B put together.
  • Two rows became normative (Q-003, Q-007), and — more useful than the count — the remaining fourteen each now name what they are actually blocked on, which for nine of them is not a BENCH item at all.

Enforcement (ADR-045)

.github/workflows/perf.yml, weekly and on dispatch, running tools/perf-ratchet.py against tools/perf-baseline.json. Six ratios, banded, two-sided; a ratcheted key that produces no result is red, a baseline entry nothing produces is red, and an empty result set exits 2 rather than 0 — a gate that passes when nothing ran is the shape ADR-174 records twice.

§11 index line

ADR-177: The measurement rig exists; §5.2 Segment B's 5ms budget HOLDS at 0.675ms — 0.22× its own derivation, outside the pre-registered band on the side nobody wrote an action for — and §5.2's "the dominant cost is viewers" is refuted, geometry outweighing per-viewer assembly by ~19× in the other direction. D-2 survives at 3.62–4.57× against a 3× demotion threshold; Q-007's 1,000 holds at 0.67µs per Effect; D-4's ⊙0.3ms intent is 0.394µs in the application layer and Orleans' dispatch is unmeasured. Ratios are gated and absolutes published, on Q-075's own reasoning. (Amends ADR-046, ADR-143; introduces Q-110, Q-111, Q-112.)


2. Write-back performed

  • Quantity_Registry.md: two new sections; Q-003 and Q-007 to Normative; Q-016’s composition refuted with the arithmetic; Q-110, Q-111, Q-112 registered; the Outstanding inventory ⊙ row taken from 16 to 14 and rewritten to name each remaining blocker, because a count is the least useful thing that row can carry.
  • Whitepaper §5.2: Table A’s two ⊙ markers gone and both rows carrying their measured figure; the Segment B derivation block carrying the measurement beside the estimate rather than instead of it; the “dominant cost” paragraph corrected; the ⊙ footnote rewritten to say what is now settled and what is not.
  • Whitepaper §11: ADR-177’s row.
  • docs/README.md: the Blocked on measurement list.
  • tools/docs-lint/uncited_ceiling.txt: unchanged at 73, verified rather than assumed — every figure added to §5.2 sits inside a paragraph or table that cites a Q-ID, or inside the declared derivation block ADR-106 clause 2 exempts.