Specification §14

Performance and adaptation.

Two adaptations, two mechanisms, and they MUST NOT be conflated. The key words MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY are to be interpreted as described in RFC 2119.

FrogNet Specification — Draft 0.9, revision 2026-08-29. Cite a conformance claim against this revision, not against a section number alone.

1. Two adaptations, two mechanisms

Croakus: §26, §49, §50

FrogNet adapts in two independent ways and an implementation MUST NOT conflate them.

Semantic adaptationMedia adaptation
Applies toEvery exchangeThe media plane only (SotF)
MechanismSend only what changed (§11)Trade fidelity for continuity (§14.3)
TriggerNext-hop RTT, per hop (§11.5)What participants publish about themselves
Saving isStructuralDeliberate degradation

2. What may be claimed for the codec

The saving from BLDC-1 is structural, not statistical: the payload is not sent because both ends already hold it (§11.10). An implementation MUST NOT quote a compression ratio for the codec itself. A ratio describes a workload, not a mechanism, and the same mechanism yields any ratio depending on how much of the traffic is repetition.

Where measured figures are published they MUST carry their workload and their conditions, and the set MUST include a negative control — a workload the mechanism does badly on — or the figures describe only the cases chosen to flatter it.

Latency figures MUST be published alongside compression figures for the same run. Compression that costs round-trip time has moved a cost rather than removed one, and a reader is entitled to see both from one measurement.

3. The media ladder

SotF (§1.3, §15) trades fidelity for continuity as the link tightens. Nine rungs, higher index richer, verified in sotf_ladder.py:

idx  code  name       kind           needs camera
 8   L8    BULLFROG   video+audio    yes
 7   L7    CHORUS     video+audio    yes
 6   L6    ENSEMBLE   video+audio    yes
 5   L5    DUET       video+audio    yes
 4   L4    SOLO       audio          no   (needs mic)
 3   L3    VOICE      audio          no   (needs mic)
 2   L2    WHISPER    text           no
 1   L1    BEACON     text           no
 0   L0    PULSE      presence       no

Audio is mono: AUDIO_CH = 1 at 16 kHz, Opus (fnphone_pa.py:43–44). The docstring at the head of sotf_ladder.py says stereo and is wrong; an implementation MUST NOT reproduce it.

A video rung needs a camera and only a camera [VIDEO_DOES_NOT_NEED_A_MIC_V1]. A machine with a camera and no microphone sends video with no audio bed — a stream with a note on it, not a disqualification — and the receiver is told there is no audio and plays what there is. Gating video rungs on a microphone made a camera-only box compute a text-only ceiling: a working webcam produced text presence.

The two lowest rungs are a floor and MUST NOT be removed from the available set.

Adding a rung MUST extend the top, never renumber. The index travels on the wire, so an existing index must keep its meaning. A peer built before a new top rung existed mislabels it in reporting and decodes the picture correctly, because on receive the index is used for reporting only — the decoder reads geometry from the bitstream.

4. Rate derivation

Rate control is two separable things, and an implementation MUST keep them apart. Conflating them is what makes the purity requirement below look impossible to satisfy alongside the hold-downs in §14.4.3.

StageIsMay consult
Target derivationWhat rate the published state implies, right nowThe published rows, and nothing else
AdoptionWhether and when to move to that targetElapsed time and the currently adopted rate

Target derivation MUST be a pure function of published state: no clock, no instance state, no I/O [ONE_DERIVATION_V1]. Every participant runs the same function over the same rows and lands on the same answer without being told, which is what makes agreement unnecessary rather than merely cheap (§10.6.2). Being pure is also what makes it testable in isolation.

Adoption is a local policy and MAY consult the clock, because it decides only when this participant moves — not what the answer is. Two participants applying different hold-downs converge on the same target at different moments; two participants deriving different targets do not converge at all. An implementation MUST NOT feed an adoption decision, or the currently adopted rate, back into the derivation (§10.6.2): a derivation that consults its own last answer is a controller with memory, and two controllers with memory diverge.

4.1 A rate that failed is not a candidate

[A_RATE_THAT_FAILED_IS_NOT_A_CANDIDATE_V1] A rule that promotes whenever every participant is currently content will promote into a rate already shown not to work, become discontent, demote, become content, and promote again.

The evidence a consumer publishes MUST therefore be more than a present verdict. It is the geometry and frame rate that consumer is actually receiving, so that a report of struggling at a size establishes that the size does not work on that link. From which:

the ceiling is the smallest size at which anybody is struggling;
nothing may promote to or above it.

A derivation with no memory of what failed produced ninety-odd oscillations in five seconds in simulation. That memory MUST live in the published state as a row every participant derives the same ceiling from — not in a controller's private history, because two controllers with memory diverge (§10.6.2).

4.2 Absence is not a measurement

Zero frames arriving means the producer sent nothing. It does not mean this consumer is slow, and reading it as slowness is a ratchet: the producer sheds, the consumer reports worse, the rate drops, the producer sheds more (§10.6.3).

4.3 Hold-downs

Damping lives in adoption (§14.4), never in derivation. Rate changes MUST be damped in both directions, and asymmetrically: a slower hold before climbing than before dropping. Continuity is for the user, who must never be asked to intervene; a picture that thins has not failed. Visible failure is for the operator, who must never be lied to. An implementation that hides a degradation from its log is not transparent, merely quiet.

5. One stream, one rate

The media relay forwards one stream and bounds the call rate to the slowest participant, so a fast participant is held to a slow one for as long as both are in the call (§17.10). This stands until the relay can send a client something other than what arrived, and an implementation MUST NOT claim per-client adaptation while it holds.

6. Publishing figures

Every published figure MUST carry its provenance, and figures of different provenance MUST NOT be presented as though they were the same kind of thing:

KindMeans
Verified in sourceA constant read at its line
Recorded runAn instrumented measurement, with its workload stated
Read off a diagnosticObserved on camera, not from an instrument log — publish a bound, not a point figure
EstimateA did-versus-would comparison against published third-party figures. MUST be labelled as an estimate.

A figure whose provenance cannot be established MUST NOT be published. Where a check reports a rate above 100% or calls a figure sourced that is not, the check is wrong, not the data — fix the check.

7. References

Specification §10.6, §11, §11a, §15, §16, §17. Source: etc/frognet_bundles/communicator/sotf_ladder.py, fnphone_pa.py, comms_control.py, fnav.py. Build manual: Croakus §49, §50, §53. Measured results: the simulator.