For Agents

Living index of themes for this project. Each H2 is a topic; bullets are wikilinks to related notes. Updated by obsidian-documenter when documenting work. Read by historian at bootstrap. Topics kept alphabetical.

The project is now called Aposemati — the folder name phone-camera-clone predates the rename. Start at aposemati.

Architecture

  • aposematicurrent architecture: four QUIC channels (preview/control/stills/video), “never stream the thing you are keeping”, “framing is metadata, never pixels”, three modules, 8 phases.
  • aposemati-phase2-live-preview — ⚠️ the shipped preview architecture is not the specified one: NWConnectionGroup/NWMultiplexGroup did not work, so preview is a third NWConnection to a second NWListener on its own port — better isolation than the spec describes, but the durable spec still records the plan. Also: both connections share one serial DispatchQueue, worth ~40% of measured interference.
  • aposemati-phase0-phase1-buildthe architecture as actually built and running: two QUIC connections (not one — shared congestion controller), 16-byte UUID prefix on asset frames replacing positional pairing, 16 MiB decode-side frame cap.
  • diy-continuity-camera-clone-research — ⚠️ superseded Wi-Fi Aware pipeline (AVFoundation → VideoToolbox → Wi-Fi Aware data path → decode → virtual cam / record). Kept as a record.

Build & Tooling

  • aposemati-build-install-run — 🔴 swift build/swift test cannot see Apps/ at all — neither app target is in Package.swift, and the Mac app was silently broken for five commits. Both xcodebuild gate commands (CODE_SIGNING_ALLOWED=NO, ~90 s) are in the note.
  • aposemati-build-install-run — the commands that actually work, and the two naming traps: the iOS product is Aposemati.app, not AposematiCamera.app, and xcodebuild -destination id= is not the devicectl identifier (same phone, two UUIDs). Also: a denied Local Network permission does not error — the browser parks forever.
  • aposemati-apple-signing-gotchas — WWDR G3 intermediate, Xcode-first device registration, security-scoped bookmarks.

Build Status

  • aposemati-phase2-live-preview — 🟢 PHASE 2 COMPLETE at c29f84d, 2026-08-25: 33 commits, 418 tests, both apps building, live preview over QUIC datagrams working end to end on loopback. ⏸️ Verdict deliberately blank pending hardware — the two deciding readings need two devices. Ranked hardware checklist inside; item 1 is re-verifying Phase 1’s 48 MP + ZSL stills with the video output attached.
  • aposemati-whole-branch-review — ✅ the last engineering step is DONE: the final whole-branch review ran on 2026-08-19 and no finding blocks the branch. The remaining work is applying its ranked fixes (arm D, AuditGapTests.swift, the C2 hybrid) and the hardware run.
  • aposemati-loopback-blind-spot — 🐛 the permanently frozen viewfinder is fixed, 2026-08-26: NetworkPreviewChannel had no teardown path at all on the sending end. Two changes — a [weak self] stateUpdateHandler in the shared class (which fixes both ends) and the retry loop the Mac already had. Verified by hand-run mutation; still open: whether awdl0 delivers .failed at all, and the missing-shutter defect.
  • aposemati-crop-and-framing — 🟡 Phase 6 first half shipped 2026-08-26 at 4c0d828, 546 tests: lens picker, framing control, countdown, one normalized crop region driving both preview and still. Two defects found by hardware in ten minutes — one now closed, one still open.
  • aposemati-phase0-phase1-build — 🟢 working software, final state 2026-08-19: 48.8 MP iPhone → Mac over awdl0 — direct. 68 commits, 219 tests, zero warnings, clean tree, branch phase0-phase1-link-and-shutter unmerged and unpushed, both apps installed on hardware. Review loop closed; the one remaining step is the final whole-branch review over all 68 commits + 27 deferred minors.

