For Agents

Reverse-chronological session log. Newest entries at top, grouped by date (## YYYY-MM-DD). Each bullet: one piece of work, short summary, wikilinks to docs touched. Updated by obsidian-documenter on every project doc write. Read by historian at bootstrap.

2026-08-26

  • 🔬 ⭐ LOOPBACK CANNOT REPRODUCE “A SEND THAT SUCCEEDS INTO NOTHING” — this is why 546 tests, eight task reviews and a whole-branch review all missed a defect a plugged-in phone found in ten minutes. Over awdl0 a QUIC datagram send into a dead peer SUCCEEDS and the sender learns nothing; over loopback the identical send fails immediately with POSIXErrorCode(rawValue: 57): Socket is not connected. The harness takes the error path the field never takes, so a test written for this defect passes against unmodified code — loopback hands the sender the very error the radio withholds. Stated plainly: the harness can only reproduce the QUIET form (peer leaves, nothing being sent); the LIVE-SENDER form — 30 fps of successful sends into a dead peer — is not reproducible on loopback at all. Anything relying on send failure to detect peer death is untestable in-harness and must be reasoned about or found on hardware — aposemati-loopback-blind-spot, aposemati-build-process-lessons, aposemati-network-framework-gotchas.
  • 🐛 The permanently frozen viewfinder is ROOT-CAUSED AND FIXED — and the cause is narrower than the previous handover claimed. NetworkPreviewChannel never installed a stateUpdateHandler, leaving exactly two routes to shutDown(): a receive error in receiveNext() (reachable only from frames()) and a send error in transmit() (which QUIC datagrams never report over the radio). The phone only ever SENDS and never calls frames(), so on the phone the channel had no path to shutDown() whatsoever — the Mac survived only incidentally, because it reads. The Mac-side log from 6607614 shows it was correct throughout: a clean 32-second loop, forever (opening a preview flow, epoch 1preview flow openframe stream finished after 0 frames at +30 s → retrying in 2.0 seconds). Both ends individually believed they were healthy; the phone’s readout showed 30 fps throughoutaposemati-loopback-blind-spot, aposemati-crop-and-framing.
  • ⚠️ ⭐ The lesson is the asymmetry: a correct pattern existed in the same file and was not applied to the sibling connection. NetworkTransport.begin() had done the right thing for the CONTROL connection all along — a [weak self] stateUpdateHandler tearing down on .failed/.cancelled. The preview flow, added later beside it, was simply never given the equivalent. Grep for this shape on any codebase that grew a second connection beside a first oneaposemati-network-framework-gotchas, aposemati-loopback-blind-spot.
  • 🔧 The fix is two changes, both needed — a [weak self] stateUpdateHandler in NetworkPreviewChannel.init calling shutDown() on .failed/.cancelled (with shutDown() nilling the handler before the cancel() that re-enters it), and the retry loop the Mac already had in HostAppModel.runPreview (while !Task.isCancelled, 2 s delay) added to CameraAppModel.streamPreview. Change 1 is one guard in the shared class both ends route through, so it fixes the Mac too. It also settles the transport-cache question without a separate fix: discardThePreviewFlowIfItDied() clears NetworkTransport.previewChannel only when channel.isShutDown, so before the fix the cache handed back the DEAD channelaposemati-loopback-blind-spot.
  • 🧪 Mutation evidence, run BY HAND rather than trusted from a report. Reverting only the source fix while keeping the tests: send-only flow after its peer went away: shut down falseExpectation failed: (noticed → nil) == true, and send-only flow reopening: peer offered a replacement, it handed back the dead oneExpectation failed: (reopened) !== (accepted). Both fail after ~15 s of timeout; with the fix restored both pass in 0.073 s. The second line is the independent confirmation that the transport cache was handing back a dead channel. It was run by hand because this project has a precedent — commit 371b32a, where a fix was later disproved because its test passed identically with and without the change — aposemati-build-process-lessons, aposemati-loopback-blind-spot.
  • 📝 A stale cross-reference has been propagating, and it was being used to block the correct fix. HANDOVER.md cites “spec §4.3” three times for the rule that silence is peer-controlled and must not be watched. That section does not existdocs/superpowers/specs/2026-08-25-preview-crop-design.md has 12 sections and none is 4.3 (the string does not occur in the file). The rule lives at docs/superpowers/specs/2026-08-18-aposemati-design.md:502. Its real scope is narrower: it binds the receiver-side reassembler stall watchdog, because keying that on silence reopens an on-demand backward watermark move that took four rounds to close. It says nothing about connection state — a stateUpdateHandler is a local transport fact, not silence, which is why the fix is compliant. A rule that travels by citation drifts in scopeaposemati-peer-restart-inference-boundary, aposemati-crop-and-framing.
  • 🎭 sendCostIsMeasuredAtBothTheLoopbackAndTheRadioSizedDatagram has a SECOND, scarier failure signature that was not written down. Beyond the documented hard 50 ms p90 flake under load, it also fails with handshakeTimedOut after 34 seconds — a link that never established at all, which reads like a transport regression rather than a timing flake. Run alone on the same machine it passed in 6.1 s, and a second full-suite run passed all 548. Both signatures are now recorded so the next person does not chase the wrong thing — documenting only the benign mode teaches the next reader to dismiss the alarming one — aposemati-build-process-lessons, aposemati-phase2-live-preview.
  • 🚶 Open, needs hardware:whether awdl0 delivers .failed at all, and how fast — on loopback the peer’s cancel() propagated in ~76 ms, but over the radio the QUIC CONNECTION_CLOSE may be lost, in which case the phone falls back to the 30 s idle timeout and recovery becomes ~32 s instead of ~2 s (still recovery, where today there is none). Also: CameraAppModel compiles into no test target, so the new retry loop is untested by construction — same as the Mac’s; the missing-shutter defect (one press in seven produced no file) is untouched and still unresolved; and pre-existing but now more visible inside a retry loop, a preview task cancelled while blocked in PreviewRendezvous.claim swallows cancellation via try?, so exit can take up to the 30 s handshake timeoutaposemati-loopback-blind-spot, aposemati-phase2-live-preview.

2026-08-25

  • 🟢 ⭐ PHASE 2 IS COMPLETE — live preview works, at c29f84d. 33 commits, 418 tests, both apps building, ten tasks, 23 rulings, ~15 deferred minors. JPEG frames from the phone through a 12-rung ladder (3 scales × 4 qualities), fragmented into QUIC datagrams, reassembled against a 200 ms deadline on the Mac, decoded off the main actor and displayed. ⏸️ The verdict is deliberately blank pending hardwaredocs/superpowers/phase2-results.md is a measurement gate, not a conclusion, because the two deciding readings need two devices and no device build was permitted — aposemati-phase2-live-preview.
  • One reading on the rig decides two verdict rows at once. PreviewFrameSize.fitting(withinLongEdge:) returns the source unchanged when it already fits, so a preview-sized buffer means rungs 0–3 do no downscaling and nothing averages away the high-frequency content JPEG pays for. 4032×2268 → 283,729 B / 240 fragments / 69.0 Mbps (passes); 1920×1080 → 482,530 B / 408 fragments / 117.4 Mbps (fails). Read CVPixelBufferGetWidth/Height in PreviewEncoder.consume firstaposemati-phase2-live-preview.
  • A lost fragment costs ONE frame, never the stream — measured, not asserted. Over two real QUIC ends at a radio-sized 1200 B datagram (408 fragments/frame): p=0 → 100/100; p=0.05% → 85/100 (projected 81.5%); p=0.20% → 47/100 (projected 44.2%). 77 lost fragments destroyed 49 frames — 1.57 per destroyed frame against 1.46 predicted. No cascade, fragmentsOutOfReach 0 throughout, 30/30 clean after the lossy stretch. So (1−p)^n is the measured behaviour and the loss table projects measurement. A quarter of one tenth of one percent of loss costs a tenth of the viewfinderaposemati-phase2-live-preview.
  • 📡 QUIC datagrams ARE reachable from Swift — with three traps. Read usableDatagramFrameSize off the datagram flow, not the parent connection (the parent returns 0); an oversize datagram is accepted with NO error and silently dropped (usable + 1 never arrives, nothing to log unless you clamp and count yourself); and the two ends disagree — sender 9156, receiver 9164 — so the clamp must use the local read — aposemati-network-framework-gotchas.
  • NWConnectionGroup + NWMultiplexGroup did not work. Every extracted connection failed POSIXErrorCode 50: Network is down across many variants, and the group reported .ready while its path was down. Two independent agents failed at it, the reviewer differently and earlier — which establishes “hard, possibly not viable”, never “impossible”, and the spec carries that hedge. Preview instead uses a second NWConnection to a second NWListener on its own port. Also trap-list material: setting both newConnectionGroupHandler and newConnectionHandler on one QUIC NWListener fails POSIXErrorCode 22 — either alone is fine — aposemati-network-framework-gotchas.
  • ⚠️ “Connection A datagrams” is not what shipped, and the durable spec has not caught up. The §4.1 claim is satisfied better than described (preview isolated from stills, not sharing a window with control) — but Phase 3+ and the H.264 planner will open a spec describing an architecture the code does not implement. .interactiveVideo is set; nothing sets .background anywhereaposemati-phase2-live-preview, aposemati.
  • The architectural claim was measured and holds — one 7 MB still fired mid-stream costs ONE preview frame, ~1.7 ms on a 33 ms budget, 90/90 delivered. ⚠️ But ~40% of the interference under saturation is our OWN single serial DispatchQueue, which both connections start on: giving preview its own queue moves end-to-end p90 9.51 → 5.81 ms. That is the coupling the two-connection split exists to remove, reintroduced one layer below it. Documented, deliberately not fixed — unreachable at 156–339 Mbps, and the file carries two verified Critical fixes — aposemati-phase2-live-preview.
  • send() costs one continuation PER FRAGMENT, so it scales with fragment count3.3–4× slower at the radio’s 408 fragments than at loopback’s 53, +9.4 to +9.5 ms p90 for the sensor-sized frame and +11.6 to +13.9 ms for the preview-sized one. The transport is not “under 3 ms” at radio-sized datagrams; it is ~11 and ~16 ms, and encode+decode falls from 75–90% of the path to about half. This is the first latency argument for H.264, alongside bandwidth and loss — three returns on one change, invisible while everything was measured at 53 fragments — aposemati-phase2-live-preview.
  • 💸 NEW, on nobody’s list: bandwidth may bind before loss does. The no-downscale case asks 117.4 Mbps sustained = 75% of the 156.3 Mbps Phase 0 measured at the Camo-drop spot, before stills, control or ACKs. §7.1 as literally written is still satisfied (a 5 MB still takes 1.03 s against a 2 s target) — but margin falls from 7.7× to 1.9×. Derived bar: preview ≤ 50% of measured sustained throughput = 78 Mbps, which the downscaled case passes and the no-downscale case fails. The spec gained a note saying it has no row for this and should — aposemati-phase2-live-preview.
  • 🔒 ⭐ Seven rebuilds of one restart detector, and the answer was to delete the mechanism. Each mechanism satisfied its requirement and each was exploitable, because at admission an honest one-fragment frame at newest+1 and a spoof at newest+1 are indistinguishable — round 3 even let an attacker take over the stream (honest 5/200, attacker 195/200). Shipped rule: peer data can never move the watermark backwards; reset() is the only backward move and the session layer calls it. Verified structurally — three writes to newestFinished, the peer-reachable one forward-only. One hard residual: a peer restarting cyclically inside the 12-frame tolerance freezes delivery invisibly, and keying the watchdog on silence would catch it and must not be done, because silence is peer-controlledaposemati-peer-restart-inference-boundary.
  • FIVE measurements in this phase answered a different question than the one asked — all carefully done, all caught by review rather than by a failing test. Subtracting p90-of-spacing from p90-of-spacing cannot see added latency at all (spacing cancels a constant offset by construction; measured properly the same run read 0.84 → 9.51 ms, not 0.24 ms); a JPEG size table taken only on the favourable side of a 2.1× downscale; a loss denominator omitting fragments that arrived in frames later discarded (2.03× overestimate, which would have forced a false H.264 verdict and a false burstiness diagnosis from the same error); a burstiness rule pointing the wrong way (bursty loss concentrates drops in fewer frames, so MORE frames survive — the model is a lower bound, the table is pessimistic); and a copy-probe using distinct buffers, which cannot discriminate on the receiving side (reuse ONE buffer, and run each variant in its own process — malloc reuses the freed arena). A number is only as good as the question it was taken underaposemati-build-process-lessons.
  • The recurring defect shape: two tasks each correct in scope, jointly leaving a hole neither owned. A dead preview flow was permanently stuck because Task 7 deferred the sender half as “the receiving half is Task 8’s” — and Task 8’s retry loop was the receiving half. Same shape produced a frameByteBudget default that rejected the very frames the phase existed to carry. Found only by a whole-phase review reading the shipped state — aposemati-build-process-lessons.
  • A test double that collapses two roles hides defects that survive full review with mutation evidence. LoopbackPreviewChannel makes sender and receiver the same object, so a receive-side watchdog wired on the sending end passed four mutation tests and could never fire on real hardware (sender delivered=0/outOfReach=0 forever; watchdog fired 0 over 25 s of a fully frozen receiver). Rule: evidence about a two-role protocol must come from two distinct objects, stated as an acceptance criterion — aposemati-build-process-lessons.
  • 🔧 Apps/ is not a Package.swift target — the Mac app was silently broken for FIVE commits, through two full-suite runs and a warning-free swift build. Both xcodebuild invocations with CODE_SIGNING_ALLOWED=NO catch it in ~90 s and are now a standing gate at every task boundaryaposemati-build-install-run, aposemati-build-process-lessons.
  • Bounding a review explicitly beats an open one. “Six-cycle only, no fuzz, no million-frame sweeps, say what you skipped” turned a multi-hour verification into minutes without losing a discriminating check. Carried into every later task — aposemati-build-process-lessons.
  • 🚶 Hardware checklist, ranked — and item 1 is not preview. AVCaptureVideoDataOutput now joins the capture session unconditionally, so 48 MP + ZSL stills must be re-verified with it attached before anything else, because stills are Phase 1’s shipped feature. Then: the delivered buffer dimension, usableDatagramFrameSize on awdl0, ⚠️ an RSS bound on parked unconfirmed preview flows (four strangers pushing 73 MB grew the footprint 93 MB with no plateau — and QUIC DATAGRAM frames are not flow-controlled, so nothing pushes back the way the 32 MB stream window does), streamLagDuration on the radio, the viewfinder over awdl0, and rotation — aposemati-phase2-live-preview, aposemati-whole-branch-review.

2026-08-19

  • 🏁 ⭐ THE FINAL WHOLE-BRANCH REVIEW RAN — verdict: no finding blocks the branch. Six reviewers in isolated clones, all 68 commits at once (adc604b961d996), each required to verify by execution. The exploit claim that would have blocked it is refuted; two of the three Criticals (C1, C2) are real but unreachable through the Phase 1 shutter path and sit under Phase 2 preview and Phase 4 video, while the third is the POSIX 57 root cause below. Three handover open questions closed with measurements. Nothing applied yet; working artifacts parked in docs/superpowers/2026-08-19-review-artifacts/aposemati-whole-branch-review.
  • POSIX 57 is SOLVED — it was never a flake. NetworkTransport.swift:369-372: the QUIC listener delivers two NWConnections per incoming connection (streamIdentifier == 1 is the one the listener was handed, 0 is the peer’s real stream); accept cancels the sibling, and cancelling any one stream resets the whole QUIC connection. 400 instrumented pairings: always receiveRaw, state == .ready at ENOTCONN, every retry succeeded. Fix is arm D — park the sibling by 4-tuple, release on the peer’s stream, reap after handshakeTimeout + pairingTimeout: 1800 pairings, zero failures. Do not use the naive return nil — it trades 1.8% instant failures for 0.56% thirty-second hangsaposemati-network-framework-gotchas, aposemati-whole-branch-review.
  • The listener’s tolerance is under 250 ms and nobody chose it. Past ~250 ms between the connection being delivered and the peer’s stream settling, the shipped listener fails 8/8 — totally, not intermittently. The ~1.8% POSIX 57 was that cliff grazed by jitter. The margin is thin, not absent: the 2026-08-18 live run stands as evidence the gap came in under 250 ms on real AWDL. Loopback baseline for the gap: median 0 ms, max 4 ms over 299 pairings — ~60× inside tolerance, which is why nobody noticed and why nobody knows what it is on a duty-cycling radio. Log the stream-lag on the next two-device runaposemati-whole-branch-review.
  • ⚖️ C1 and C2 are mutually exclusive — a decision, not a tuning problem. A photo survives a late read iff budget ≥ maxFrameSize, and maxFrameSize is 16 MB — so any budget that saves a 48 MP HEIC is one an unconfirmed stranger can hold, times the number of strangers. Recommendation: adopt the hybrid for C2 (~115 lines, not the one line first reported; budget 24 MB, which must strictly exceed maxFrameSize) and drop the C1 guarantee as unachievable, deleting its test and documenting that the two transport backends are not interchangeable across a peer close — aposemati-whole-branch-review, aposemati-phase0-phase1-build.
  • Method: mutation testing has two opposite failure modes, and neither was guarded in any of the 14 task reviews. False SURVIVED — the mutant was reverted or never compiled in (one agent’s directory-wide git checkout -- Sources Tests silently reverted another’s in-flight mutations; SwiftPM has a same-second mtime race that reuses the previous binary). False CAUGHT21% of pristine runs fail under load. Fix: a canary test that passes only if the mutant is live, plus source/binary hashing. Also: concurrent agents must not share a git checkout — give each its own git clone --localaposemati-build-process-lessons.
  • “Verify by execution” is necessary but NOT sufficient — the instrument must be checked against an independent one. Both wrong claims in this review were measurement artifacts, not misreadings: a flood figure that counted the attackers’ memory as the victim’s, and a harness reusing one Data buffer that copy-on-write shared across all 40 frames (Data(count:) is also invalid — untouched zero pages are never resident). True figure 320.8 MB, not 8.2. Both were caught by another reviewer’s number disagreeing, never by re-reading the codeaposemati-build-process-lessons.
  • 🔒 The “cheating initiator dictates the six digits” exploit is REFUTED — the commit-reveal binds correctly; the grind was offline computation never pushed through the shipped responder, which returns pairingCommitmentBroken and admits no transport. But pairingCommitmentBroken has ZERO test coverage, and the existing MITM test does not cover it — its attacker is an honest relay, so it exercises nonce mixing, not the commitment. Deleting the verification guard at NetworkTransport.swift:450 SURVIVES mutation — aposemati-pairing-security-model.
  • 📊 Test suite is about 70% real: 119 mutations, 35 survived (~71%). The missing 30% is not random — it clusters on nine tuned constants pinned only by tests that read them (several of them outputs of field measurement) and on the commit-reveal. Probe.swift is 0.00% covered (0/666 lines) and Apps/ has no test target at all (1,738 lines). Genuinely strong: PairingSecret’s golden HKDF vectors killed 13 of 17 mutations, all four survivors provably equivalent — aposemati-whole-branch-review.
  • 📝 Doc drift found: HANDOVER.md says 66 commits (actual 68) and 27 deferred minors (actual 29); the ledger has no rulings for the last four commits; and progress.md:410 calls the failed 10 s bound the pairing bound while HANDOVER.md:131 calls it readiness — an ambiguity that matters because the new margin finding concerns a third quantity — aposemati-whole-branch-review.
  • 🏁 Session closed: 68 commits, 219 tests, zero warnings, clean tree, both apps installed and running on hardware. The review loop is closed — every finding from every round is fixed or recorded. Four fixes landed after the earlier documentation pass: aef872b (the transport now reads nothing from a peer until the app asks for its frames — the Critical; 34.7 MB peak against four flooding strangers vs 486 MB with the fix reverted, measured in a separate victim process with a control), ed436a0 (readiness bound 10 s → 30 s and instrumented), 86fb3f1 (deferred reading was silently dropping; a late reader can again collect what arrived before the peer left), e1cd333 (the refusal-recovery button no longer loops forever) — aposemati-phase0-phase1-build.
  • Process lesson that cost two fix rounds: trace a symptom to its single throw site and measure there. The bound that failed in the field was raised only after tracing the exact string the owner saw — “the phone did not finish the handshake in time” — to handshakeTimedOut, thrown from one site, awaitReady(_:timeout:), and measuring it still failing at 10.65 s. An earlier round had raised a different bound on a premise that contradicted its own error table, and reading the diff would have passed it. Standing rule: treat a confident report as a claim to verify, not as evidenceaposemati-build-process-lessons.
  • 🔧 Build/install gotchas that cost real time: the iOS product is Aposemati.app, not AposematiCamera.app (scheme name ≠ product name), and the xcodebuild -destination id= is not the devicectl identifier — the same phone has two UUIDs (00008140-… for building, 03750A7F-… for installing) — aposemati-build-install-run.
  • 🔜 The one remaining step, not started: the final whole-branch review over all 68 commits at once, triaging the 27 deferred minors in the ledger. Every review so far was scoped to one task’s diff, so cross-task inconsistencies are structurally invisible to them. Run it in a fresh session with HANDOVER.md + the ledger as input. Also open: an intermittent POSIX 57 transient during pairing, reproducible under a flood harness on first run, cause unknown, noted by two independent reviewers. Untested on hardware: the 30 s handshake, deliberately refusing and reconnecting, and ⭐ the walk to the spot where Camo drops, with the apps — the last untested product claim and the entire reason the project exists.
  • 🟢 IT WORKS — Phase 0 + Phase 1 built and proven on hardware. On 2026-08-18 23:47 the first live two-device run captured two photos at 8064x6048 (48.8 MP) from an iPhone 16 Pro to a Mac over a link reported as awdl0 — direct, into ~/Pictures/Aposemati/2026-08-18/~17x the pixels Continuity Camera can deliver. 61 commits, 215 tests, zero warnings, branch phase0-phase1-link-and-shutter, unmerged and unpushed. Still in flight: two unverified fix rounds and the whole-branch review — aposemati-phase0-phase1-build.
  • Phase 0 field measurements, the first throughput figures that exist for this link anywhere: 339 Mbps beside the router, 156 Mbps at the exact spot where Camo drops, both awdl0 — direct, rtt 37 ms unchanged at both, 0.011% retransmission at range. Range costs ~54% of bandwidth and zero latency. Also caught a probe bug that was timing the close handshake, pinning every reading near the 25 Mbps pass threshold by coincidence — aposemati-phase0-field-measurements.
  • Short-authentication-string comparison alone is NOT MITM-resistant. A reviewer ground two colliding certificates (200 identities in 0.0836 s; collision at 1019 pairs) and ran a working machine-in-the-middle that relayed a real photo in 0.77 seconds — because the attacker chooses both certificates and nothing commits either side before seeing the peer’s. Fixed with a commit-reveal nonce exchange; identity is in-memory per launch, never in the keychain. Also: 252 MB buffered by an unconfirmed peer, and the UI-as-security-boundary findings — aposemati-pairing-security-model.
  • Network.framework corrections that overturn earlier notes: NWBrowser.Result.interfaces is the only public source of an AWDL NWInterface (NWPathMonitor never reports awdl0); never filter by interface typeawdl0 reports type == .wifi; QUIC does not support TLS-PSK (-9858 in six variants, works over TCP) which moved the pairing model mid-build; a QUIC listener reaches .ready with a broken security config so .ready proves nothing — aposemati-network-framework-gotchas, aposemati-transport-findings.
  • The uncancellable-continuation trap — a bare withCheckedContinuation raced inside a withTaskGroup cannot be cancelled and withTaskGroup awaits every child. Latent for the whole build because the QUIC idle timer had been accidentally rescuing it; adding a keepalive removed the rescue and turned it into a permanent hang that swallows even test-level timeouts. Appeared three times in one project — swift-uncancellable-continuation-trap.
  • Character.isNumber accepts Arabic-Indic, Devanagari and fullwidth digits — hit three separate times in one codebase (interface-name classification, pairing-code validation, file-extension sanitising). $0.isASCII && is load-bearing; an allowlist Set<String> closes it structurally — swift-character-isnumber-digit-trap.
  • Signing/sandbox gotchas: a fresh Apple Development cert is issued by WWDR G3 and a missing intermediate makes find-identity report 0 valid identities while codesign fails errSecInternalComponent; Xcode must register a new device once before -allowProvisioningUpdates works; a sandboxed app needs a security-scoped bookmark, not a stored path; rtk strips comment lines from file dumps and hid a // swift-tools-version: directive — aposemati-apple-signing-gotchas.
  • Process lessons worth reusing on any agent build: verify by execution, never by reading; passing tests are not evidence and mutation testing is required wherever a fix claims to close a hole; multiple reviews found a test named for a failure did not catch it. The agent that committed incrementally lost nothing to a machine sleep — the one that did not lost an entire fix round — aposemati-build-process-lessons.
  • Corrected the device capability matrix: 48 MP starts at the iPhone 14 Pro; iPhone 12/13/14 non-Pro and every iPad are 12 MP. The spec originally said 48 MP everywhere; nothing may hardcode a resolution — aposemati-apple-api-measurements.

2026-08-18

  • 🔴 INVALIDATED the Wi-Fi Aware architecture. WiFiAware.framework is @available(macOS, unavailable) — no native, no Catalyst, no -macabi swiftinterface, absent from System/iOSSupport, and Xcode’s WIFI_AWARE record lists iOS only. Also debunked “the EU killed AWDL”: awdl0/llw0 are live on macOS 26.5.1 (11.5 GB inbound on llw0). New primary candidate is NWParameters.includePeerToPeeraposemati-transport-findings.
  • Project renamed and re-scoped as Aposemati (aposematism = warning coloration, the opposite of camouflage — a pun on Camo). Open source, free, Apple-only. v1 = 48MP stills + on-device video recording; iPhone shoots, Mac is viewfinder/shutter/gallery. Four QUIC channels; “never stream the thing you are keeping”; “framing is metadata, never pixels”. Virtual camera deferred to phase 7 — aposemati.
  • Measured Apple API facts on macOS 26.5 / Xcode 26.3: Continuity Camera tops out at 1920x1440 8-bit 420v (identical on iPhone 16 Pro and 13 Pro Max), VideoToolbox low-latency takes H.264 4K30 from 140.1 ms → 16.4 ms, AV1 encode does not exist (-12908), a full Core Image colour chain costs 0.506 ms at 1080p, CIPersonSegmentation accurate is a fixed ~30 ms → hard 33 fps ceiling. Plus CMIO gotchas (XPC does not work — use a sink stream) and Developer ID + notarization as the distribution path — aposemati-apple-api-measurements.
  • Binary-level teardown of Camo Studio 2.8.2: $9.99/mo (doubled since 2023), free tier capped at 720p/30, and the key weakness — the exclusion matrix (no 4K + background blur, no 60 fps + Portrait). No NDI, no curves/HSL/scopes, no encoder control, AR effects require internet. Undocumented AiBackgroundStyles.json with 16 generative background presets — camo-studio-teardown.
  • Annotated diy-continuity-camera-clone-research as superseded with a top-of-note [!danger] callout and an inline AWDL correction. Kept in full as a record — its transport-is-the-product thesis was correct.
  • Homelab receiver closed out: telep-mainframe’s AX210 has no antenna on its u.FL connector and cannot be a wireless peer at all, independent of the Linux NAN driver question — aposemati.