The final whole-branch review of Aposemati — the one engineering step the build had left open — ran on 2026-08-19 over the entire branch at once (adc604b → 961d996, 68 commits), with six reviewers in isolated clones, each required to verify by execution.
Verdict: no finding blocks the branch
The one exploit claim that would have blocked it — “a cheating initiator dictates the six digits” — is refuted. Two Criticals are real but unreachable through the Phase 1 shutter path, which is why the live run worked; both sit directly under Phase 2 preview and Phase 4 video. Three handover open questions are now closed with measurements, including POSIX 57.
For Agents
Full report:
/Users/levander/coding/aposemati/docs/superpowers/2026-08-19-whole-branch-review.md(355 lines, the source of truth). Working code artifacts, not applied:docs/superpowers/2026-08-19-review-artifacts/—AuditGapTests.swift(296 lines, 15 kill-verified tests, suite 219 → 232),armD-NetworkTransport.swift(the C3 fix, 1800 pairings zero failures),armD-with-streamlag-NetworkTransport.swift,backpressure.diff. They live outsideSources/andTests/, so the SPM build is unaffected (verified). Nothing in this review has been applied to the branch.
⭐ POSIX 57 is solved — the branch’s one unexplained defect
Root cause found: the QUIC listener delivers two
NWConnections per incoming connection
NetworkTransport.swift:369-372. For each incoming connection the listener hands up two connections:streamIdentifier == 1(the one the listener was handed) andstreamIdentifier == 0(the peer’s real stream).acceptcancels the sibling — and cancelling any one stream resets the whole QUIC connection.400 instrumented pairings: the error is always in
receiveRaw, neversendRaw;connection.state == .readyat the moment Network.framework reports ENOTCONN; every immediate retry succeeded. Healthy pairings logguard-rejected stream=1thenaccept-ok stream=0; failing ones show the rejection and noaccept-okever arrives.
This closes the callout that stood in aposemati-phase0-phase1-build and aposemati as “cause unknown, noted independently by two reviewers”. It was never a flake. The full API-level statement of the trap is in 4. A QUIC listener hands you TWO connections per peer — and cancelling either resets both.
The fix: arm D (reference lifetime)
Park the sibling keyed by its 4-tuple, release it when the peer’s stream settles, reap orphans after handshakeTimeout + pairingTimeout. The reap window is derived from the code’s own timeouts, not from a loopback measurement. 1800 pairings, zero failures of any mode — and the only variant that matched shipped throughput.
Costs, stated rather than hidden: process-global mutable state; the 4-tuple key is a heuristic; the sweep is lazy; and teardown ordering is “could not construct a failure”, which is weaker than proved.
Do NOT ship the naive
return nilSimply declining the sibling instead of cancelling it trades a 1.8% instant-failure rate for a 0.56% rate of thirty-second hangs, plus roughly 2× the throughput cost. A hang is strictly worse than a fast failure that retries — this is the tempting one-liner, and it is the wrong fix.
⭐ The margin finding — the listener tolerates under 250 ms, and nobody chose that
C3. The shipped listener works only if the peer’s stream settles within ~250 ms of the listener being handed its connection. Past that it fails totally, not intermittently. Margin sweep, 8 pairings per cell, delaying the initiator’s first send after readiness:
| delay | shipped | never cancel | cancel @1 s | arm D (reference lifetime) |
|---|---|---|---|---|
| 0 ms | 7/8 | 8/8 | 8/8 | 8/8 |
| 250 ms | 0/8 | 8/8 | 8/8 | 8/8 |
| 900 ms | 0/8 | 8/8 | 8/8 | 8/8 |
| 1100 ms | 0/8 | 8/8 | 0/8 | 8/8 |
| 1500 ms | 0/8 | 8/8 | 0/8 | 8/8 |
The ~1.8% POSIX 57 rate was that cliff grazed by loopback jitter. Not a probabilistic bug — a hard edge, occasionally crossed.
Correction on the record: the margin is thin, not absent
An earlier framing in this review claimed there was no field evidence the listener ever pairs. That is wrong.
progress.md:407records LIVE END-TO-END RUN SUCCEEDED and two 48 MP photos crossing the link. The field run is positive evidence that the gap came in under 250 ms on real AWDL. The failed 10.65 s bound was a different phase entirely.
Loopback baseline for the gap, 299 pairings: min=0 median=0 p90=0 p99=2 max=4 ms. Shipped tolerance is ~60× the worst loopback value — which is exactly why nobody noticed it, and equally why nobody knows what it is on a duty-cycling radio.
The one field instrument worth adding
Log the stream-lag on the next two-device run
At the listener: the interval between the QUIC connection being delivered and the peer’s own stream becoming ready. That is precisely the quantity C3’s tolerance applies to, and it is unmeasured on any real link. Arm D computes it structurally (
parkedAt→ release is a subtraction), andarmD-with-streamlag-NetworkTransport.swiftalready carries the instrument.Log it beside
readinessDuration/pairingDuration. Single-digit ms ⇒ shipped’s margin is fine and arm D is tidiness. Tens or hundreds ⇒ arm D is load-bearing. It costs nothing and converts a judgement call into a number.
⭐ C1 and C2 are mutually exclusive — a decision, not a tuning problem
The constraint, stated exactly
A photo survives a late read iff
budget ≥ the largest frame.FrameCodec.maxFrameSizeis 16 MB, so any budget that saves a 48 MP HEIC is a budget an unconfirmed stranger can also hold — times the number of strangers. The curve is monotone, not tunable.
| config | 15 MB photo, late reader | 4 strangers flooding | 60 MB throughput |
|---|---|---|---|
shipped (aef872b .held gate) | LOST | 156.9 MB | 3796 Mbps |
| budget 4 MB | LOST | 159.0 MB | 3230 Mbps |
| budget 16 MB | survives | 220.4 MB | 3955 Mbps |
| budget 24 MB | survives | 254.8 MB | — |
Recommendation: split them
C2 — adopt the hybrid. Keep aef872b’s gate exactly as built; apply the budget only after frames() is called. This is ~115 lines, not the one line first reported — the literal one-liner (dropping startReading() from begin()) is broken, and it took four attempts to get right. Measured on the working version:
- C2 itself: 354.5 MB → 82.5 MB grown (77% reduction), sender throttled from 40 frames to 4
- pre-confirmation flood, two-process instrument, 64 strangers: shipped 48.9 MB vs hybrid 48.7 / 50.3 MB — matches, because nothing is read before
frames(). Had this not matched, the recommendation would have been wrong. - throughput: no regression (overlapping ranges, paired samples)
- suite: one failure,
aFrameSentBeforeThePeerClosedStillReachesALateConsumer— the test for the guarantee being dropped. The other three failures from the full patch are gone, so the “closed without reading anything it sent” promise (HostAppModel.swift:131,168,174) stays literally true — no security-copy change needed - the 5 ms poll never runs pre-confirmation (0 retry ticks over 10 s of a flooding stranger), so its CPU cost never lands on an idle phone
Budget 24 MB — and the rule is sharper than "size it to
maxFrameSize"The budget must strictly exceed
maxFrameSize.aMultiMegabyteAssetCrossesTheLinkIntactsends exactly 4 MB and fails at a 4 MB budget, because payload + header + assembler backlog reaches the budget before the frame can complete. At a 16 MB budget a legal 16 MB frame would not pass.
C1 — drop the guarantee as unachievable. Delete aFrameSentBeforeThePeerClosedStillReachesALateConsumer, remove the claim from HANDOVER.md:133-136, and document in Transport that the two backends are not interchangeable across a peer close — so Phase 2 and Phase 4 are not written against a promise that does not exist. The justification is measured: the only way to honour it is to remove the gate, and removing the gate costs more than the guarantee is worth.
The hybrid makes C1 worse than shipped, not equal
Shipped still salvages everything under one 256 KB chunk (250/250 small frames; 254 of 1000). The hybrid delivers nothing at all on the late-reader path. The cause is structural: once a budget can pause the reader, “queue is empty” stops meaning “nothing more is coming”, and the pump finishes the stream before
startReading()arms a receive. A correct drain-after-close needs an explicit fourth state — “socket exhausted” as distinct from “peer closed” — attempted three times and not achieved.Since the guarantee is being dropped, nothing depends on that partial chunk — but it makes the non-interchangeability documentation more important, because the divergence from
LoopbackTransportbecomes total rather than partial. This retires the “interchangeable four times over” property recorded in aposemati-build-process-lessons.
A cleaner alternative, unbuilt and unmeasured
Instead of pausing the reader at the budget, tear the link down when the backlog exceeds it, with an explicit error. No pause/resume, no fourth state, no drain-after-close interaction — and it matches a pattern the codebase already has:
FrameAssembleralready does exactly this for a stalled frame (.stalled(bytesHeld:)→tearDown). It trades “a slow consumer throttles the sender” for “a slow consumer loses the link” — worse product behaviour, far smaller and more auditable change. Worth evaluating before committing to the pausing version.
The Criticals in detail
C1 — 86fb3f1 does not deliver its guarantee
NetworkTransport.swift:649-670. The late-reader test sends 10 bytes. The guarantee holds to 256 KB and not one byte further:
250 × 1024 B → 250/250 frames no loss
1000 × 1024 B → 254/1000 LOSS
1 × 262144 B → 0/1 LOSS
1 × 4194304 B → 0/1 LOSS
A 4 MB photo vanishes silently while the receiver’s own carriedTraffic() reports 4,210,729 bytes acknowledged. No error. The cliff is receiveChunk (:153) — lowering it 256 KB → 16 KB moved the cliff exactly. The obvious fix does not work: tested at both sizes, byte-identical results. The bytes are gone once the peer’s close is processed.
C2 — the memory hole was moved past the confirmation gate, not closed
NetworkTransport.swift:189 (arrivals is .unbounded), :649-670 (re-arms regardless of consumer lag). Independently measured by two reviewers with different instruments: 367.6 MB retained / 320 MB offered, slope 1.03 (resident_size); 351.5 MB / 320 MB, slope 1.02 (phys_footprint). No ceiling across 80–480 MB, and it is a retained level, not a peak — 339 MB still held at t=14 s.
The shipped phone code is worse than the transport
CameraSessionbuffers 536.3 MB of frames it then discards —CaptureSession.swift:49-53continues past non-control frames whilehandleCaptureblocks the same loop. Forty ordinary 7 MB photos land anywhere between 86.6 and 308.6 MB for identical work. A consumer with no added delay held 238 MB once the machine got busy.
Threat weighting: both, weighted to robustness. The Mac case needs no attacker at all — handleAsset does a synchronous store.write inside the actor and StoragePreference permits an SMB share; at field rates that is 19.5–42.4 MB accumulating per second the actor is blocked. The phone case is a security bug but inside the trust boundary (the attacker must pass the six-digit comparison), and is materially weaker than the pre-confirmation hole any stranger could reach.
No test anywhere measures memory on a reading transport.
LoopbackTransport is strictly worse — but test-only
LoopbackTransport.swift:60-61 has an unbounded queue and no flow control behind it, where NetworkTransport is at least bounded by QUIC’s 32 MB window. Measured with distinct, fully touched buffers: 320.8 MB accumulated for 320 MB offered, 1:1, all 40 sends completing instantly — no back-pressure of any kind.
Deprioritised because LoopbackTransport has zero non-test uses (grep across Sources and Apps returns only its own definition). It cannot jetsam a user’s phone. Adding a buffering policy there is a real fix facing the same drop-versus-block dilemma as C2, not a cosmetic one.
Fixes, ranked
- Arm D for C3 — the reference-lifetime sibling release. Artifact ready.
- Land
AuditGapTests.swift— 296 lines, 15 tests, each verified to kill its intended mutant; suite 219 → 232. - The hybrid for C2 — ~115 lines, budget 24 MB. Or evaluate the tear-down alternative first.
- Drop the C1 guarantee and document non-interchangeability.
- Log the stream-lag beside
readinessDuration/pairingDuration. - Re-derive the
landedbound inaStrangerNobodyConfirmed…from the offered total (offered/2= 64 MiB), switchresidentBytes()tophys_footprint, mark it.serialized. AssetStore.swift:103— one line, reusingcontainersat:10:.filter { !$0.hasDirectoryPath && Self.containers.contains($0.pathExtension.lowercased()) }.- Delete
WireMessage.status(DeviceStatus)or implement and pin it — zero construction sites. HostAppModel.swift:187—runModal()pumps the main queue; re-checkcanChangeStorageRoot.StoragePreference.swift:29-32—restore()permanently deletes the bookmark whentake()fails, losing a folder on a drive that was unmounted at launch.
Test suite: about 70% real
119 mutations, 35 survived, ~71% score. The missing 30% is not randomly distributed — it clusters on tuned constants and on the one security mechanism the project’s own documents call load-bearing.
The suite is not deterministically green even unmutated
3 of 14 pristine runs failed under load (21%). Both causes are real defects, not bad tests: POSIX 57 in pairing (C3), and
landed → 29,054,366against a 24 MiB bound — the Critical fix’s own memory assertion exceeded by 21%. CI on this repo currently cannot distinguish a regression from noise.
Survivors that matter most:
| site | mutation | verdict |
|---|---|---|
NetworkTransport.swift:450 | delete the commit-reveal verification guard | SURVIVED, kill-proven |
NetworkTransport.swift:445 | responder reveals nonce before reading commitment | SURVIVED |
PeerDiscovery.swift:27 | peerToPeerInterface accepts any interface | SURVIVED |
PeerDiscovery.swift:85 | includePeerToPeer → false | SURVIVED |
NetworkTransport.swift:155 | windowBytes 32 MB → 512 MB | SURVIVED |
LinkReport.swift:11 | allowlist → denylist (utun4 becomes “direct”) | SURVIVED |
FrameCodec.swift:42,51,53 | M2 / M3 / M4 | all SURVIVED |
CaptureSession.swift:27 | AssetFrame.decode >= → > | SURVIVED |
Nine tuned constants are pinned only by tests that read them
announcementLimit,nameAttemptLimit,marginalFloorMbps,maxFrameSize,windowBytes,concurrentStreams,receiveChunk,pairingTimeout,clockSkewAllowance. Several are the output of field measurement. The suite cannot disagree with any of them.
Six vacuous tests, each proved by mutation. Coverage: Probe.swift 0.00% (0/666 lines); Apps/ has no test target at all — 1,738 lines covering pairing confirmation, the refusal blacklist, the capture pipeline and storage preference.
Genuinely strong, and worth saying
PairingSecretis the best-tested code here — golden HKDF vectors killed 13 of 17 mutations and all four survivors are provably equivalent. Theaef872bgate could not be weakened five different ways without a test noticing.handshake >= 30spins thea94717dfield fix.
Deferred minors — 29 triaged (the handover says 27)
Six promoted to Important: the unguarded tunnel classification; three surviving FrameCodec mutants; WireMessage.status as dead wire surface about to ossify; AssetFrame.decode with zero direct tests; allAssets() returning symlinks and documents; Apps/ with no test target.
Seven items disproved by measurement — strike them:
AssetFrame.decodedoes not copy (+0.0 MB at 7 MB and at 64 MB)captureFailuresis consumed (HostAppModel.swift:313) and cheap (5,000 = 2.5 MB, because it carriesbyteCountnotData) — a strength, not a defect- the 1-second-sampler exposure is dead (1056 MiB → 16.1 MB), superseded by
aef872b 402erefuse-path continuation leak — stale at HEAD, confirmed twiceProbe’s uncancellable continuations — safe; never awaited directly.HANDOVER.md:172-173states the wrong reason: what matters is not “has a handler” but “is the continuation ever awaited directly” (see swift-uncancellable-continuation-trap)DeviceCamera.captureStill— safe, already haswithTaskCancellationHandlerkey: Datatiming-unsafe==— stale, no such field exists
The ledger’s marginalFloorMbps tolerance of (4.0, 8.0] is wrong: swept and bisected it is (4.473924266666667, 8.0]. The binding constraint is a 32 MiB/60 s sample in the Probe test target, not in ThroughputSampleTests.swift.
Documentation drift found
The record disagrees with itself in four places
HANDOVER.mdsays 66 commits;git rev-list --countsays 68HANDOVER.mdsays 27 deferred minors; the ledger has 29 (~60 findings)- the ledger carries no rulings for the last four commits (
ed436a0,86fb3f1,3dab496,961d996)progress.md:410calls the failed 10 s bound the pairing bound;HANDOVER.md:131calls it the readiness bound. The record is ambiguous and it matters, because C3’s margin concerns a third quantity — the listener-to-peer-stream gap.
Accepted gaps in this review
Sources/AposematiProbe/ (1,006 lines) got a grep for unbounded collections, not a bounds review — it is in neither app’s dependency path. The 5 ms poll’s CPU/battery cost was measured for throughput only, on a Mac rather than a phone. Apps/ code was reasoned about but not executed, since neither app target is in the SPM package.
Still needs hardware
- Re-test the handshake at the raised 30 s bound.
- ⭐ Walk to the spot where Camo dies, with both apps — still the last untested product claim.
- Press “They do not match”, then reconnect.
- New: log the stream-lag on that same run. It costs nothing and converts C3 from a judgement call into a number.
What Phase 2 did with this review’s findings — 2026-08-25
- Arm D and the C2 budget survived intact. Phase 2 modifies
NetworkTransport.swiftheavily and both were verified byte-identical apart from signature changes, at every task boundary. streamLagDurationis now instrumented, so item 4 above costs nothing on the next two-device run. Loopback baseline: median 0 ms, p99 1–4 ms, max 2–11 ms. It is the reading that decides whether arm D is load-bearing or tidiness.- The C2 memory shape recurred one layer up, and was bounded rather than argued. The preview reassembler’s per-peer ceiling is exactly 18.0 MiB (
2 × inFlightLimit 12 × frameByteBudget 512 KiB), no collection grows with peer input, and the bound test still fails against an unbounded implementation (20k sequences: peak 20,000 > 12, 23.7 MB > ceiling). - ⚠️ But the equivalent question is still open on datagrams, and it is worse there. QUIC DATAGRAM frames are not flow-controlled — nothing pushes back the way the 32 MB stream window does — and no RSS bound on parked, unconfirmed preview flows could be established in-process: four strangers pushing 73 MB grew the footprint 93 MB with no plateau, and it did not return after cancelling them. This is now the highest-value memory question for the hardware rig.
Apps/being outside the SPM package cost five commits of a silently broken Mac app in Phase 2 — the “reasoned about but not executed” gap in Accepted gaps above, arriving as a real defect. Bothxcodebuildinvocations are now a standing ~90 s gate at every task boundary.
Related
- aposemati-phase0-phase1-build — the build this reviewed
- 4. A QUIC listener hands you TWO connections per peer — and cancelling either resets both — the API-level form of the POSIX 57 root cause
- Mutation testing has two opposite failure modes — the method lessons this review produced
- aposemati-pairing-security-model — the refuted exploit and the untested commitment guard
- aposemati-phase0-field-measurements — the field numbers the margin finding is set against
- aposemati — project overview