Code Review & Audit

  • aposemati-whole-branch-reviewthe final whole-branch review, 2026-08-19: six reviewers, isolated clones, all 68 commits at once, verify-by-execution. No finding blocks the branch. POSIX 57 root-caused; the 250 ms listener margin; C1/C2 mutually exclusive; test suite ~70% real (119 mutations, 35 survived); 29 deferred minors triaged with seven struck as disproved by measurement; four documentation-drift items. Nothing applied yet.
  • aposemati-build-process-lessons — the review method: mutation testing’s two opposite failure modes, instrument-vs-instrument checking, per-agent clones, a per-task review’s structural blindness.
  • aposemati-build-process-lessons — ⭐ Phase 2’s additions: five measurements that answered a different question than the one asked; two tasks each correct in scope, jointly leaving a hole neither owned; a test double that collapses two roles hides defects that survive full review with mutation evidence; Apps/ is outside the SPM package and was silently broken for five commits; and bounding a review explicitly beats an open one.
  • aposemati-phase2-live-preview — 23 rulings and ~15 deferred minors in one phase, one whole-phase review plus a fix wave, with the parked residuals listed rather than quietly closed.
  • aposemati-loopback-blind-spot — ⚠️ the limit of review itself: 546 tests, eight task reviews and a whole-branch review all missed a defect a plugged-in phone found in ten minutes, because the harness cannot produce the failure the field produces. The mutation was run by hand rather than trusted from a report — precedent: commit 371b32a, a fix later disproved because its test passed identically with and without the change.

Competitive Intelligence

  • camo-studio-teardown — Camo Studio 2.8.2 at binary level: pricing, free-tier limits, the exclusion matrix, missing NDI/curves/encoder control, Snap Camera Kit AR dependency, AiBackgroundStyles.json.
  • aposemati-apple-api-measurements — Continuity Camera’s real ceiling (1920x1440, 8-bit), Photo Booth’s non-destructive effect model, OBS/Camo signing posture.

Distribution & Signing

  • aposemati-build-install-run — signing summary plus the install-side identifier trap.
  • aposemati-apple-signing-gotchasWWDR G3 intermediate missingfind-identity reports 0 valid identities and codesign fails errSecInternalComponent; Xcode device registration before -allowProvisioningUpdates; security-scoped bookmarks in the sandbox; XcodeGen with an uncommitted .xcodeproj.
  • aposemati-apple-api-measurements — Developer ID + notarization, not Mac App Store; OBS (2MMRE5MTB8) and Camo (Q248YREB53) both unsandboxed with no MAS receipt; Camo’s temporary-exception.sbpl for usbmuxd; system extensions must be notarized to load.

Homelab Integration

Image Processing & Encoding

  • aposemati-apple-api-measurementsdevice capability matrix: 48 MP starts at the iPhone 14 Pro; 12/13/14 non-Pro and all iPads are 12 MP; 24 MP only via deferred delivery (opted out). Nothing hardcodes a resolution.
  • aposemati-apple-api-measurements — VideoToolbox low-latency latencies (H.264 4K30 140.1 → 16.4 ms), kVTCompressionPreset_VideoConferencing, AV1 encode does not exist, Core Image colour chain at 0.506 ms/1080p, CIPersonSegmentation accurate fixed at ~30 ms.
  • aposemati-crop-and-framingAVCaptureSession’s .photo preset silently selected a 17 fps video format and nobody had noticed; a format offering 4032×3024 video at 30 fps and 8064×6048 stills sat unused in device.formats the whole time. Now 30–31 fps on hardware. Found by printing the format table, not by reasoning — two mechanisms were confidently argued first and both were wrong. Also: at zoom 1 the phone’s bytes go to disk untouched (~0 ms, gain map intact); only a zoomed still is re-encoded (~300 ms).
  • aposemati-phase2-live-previewthe JPEG-per-frame viewfinder and the H.264 gate: a 12-rung ladder (3 scales × 4 qualities, long edge 1920), reactive admission, and the measured baseline H.264 must beat — 482,530 B / 408 fragments / 117.4 Mbps against ~33,300 B / ~29 fragments / ~8.1 Mbps. H.264 buys bandwidth and loss tolerance by spending latency (23.2 ms encode vs JPEG’s 2.8–7.6). Periodic intra-refresh goes live the moment H.264 lands, and the measured p sets its interval.

