This page lists the limits of the current build and the problems found while writing these documents by reading the code and running it. Each entry gives the symptom, the cause with a file reference, the impact, and a workaround or fix. Nothing here blocks the main demo: Light and Sound fountain transfers of text, photos and short videos work as documented. The Vibration channel is the exception: it is experimental and doesn’t work reliably yet (§2.8).
Back to the documentation index.
| Level | Meaning |
|---|---|
| High | Can make a user-visible transfer fail or report the wrong outcome |
| Medium | Wastes time or airtime, or misleads developers |
| Low | Cosmetic, or only reachable from developer screens |
AppController.sendPhysicalMessage marks both direct paths (Light and Sound) as sent, or failed.HardwareAcousticChannel.cancelTransmit now stops the audio player immediately, and the transfer screen’s cancel reaches it.acousticFountainMaxBytes (8 192 B) take the fountain path. Larger ones go through runHardwareTransfer and the legacy packet/FSK modem. The receiver listens with the fountain modem, so it never decodes them.hardwareTransportConfig). The sender starts retransmitting while the first copy is still on air.ackTimeoutMs with the channel’s expected airtime.Light has no return path, so the sender streams until Stop or the 10-minute safety cap. The receiver’s DONE is the only completion signal. Resume streaming continues the same session if the sender stopped too early. See ADR-14.
HardwareAcousticChannel), which Android only guarantees to pass 18.5–20 kHz on devices that declare near-ultrasound support. Bluetooth hands-free audio stops at 8 kHz, and web browsers resample the microphone.A Silent frame always carries a 24-byte block, so a three-letter message takes one 4.8 s frame. A plain two-tone FSK sender at 60 ms per bit needs 4.3 s for the same text, but falls behind from about five characters on and has no error correction. See Sound Channel §14.
HardwareAcousticChannel._playWav starts the readout clock when AudioPlayer.play() returns. Output buffering adds tens of milliseconds before the sound leaves the speaker, and far more over Bluetooth.SpectrumAnalyzer treats peaks within 4 bins (172 Hz) of a stronger one as window leakage, and neighbouring audible groups can put tones 1 bin apart.physical_codecs_test.dart), but real phone-to-phone transfers haven’t been made to work reliably. The vibration-coupled Simulation Lab scenario doesn’t prove otherwise, because it ends up sending over the simulated optical channel (§3.3, first row).ReliableTransport._handleAck sets lastAcked = seq and drops every pending packet up to seq (lib/core/transport/reliable_transport.dart). A later NACK finds nothing pending to resend.lastAcked only over a contiguous run.optical-degrades and burst-loss fail in every measured run. The engine makes the right decision (optical ≈0.27, acoustic ≈0.70), but only at the first evaluation (iteration 20), after issue 3.1 has already stalled the transfer. Details and measurements: Simulation Lab.
| Quirk | Effect |
|---|---|
| Undiscoverable channels are still tested and can be selected | “Acoustic only” scenarios may run on optical |
| Override profiles replace all fields of a channel | Unset fields fall back to class defaults, not to the base profile |
| “Fixed” comparison strategies still run the adaptive orchestrator | Their switch count is hard-coded to 0 |
| Lost packets cost no simulated time | Throughput under loss looks better than reality |
| Progress bars stay at 0%, score rows don’t update after a switch, “Run All” doesn’t refresh the screen | UI only |
| The adaptive comparison uses 4 096 bytes instead of the scenario’s 8 192; “Static Switch” is never run | Comparison numbers aren’t like-for-like |
In hardware mode with acoustic available and no forced channel, TransferManager always selects acoustic (“Acoustic is bidirectional”, fixed score 0.9). The main UI always forces the channel, so this only shows up in the developer screens.
compressImageForTransfer always decodes and re-encodes at JPEG quality 78. It runs in the compose screen for picked photos, and again in AppController._prepareEnvelope for every image, including the bundled samples.If you attach a photo or video and type text, SendComposeScreen._buildPayload sends only the text. Fix: send both (two envelopes), or disable the text box while an attachment is present.
qrFountainFlagGzip (0x01) and isGzip exist in qr_fountain_frame.dart, but no sender sets the flag and no receiver inflates. Text could gain 2–3× from compression; JPEG and MP4 gain nothing.
nameLen and mimeLen are single bytes. ChatPayloadCodec.encode doesn’t truncate, so a UTF-8 file name over 255 bytes would corrupt the envelope. Fix: clamp names in encode.
codeUnits for data (Low)ComposePayload.data for text is text.codeUnits (UTF-16 units truncated to bytes). The envelope itself is built from text via encodeText (UTF-8), so transfers are correct. Only byteSize shown before sending can be off for non-ASCII text.
| Issue | Where | Impact | Fix |
|---|---|---|---|
| Release builds are signed with the debug key | android/app/build.gradle.kts |
Fine for sideloading and demos; can’t be published on Play | Add a release keystore (Build and Release) |
applicationId still marked TODO |
Same file | A generic ID may clash with other builds | Choose a final reverse-DNS ID before publishing |
| Brightness boost is Android-only | OpticalDisplayControl |
iOS senders must turn brightness up by hand | Add an iOS implementation (UIScreen.main.brightness) |
| Web: camera needs HTTPS or localhost; no vibration; no Gallery saving | Browser limits | Web works as a Light receiver/sender demo only | By design |
flutter test reports the platform as Android on every OS |
Test environment | platform_capabilities_test always takes the “hardware” branch |
Documented in Testing |
| CSK and legacy FSK modems aren’t reachable from the main UI | By design (ADR-15) | Kept as libraries and in tests | None needed |
These aren’t bugs; they follow from the physics. See Performance.
| Limit | Value | Why |
|---|---|---|
| Light range | ≈15–40 cm | Pixels per QR module at the camera (≥ 5 needed) |
| Light speed | ≈1.3–2.5 KB/s | Camera decode rate × bytes per sparse QR |
| Sound speed | 10.8–35.8 B/s audible; 3.4–5.0 B/s Silent | Symbol length needed to beat echo; Silent can play only one tone at a time without an audible difference tone |
| Sound range | ≈0.3–2 m audible; ≈0.1–0.5 m Silent | Speaker power and room noise; phones are weak at 19 kHz |
| Vibration speed | ≈0.5 B/s (nominal; real transfers are unreliable, see §2.8) | Motor spin-up/down time (tens of ms per pulse) |
| Vibration range | Phones touching | The accelerometer must feel the other phone’s motor |
| Security | None | See Security |
| # | Issue | Severity | Area |
|---|---|---|---|
| 2.1 | Sound sends marked delivered; Stop let a burst play out | Fixed | App controller, acoustic channel |
| 2.2 | Sound > 8 KiB uses an undecodable path | High | App controller |
| 2.3 | Vibration airtime > ACK timeout | Medium | Transport config |
| 2.5 | Silent depends on each phone’s 19 kHz response | Medium (by design) | Hardware |
| 2.6 | Silent “sos” takes one 4.8 s frame | Low (by design) | Sound profiles |
| 2.7 | Live kHz readout slightly ahead of the audio; close chord tones merge | Low | Sound UI |
| 2.8 | Vibration transfers unreliable on real phones (experimental) | High | Vibration channel |
| 3.1 | Cumulative ACK handling | High (protocol path) | Transport |
| 3.2 | Switching scenarios fail | Medium | Simulation |
| 3.3 | Simulation Lab quirks | Low | Simulation / UI |
| 3.4 | Hardware selection ignores scores | Low | Transfer manager |
| 4.1 | Photos re-compressed and grow | Medium | Media |
| 4.2 | Text replaces attachment | Medium | Compose UI |
| 4.3 | Gzip flag unused | Low | Light frame |
| 4.4 | 255-byte name limit unchecked | Low | Envelope |
| 4.5 | codeUnits byte size |
Low | Compose model |
| 5 | Debug signing, TODO applicationId, platform gaps | Medium / Low | Build |
The Roadmap turns the High and Medium items into planned work.