Process & Method

  • aposemati-loopback-blind-spot — ⭐ loopback cannot reproduce “a send that succeeds into nothing”, and that is why a 546-test suite was silent about a real defect. Over awdl0 a datagram send into a dead peer succeeds; on loopback it fails with POSIXErrorCode 57, so the harness takes the error path the field never takes and a test written for the defect passes against unmodified code. Only the quiet form (peer leaves, nothing being sent) is reproducible; the live-sender form is not reproducible at all. Rule: ask which error the real medium declines to produce before trusting a harness about a failure mode.
  • aposemati-build-process-lessons — ⭐ mutation testing has two opposite failure modes and neither was guarded in any of 14 task reviews: false SURVIVED (mutant reverted by another agent’s directory-wide git checkout, or never compiled in — SwiftPM’s same-second mtime race reuses the previous binary) and false CAUGHT (21% of pristine runs fail under load). Guard with a canary test that passes only if the mutant is live plus source/binary hashing; give each concurrent agent its own git clone --local.
  • aposemati-build-process-lessons — ⭐ “verify by execution” is necessary but NOT sufficient — check the instrument against an independent one. Both wrong claims in the whole-branch review were measurement artifacts (attackers’ memory counted as the victim’s; a harness reusing one Data buffer that copy-on-write shared across all frames — and Data(count:) is equally invalid, untouched zero pages are never resident). Caught by another reviewer’s number disagreeing, never by re-reading code.
  • aposemati-build-process-lessons — ⭐ trace a symptom to its single throw site and measure there (two fix rounds were spent on a bug a review reported as fixed: once from a confident report that contradicted its own error table, once from a mutation-harness false negative) and treat a confident report as a claim to verify; a per-task review structurally cannot see across tasks; verify by execution, not by reading; passing tests are never evidence; mutation testing required wherever a fix claims to close a hole; a test named for a failure often does not catch it; commit-as-you-go (one agent lost a whole fix round to a machine sleep, another lost nothing).

Project Decisions

  • aposemati-pairing-security-modelin-memory per-launch TLS identity, never persisted, never in the keychain (accepted cost: re-pair on every relaunch); commit-reveal SAS over a typed code; the pairing model moved twice, both times because measurement contradicted the plan.
  • aposemati — name (aposematism, a pun on Camo), open source + free, Apple-only, “serious personal tool”, v1 = 48MP stills + on-device recording, virtual camera deferred to phase 7, off-router transport as the defining constraint.

Research & Planning

  • aposemati — phase plan; Phase 0 transport spike is existential because no throughput figure exists anywhere.
  • aposemati-phase2-live-previewthe open decision: JPEG-per-frame or H.264. Four passing rows and four failing rows, the exact two-device protocol that fills them, and a deliberately blank verdict. Row 4 (bitrate against measured throughput) is the one most likely to fail and the one not in the spec.
  • aposemati-transport-findings — the research that killed the previous plan; SDK-header-level verification method.
  • diy-continuity-camera-clone-research — ⚠️ superseded original brief; transport-first framing still valid.

Security

  • aposemati-pairing-security-model — ✅ the “cheating initiator dictates the six digits” exploit is REFUTED (the commit-reveal binds; the grind was offline computation never pushed through the shipped responder) — but pairingCommitmentBroken has zero test coverage, deleting the verification guard SURVIVES mutation, and the existing MITM test exercises nonce mixing, not the commitment. Online regrinding rated Low (~297/s, no rate limit, but the code cannot be steered).
  • aposemati-pairing-security-model — ✅ the pre-confirmation memory hole is CLOSED (aef872b): the transport reads nothing until the app asks for frames — 34.7 MB peak against four flooding strangers vs 486 MB reverted; and the refusal-recovery endless loop is closed (e1cd333). Plus the 0.77 s machine-in-the-middle that defeated plain SAS comparison, and the commit-reveal nonce exchange that fixes it; 252 MB buffered by an unconfirmed peer; the refusal blacklist’s per-process-lifetime scope; UI as part of the security boundary.
  • aposemati-peer-restart-inference-boundary — ⭐ peer data must never move the watermark backwards. Seven rebuilds of one restart detector; four inference mechanisms each satisfied their requirement and each was exploitable, because an honest restart and a spoofed one are indistinguishable at admission. The fifth answer was to delete the mechanism and let the session layer call reset(). One hard residual (cyclic restart inside the reordering tolerance) documented with the reason the obvious silence-keyed fix must not be used.
  • swift-character-isnumber-digit-trap — non-ASCII digits passing validation in three places, one of them the pairing code.
  • aposemati-network-framework-gotchas — QUIC has no TLS-PSK; .ready on a listener proves nothing about the security config.
  • ⚠️ aposemati-peer-restart-inference-boundarythe silence rule’s real scope, and a miscitation that was widening it (2026-08-26): it binds the receiver-side reassembler stall watchdog only and says nothing about connection state. HANDOVER.md cites “spec §4.3” three times; that section does not exist, the rule is at 2026-08-18-aposemati-design.md:502. A rule that travels by citation drifts in scope — here it was being used to argue against a stateUpdateHandler, which is a local transport fact, not silence.

Superseded & Corrections

  • diy-continuity-camera-clone-research — ⚠️ SUPERSEDED 2026-08-18. Wi-Fi Aware “winning path” invalidated; AWDL-was-replaced claim corrected inline.
  • aposemati-transport-findings — the correcting note.
  • aposemati-network-framework-gotchas — ⚠️ corrections to the transport research: NWBrowser.Result.interfaces is the only public AWDL source; never filter by interface type; QUIC has no TLS-PSK; Multipeer is deprecated as of the 27.0 SDK, not 26.x.
  • aposemati-apple-api-measurements — ⚠️ the “48 MP everywhere” assumption corrected to a per-device capability matrix.
  • aposemati — ⚠️ four channels on two connections, not one; phases renumbered (preview moved from Phase 1 to Phase 2).
  • ⚠️ aposemati-crop-and-framing“framing is metadata, never pixels” was REVERSED 2026-08-26: the crop ships destructive and the uncropped 48 MP original is never written to disk. Chosen deliberately and reaffirmed when the contradiction with this vault was found. Cost: a 2× crop takes 48.8 → 12.2 MP with no original to return to.
  • ⚠️ aposemati-peer-restart-inference-boundaryHANDOVER.md’s “spec §4.3” does not exist (cited three times); the silence rule is at 2026-08-18-aposemati-design.md:502 and is narrower than the citation implied.
  • ⚠️ aposemati-crop-and-framing — the freeze’s “leading hypothesis, untested” is now confirmed, fixed, and narrower than statedaposemati-loopback-blind-spot.
  • ⚠️ aposemati-phase2-live-previewcorrected again: preview is a third connection on its own listener and port, not datagrams on Connection A; .background is set nowhere; the “~155 fragments” figure carried forward from Task 6 is 240 (and the other case is 408); the burstiness rule was backwards; and the loss denominator was inflating p by 2.03×.

Swift & Concurrency Traps

  • swift-uncancellable-continuation-trap — a bare withCheckedContinuation inside a withTaskGroup cannot be cancelled; the QUIC idle timer had been accidentally rescuing the deadlock until a keepalive removed it. Appeared three times; fix is withTaskCancellationHandler.
  • swift-character-isnumber-digit-trapCharacter.isNumber / CharacterSet.alphanumerics accept Arabic-Indic, Devanagari and fullwidth digits. Also the unpinned-DateFormatter-locale neighbour.

Transport

  • aposemati-phase0-field-measurementsthe numbers: 339 Mbps beside the router, 156 Mbps where Camo drops, both awdl0 — direct, rtt 37 ms unchanged, 0.011% retransmission. Range costs bandwidth, not latency.
  • aposemati-network-framework-gotchas — how to obtain and pin an AWDL interface at all, plus the QUIC/keepalive/datagram traps.
  • aposemati-network-framework-gotchas — 📡 QUIC datagrams ARE reachable from Swift: read usableDatagramFrameSize off the datagram flow (the parent connection returns 0); an oversize datagram is accepted with NO error and silently dropped; the two ends disagree (9156 vs 9164) so clamp on the local read; DATAGRAM frames are not flow-controlled. ❌ NWConnectionGroup + NWMultiplexGroup did not workPOSIXErrorCode 50 on every extracted connection, group .ready with its path down; and both newConnectionGroupHandler and newConnectionHandler on one listener fails POSIXErrorCode 22.
  • aposemati-phase2-live-previewthe loss table and the bandwidth ceiling: a lost fragment costs exactly one frame (measured on two real QUIC ends at 1200 B); 10% of frames go at p=0.026–0.044%; preview at 117.4 Mbps is 75% of the 156.3 Mbps measured at the Camo-drop spot, against a derived 50% bar. Phase 0’s 0.011% retransmission is a different quantity and cannot be substituted for p.
  • aposemati-network-framework-gotchas — ✅ POSIX 57 SOLVED: a QUIC listener hands you two NWConnections per peer (streamIdentifier 1 = the one it was handed, 0 = the peer’s real stream) and cancelling either resets the whole connection. Tolerance is a hard cliff at ~250 ms, not a probability. Fix is a reference lifetime (park by 4-tuple, release on the peer’s stream, reap from your own timeouts) — 1800 pairings, zero failures; the naive return nil is worse than the bug.
  • aposemati-phase0-phase1-build — the readiness bound is 30 s (was 10, measured still failing at 10.65 s over a radio). The POSIX 57 that was long unexplained is now root-caused — see above.
  • aposemati-whole-branch-reviewC1/C2: the late-reader guarantee holds only to 256 KB (a 4 MB photo vanishes silently while carriedTraffic() reports it acknowledged), and LoopbackTransport accumulates 320.8 MB for 320 MB offered with no back-pressure — but has zero non-test uses.
  • aposemati-transport-findings — ⭐ the verdict: Wi-Fi Aware is @available(macOS, unavailable) (five independent proofs); AWDL is alive (awdl0 UP, llw0 11.5 GB in); no public AWDL API; Multipeer Connectivity not deprecated; NWParameters.includePeerToPeer is the primary candidate; iPhone 16/17 non-Pro are USB 2 only; Wi-Fi Aware hardware/entitlement/Info.plist details kept for a future iPhone↔iPad path.
  • aposemati-network-framework-gotchas — 💀 a QUIC datagram send into a DEAD peer SUCCEEDS over awdl0 and FAILS on loopback (POSIXErrorCode 57). Never make send failure your peer-death detector — install a [weak self] stateUpdateHandler on every NWConnection and nil it inside shutDown() before the cancel() that re-enters it. Loopback propagates a peer’s cancel() in ~76 ms; over the radio the CONNECTION_CLOSE may be lost and the survivor falls back to the 30 s idle timeout — unmeasured, and hardware checklist item 8.
  • aposemati-loopback-blind-spot — the defect that fact caused: a permanently frozen viewfinder with both ends believing they were healthy, and a Mac log looping cleanly every 32 seconds forever.
  • aposemati — four-channel QUIC design over whichever transport survives Phase 0.
  • camo-studio-teardown — Camo’s own transport prefs (“Bonjour connections”, “Peer-to-peer”, “Malheur connections”, “Send video packets twice”).
  • diy-continuity-camera-clone-research — ⚠️ superseded options table (USB, AWDL, Wi-Fi Aware, hotspot).

Virtual Camera

  • aposemati-apple-api-measurementsCMIO extension gotchas: XPC does not work (CMIOExtensionMachServiceName is “a red herring”, NSCocoaErrorDomain Code=4099); use a sink stream via CMIOStreamCopyBufferQueue + CMSimpleQueueEnqueue; DAL disabled in Sonoma 14.1; container must live in /Applications; entitlement list.
  • camo-studio-teardown — Camo ships com.reincubate.macos.cam.avextension + a CoreAudio virtual driver + a LaunchAgent.
  • aposemati — deferred to phase 7.
  • diy-continuity-camera-clone-research — original cross-platform sink survey (CMIO / Media Foundation / v4l2loopback); Windows and Linux are now out of scope.