---
type: reference
title: "CAMDOM -- Debugging Stories & Bug Narratives"
description: "The bugs that shaped CAMDOM: 'cache' bug, Android x Android failure, iOS server election, multi-device cascade, CoreNFC crash, bond state, OpenReplay deployment."
tags: [camdom, debugging, bugs, debugging-stories, openreplay, ble, post-mortem]
timestamp: "2026-07-20"
---

# CAMDOM -- Debugging Stories & Bug Narratives

> Every bug teaches something. These are the stories of the bugs that shaped CAMDOM during the critical Aug 14-22, 2024 debugging sprint -- reconstructed from WhatsApp chats, OpenReplay session data, and code archaeology.

---

## 1. The "Cache" Bug (Aug 14-20)

The most persistent and misunderstood bug in the entire project.

### What the client called it

> "tem um problema grande CASHE no app do android e ios. dps q vc comeca a conectar e desconectar mais de uma vez com o app aberto, comeca a dar pau"
>
> -- Felipe, forwarding client feedback, Aug 14

The client used "cache" as a catch-all term for any behavior that repeated on reconnect. It stuck. Everyone started calling it the cache bug, even though it had nothing to do with caching.

### What it actually was

After connecting, the first packet sent to the server failed because the server was not ready yet. This triggered the alarm state, which looked like "cache" because it happened repeatedly -- every time you connected and disconnected, the first-packet-after-connect failure would fire the alarm again.

### Discovery path

- **Aug 14:** Felipe reports it from client testing. The client describes it as a cache problem that happens after connecting and disconnecting multiple times with the app open.
- **Aug 15:** Alefita investigates via OpenReplay sessions (deployed secretly one day earlier).
- **Aug 15:** OpenReplay reveals the exact moment of failure:

> "nossa, vi aqui, muuuuito estranho sabe, consegui achar umas sessions android android e o android ta conectando, manda o pacote de sync, recebe a resposta positiva do sync e entonces disconecta, acho que a logica que eu adicionei pro retry, deve estar com problema no android, o que n faz muito sentido pq e a mesma implementacao, mas vou dar uma revisada"
>
> -- Alefita, Aug 15, 13:33

- **Aug 19:** Alefita identifies root cause after replicating the exact scenario with 3 iPhones:

> "basicamente tem 1 device que ta disconectando e o server n disconecta todo mundo, e o problema de 'cache' quando se conecta e pq ele da um problema ao mandar a primeira mensagem pq o servidor n ta respondendo ainda, dai ele dispara o alarme, mas e so adicionar um tratamento pro erro, como ja acontece quando ele da erro ao se conectar"
>
> -- Alefita, Aug 19, 16:18

- **Aug 20:** Fixed by adding error handling for the first packet after connection, similar to existing connection error handling.

### The OpenReplay connection

Alefita secretly deployed OpenReplay on Aug 15 to debug this exact issue. The session replay showed the exact moment of failure: Android connecting, sending sync, receiving positive sync response, then disconnecting because the alarm fired on a first-packet error.

> "Temos session replay habilitado desde a ultima versao, vou conseguir pegar cada sessao que eles fizeram o teste e ver os eventos, erros, e replay da session"
>
> -- Alefita, Aug 15, 08:41

Victor set up the OpenReplay instance at `openreplay.pay4brain.com.br` pointing to a GCP IP. The tool ran on k3s. Within hours, Alefita had session data from the testers.

### Technical root cause

In `BleManager.ts`, after `registerClientCallbacks()`, the client immediately sends the connection packet. If the server has not finished processing the connection, the write fails. The error path triggers alarm state instead of graceful retry, because the error handler did not distinguish between "connection lost" and "first packet failed."

### The fix

Added error handling around the first packet send, analogous to the existing connection error handling. When the first packet fails, the client retries instead of firing the alarm.

### Lesson

Misnamed bugs are dangerous. Calling it "cache" sent people looking in the wrong direction. The real issue was a race condition between connection completion and first-packet transmission. Accurate terminology matters.

---

## 2. Android x Android Complete Failure (Aug 15)

### The crisis

> "felipe, cara... nao funcionou a conexao entre 2 androids, mano. nada. nao funciona."
>
> -- Client feedback, forwarded by Felipe, Aug 15, 09:05

> "Nao me fala"
> "Tao Hiperventilando la"
>
> -- Felipe, Aug 15, 09:07

### Alefita's response

> "Demais, foi a primeira coisa que eu fiz funcionar kkk"
>
> -- Alefita, Aug 15, 13:17

She had validated Android x Android extensively. The failure made no sense.

### Investigation

OpenReplay sessions showed Android connecting, sending sync, receiving positive response, then disconnecting -- identical behavior to the "cache" bug. But the deeper issue was different.

> "acho que a logica que eu adicionei pro retry, deve estar com problema no android"
>
> -- Alefita, Aug 15, 13:33

### Root cause

Two problems compounded:

1. The retry mechanism added for connection resilience was interfering with the normal connection flow on Android.
2. The testers were "clearing cache" between tests, which reset Android permissions, breaking BLE functionality entirely.

### Fix

Adjusted the retry logic to not trigger during successful connection handshake. Additionally, Alefita identified that the testers needed to unpair Bluetooth bonds from previous versions before testing.

### Lesson

When a feature that was "the first thing you got working" breaks, check the test environment first. The testers' habit of clearing cache was destroying Android's BLE permissions.

---

## 3. iOS x Android Server Election Bug (Aug 15-16)

### The problem

iOS and Android could not connect to each other. Android devices got stuck in scanning mode.

### Alefita's discovery

> "eu descobri que em um caso de Android e iOS o Android tem sempre que ser o server, vou testar minha tese vendo os erros que eles pegaram"
>
> -- Alefita, Aug 15, 08:52

### Root cause

When Android saw an iPhone nearby (via BLE scan), it assumed it should become the server based on the advertising value comparison. But the iPhone also expected to be server. Both devices tried to become GATT server, creating a deadlock where neither would initiate a client connection.

The original code had the advertising value ranges, but the server election logic was not properly handling the case where an Android device needed to always win when paired with iOS, because iOS CoreBluetooth has severe limitations as a GATT server.

### The advertising value design

This led to the advertising value range system that persists in the codebase:

```
iOS advertises:    1-124
iOS as server:     exactly 125
Android advertises: 126-249
Android as server:  exactly 251
```

Higher value = server. In iOS x Android, Android always has higher value and therefore always becomes the server.

### Connection packet format

`c:{platform}:{advertisedValue}` where platform is `0` (iOS) or `1` (Android).

### Fix

> "Correcoes na conexao androidXios, varios ajustes na logica de migracao onde se tiver 1 Android ele sempre vai ser o server, essa logica estava travando os Android de se conectarem pois ao verem 1 iPhone por perto eles sempre assumiam que tinham que ser server e paravam o scan daí n se conectavam"
>
> -- Alefita, Aug 16, 00:33

### Lesson

Cross-platform BLE is not just about different APIs -- it is about different capabilities. iOS cannot reliably serve as GATT server for Android clients. The election algorithm had to encode this asymmetry directly into the protocol.

---

## 4. Multi-Device Disconnect Cascade (Aug 14-19)

### The problem

With 3+ devices connected, disconnecting one device caused alarms on all other devices unexpectedly.

### Felipe's report

> "qnd os dois apps saem ao mesmo tempo"
>
> -- Client feedback, forwarded by Felipe, Aug 12

### Investigation

Alefita identified the issue on Aug 14:

> "funcionou 'quase' certinho, so parece que nao registrou a desconexao segura que rolou, acho que ta flipado a logica"
>
> -- Alefita, Aug 14, 11:18

By Aug 17, the intermittent nature was clear:

> "to com um bug intermitente na desconexao consensual, se achar alguma coisa me avisa, aproveitei e instrumentei tbm pra quando for olhar os replays, ter uma visao de sessao que inicia quando o server inicia e termina quando todos desconectam consensualmente"
>
> -- Alefita, Aug 17, 10:17

### Root cause

The disconnect negotiation protocol was not handling the case where one device drops while others are still connected. The `askedDisconnectionDevices` array was not being properly cleaned when a device disconnected unexpectedly. The server's poll check (`askedDisconnectionDevices.length === connectedClients.length`) would never match because the disconnected device was still counted in `connectedClients`.

### Fix

Added proper cleanup of disconnect state when a device drops unexpectedly. Also adjusted the consensus mechanism to account for the server's own state in the comparison.

### Lesson

The disconnect protocol's `askedDisconnectionDevices` vs `connectedClients` comparison assumed all devices would cooperate. In reality, devices can drop at any time. State cleanup on unexpected disconnect is as important as the happy path.

---

## 5. CoreNFC Crash (Aug 19)

### The crash

The app crashed on launch in the release build. Development builds worked fine.

### Felipe's panic

> "Crash no ios. Mensagem do cliente. Assim... Cliente ta surtado la"
>
> -- Felipe, Aug 19, 05:50

> "Ficou MUITO ruim com o cliente. A gente combinou uma outra coisa. Por favor. Foca em colocar uma versao o mais rapido possivel que funciona"
>
> -- Felipe, Aug 19, 06:10

### Investigation

Alefita woke up to the crisis:

> "Wtf. Bom dia, ja to de olho nisso"
>
> -- Alefita, Aug 19, 07:09

Within minutes:

> "foi o CoreNFC, rodei em release aqui no meu celular tinha rodado liso, subindo build"
>
> -- Alefita, Aug 19, 07:17

### Root cause

CoreNFC framework was included in the release build but not properly configured. The NFC entitlement was added to suppress Apple Pay pop-ups (the NFC proximity triggering Apple's contact exchange), but the CoreNFC framework reference in the release entitlements caused an immediate crash on launch. Development builds had different entitlements that masked the issue.

### Fix

Removed CoreNFC references from the project entirely. The NFC suppression approach via `com.apple.developer.passkit.pass-presentation-suppression` entitlement was attempted but required direct Apple approval. Ultimately, NFC was abandoned as a feature.

### Lesson

Entitlements and framework references must be tested in release builds specifically. Development and release builds can have different entitlement configurations, and a framework that works fine in debug can crash in release if the entitlement chain is incomplete.

---

## 6. The Bond State Problem (Aug 19-20)

### The problem

After updating from a previous version, Android devices could not connect. Users had to manually unpair Bluetooth bonds in system settings.

### Why it happened

A previous version of the app created BLE bonds that persisted across app uninstalls. The new version's BLE service UUID was different, but the old bond prevented discovery. The OS-level Bluetooth bond was blocking the app from seeing the device's advertisement.

### The workaround Alefita prescribed

> "Precisamos explicar pros users de Android que devido aos testes de versoes passadas eles precisam ir nas configuracoes do bluetooth e desparear de todos os dispositivos que ja foram pareados (principalmente se for so um numero entre 0 e 255 o nome do dispositivo pareado)"
>
> -- Alefita, Aug 16, 00:33

### Felipe's frustration

> "Nao rola dele ter que limpar as conexoes do Bluetooth previas"
>
> -- Felipe, Aug 20, 05:41

> "Nao rola isso. Nem eu sei como fazer"
>
> -- Felipe, Aug 20, 05:45

### Alefita's response

> "Paciencia, isso nao e algo que todo mundo tem que fazer, mas teve uma versao que fez o bond e precisa resetar o estado de bond senao ele nao vai conseguir ver o server do bluetooth ai nada vai funcionar"
>
> -- Alefita, Aug 20, 06:06

### Why this is a fundamental BLE problem

BLE bonds are managed by the OS, not the app. Uninstalling the app does not clear bonds. This is a platform limitation, not a bug. The bond persists at the Android Bluetooth stack level, and if the service UUID changes between versions, the bond becomes an obstruction.

### Lesson

BLE bonds are sticky. Any version that creates bonds must account for what happens when those bonds persist after an app update or uninstall. The bond state is the one piece of BLE that outlives the app.

---

## 7. Sound and Haptics Disappearing (Aug 20)

### The mystery

> "Sumiram som e vibracoes. Tinha som e vibracao quando movia a bola e etc. Nao tem mais"
>
> -- Felipe, Aug 20, 10:13

### Investigation

Alefita showed in a video that sounds only play during alarm state, not during normal interaction. The VolumeManager only sets volume to max for alarm sounds.

> "Ele so seta o volume no maximo pro alarme, se vc tiver com o volume baixo ele nao vai tocar"
>
> -- Alefita, Aug 20, 10:22

### Root cause

Not actually a bug. The behavior was correct but the expectation was wrong. The swipe gesture haptics were working (they are system-level, not gated by VolumeManager). The alarm sound was gated by the volume setting -- the `VolumeManager` only forces max volume for alarm playback. If the system volume was low, non-alarm sounds would not be audible.

### Lesson

When a tester says "it disappeared," verify whether it was ever there in the way they remember. Expectation mismatches are as common as real bugs.

---

## 8. The "Tested Wrong Version" Problem (Aug 21-22)

The debugging story that nearly broke the team.

### The discovery

> "e a alef n mandou os videos, pqp mano, perdi umas 7 horas do meu dia debugando algo que os caras simplesmente tinham testado a porra da versao errada no android, ai eu faco o que com isso, bato palma pra eles, mando um 'WE NEED TO FIX IT' ?"
>
> -- Alefita, Aug 22, 06:32

### What happened

The testers were running Android version 2.21 while iOS was on version 2.31. The Android version was from Aug 17, before the critical connection fixes. All the "bugs" they reported on Aug 20-21 were already fixed in the current Android build.

> "Passei um bom tempo apanhando ate perceber que eles testaram o Android na versao errada kkkkkkkkkkkkk"
>
> -- Alefita, Aug 21, 18:38

### The compounding discovery

But it got worse. The testers had also not opened the Options menu, so both the distance alarm and the fire-on-disconnect-alone features were disabled (they default to off):

> "MANOOOOOOOOOOOOOO ELES NAO ABRIRAM A PORRA DO MENU DE OPCOES FELIPE, VEM POR PADRADO DESATIVADO O ALARME POR DISTANCIA E O FIRE WHEN SOMEONE TRIES TO DISCONNECT ALONE, PQP, OS CARAS N SE DAO AO MENOR TRABALHO DE PERGUNTAR"
>
> -- Alefita, Aug 22, 06:35

### Felipe's response

> "Calma. Uma coisa de cada vez. 1 tudo entra ligado. Tudo. Assim a gente resolve esse ultimo item ta?"
>
> -- Felipe, Aug 22, 06:37

### The aftermath

Alefita changed defaults so all features shipped enabled. The testers' feedback from testing wrong versions had already caused Alefita to make changes to correct behavior, introducing new regressions:

> "se eu subir a versao de ontem que eles testaram errado, nao tem essas 'correcoes' que eu fiz em cima do feedback deles usando o app em versoes erradas, tenho CERTEZA que vao achar pelo no IOS, CERTEZA CARA"
>
> -- Alefita, Aug 22, 06:40

### Lesson

Version coordination between iOS and Android testers is critical when both platforms are in active development. Without enforced version checking (which Alefita later added as a sync-time validation), tester feedback can be based on stale code, causing developers to "fix" things that are not broken and breaking things that were working.

---

## 9. Intermittent iOS x iOS Disconnect (Aug 19-20)

### The problem

iOS x iOS connections worked perfectly one day but had errors the next.

> "ios com ios ONTEM tava rodando lindo, o unico pau que deu era qnd testamos 3 ios. querido, depois me fala, ok?"
>
> -- Client, forwarded by Felipe, Aug 20, 05:41

### Investigation

The issue was specific to 3-device iOS connections. With 2 iPhones and a Mac M1 acting as iPad, the fix worked. But with 3 physical iPhones, the server election had an edge case.

### Root cause

3-device iOS connection had a specific edge case in the server election where two devices could claim server role simultaneously. Since iOS values are in the 1-124 range, with 3 devices there was a higher chance of two devices picking close values. The randomization was not sufficient to guarantee uniqueness at higher device counts.

### Fix

Adjusted the advertising value randomization to ensure unique values across iOS devices. Also adjusted the migration logic so that only the explicit case of a late-joining Android device triggers migration:

> "ajustei a logica de migrations tanto ios quando android, o unico caso onde uma migration vai acontecer e DEVE na minha opiniao, n acho que deva ser invisivel, e se um user android chegar na brincadeira depois de 20 segundos que todo mundo conectado, ai todo mundo migra pro android e rola a animacao de migrate"
>
> -- Alefita, Camdom chat, Aug 23

### Lesson

Random value collision probability increases with device count. The server election algorithm needed to account for the multi-device case, not just the two-device case.

---

## 10. Alarm Triggering on App Switch (Aug 20)

### The problem

The alarm behavior changed between versions:

> "Antes era: qnd vc encostava na tela para trocar de app o alarme ja tocava. Agora nada acontece e so toca se a gnt sair do app e comecar a navegar no celular"
>
> -- Felipe, Aug 20, 10:57

### Root cause

The AppState listener was checking for any state change from "active," but the fix for the "cache" bug had tightened the check to only trigger on "background" specifically. Pulling down the notification center (which changes AppState to "inactive" but not "background") no longer triggered the alarm.

> "Isso dessa forma tava causando os 'erros de cache' tipo, o app recebe o evento de onChange e agora so checa se o state e igual background pra dai disparar o alarme, antes ele tava checando qlq coisa diferente de ativo e disparando, dai puxar a central de notificacoes disparava e falavam 'erro de cache'"
>
> -- Alefita, Aug 20, 11:13

### The reverse problem

The client also reported:

> "quando o celular ta conectado, se apenas uma pessoa apertar o botao de destravar o alarme toca. Tem q tirar isso"
>
> -- Felipe, Aug 20, 10:57

### The spec conflict

> "A spec que passaram foi: se alguem tentar se desconectar sozinho o alarme deve tocar"
>
> -- Alefita, Aug 20, 11:14

The original spec said alarm fires on disconnect attempt. But in practice, this made the alarm too sensitive. One person pressing the button would alarm everyone. The negotiation between spec and UX was ongoing, and Alefita eventually removed the feature from the Options UI defaults.

### Lesson

The AppState change detection threshold is a UX decision, not just a technical one. Too sensitive = false positives from notification center. Too loose = actual backgrounding goes undetected. The "cache" bug fix inadvertently changed the alarm sensitivity.

---

## 11. The Spec vs Reality Gap (Aug 20-22)

A meta-bug that was not a code defect but a communication failure.

### What happened

The client's feedback consistently described behaviors as bugs that were actually working-as-designed. The alarm firing when someone tried to disconnect alone was the spec. The migration animation appearing when an Android device joined late was designed behavior. The error message showing on connection failure was intentional (GenZ users expect feedback, not silent retries).

> "eles precisam se decidir o que e pra fazer no caso do problema de conexao. Pq quando eu so deixei a bola travada no scanning reclamaram que nada acontecia, justamente por estar tentando a reconexao em background sem alterar a interface. Agora, reclamaram que eu fiz a bola voltar ao seu lugar inicial pra indicar a desconexao, mostrei uma mensagem explicando o erro e entao tentava novamente"
>
> -- Alefita, Aug 20, 11:39

### The feedback loop

Alefita created annotated screenshots to explain what each button does:

> "Vou explicar o que cada botao do app faz e como as coisas funcionam com prints e setas vermelhas como se tivesse ensinando minha mae a usar"
>
> -- Alefita, Aug 22, 07:00

### Felipe's wisdom

> "Cliente e SEMPRE assim. Vai descansar. Vc ta atrasada. Stressada. Nao sabe. E pensa. Os usuarios deles tb nao vao saber. Por isso e importante tudo funcionar sem friccao"
>
> -- Felipe, Aug 22, 06:53

### Lesson

When testers describe a behavior as a bug, first verify whether it matches the spec. If it does, the problem is not the code -- it is the spec or the communication. The solution is documentation and defaults, not code changes.

---

## 12. Debugging Toolkit

What tools were used during the Aug 14-22 debugging sprint:

| Tool | Purpose | Deployment |
|------|---------|------------|
| **OpenReplay** (session replay) | Caught the cache bug by replaying exact failure moments | Deployed secretly Aug 15, self-hosted at `openreplay.pay4brain.com.br` on GCP |
| **Console logs** (BleManager.ts) | Extensive logging of packet flow, connection state, error paths | Instrumented per-session with server/client view correlation |
| **Multiple physical devices** | 4 Android phones + 2 iPhones + Mac M1 (as iPad) | Required for reproducing multi-device scenarios |
| **Video recording of test sessions** | Client testers recorded their failures | Sent via SharePoint links |
| **WhatsApp group** | Real-time bug reports from testers + rapid triage | Primary communication channel |
| **App Store / TestFlight / Google Play** | Distribution for testers | Internal testing tracks |

### OpenReplay specifics

Alefita deployed OpenReplay on Aug 15:

> "Preciso subir uma instancia do Open replay pra gente, session replay for free hehe"
>
> -- Alefita, Aug 15, 16:01

Victor set up the DNS:

> "openreplay.pay4brain.com.br apontando para 34.128.174.243"
>
> -- Victor, Aug 18, 12:55

The self-hosted version removed the 1000 user monthly limit of the cloud offering. By Aug 17, Alefita had instrumented the sessions to track server/client correlation:

> "aproveitei e instrumentei tbm pra quando for olhar os replays, ter uma visao de sessao que inicia quando o server inicia e termina quando todos desconectam consensualmente"
>
> -- Alefita, Aug 17, 10:17

### The logging insight

The logs revealed patterns that raw error reports could not:

> "consegui achar umas sessions android android e o android ta conectando, manda o pacote de sync, recebe a resposta positiva do sync e entao disconecta"
>
> -- Alefita, Aug 15, 13:33

Without session replay, the team would have been guessing. With it, the exact packet sequence was visible.

---

## 13. Pattern: The Regression Cascade

Looking across all these bugs, a pattern emerges. Aug 14-22 was a cascade where fixing one bug introduced or exposed another:

1. **Aug 14:** Cache bug reported
2. **Aug 15:** Retry logic fix for cache bug breaks Android x Android
3. **Aug 15-16:** iOS x Android server election discovered and fixed
4. **Aug 17:** Multi-device disconnect still intermittent
5. **Aug 19:** CoreNFC crash from an unrelated entitlement change
6. **Aug 19:** Bond state problem from earlier versions
7. **Aug 20:** Alarm sensitivity changes from cache bug fix
8. **Aug 20:** Sound/haptics "disappearing" (not a real bug)
9. **Aug 21-22:** Testers using wrong Android version, misinterpreting disabled features as bugs
10. **Aug 22:** All of the above compounding into "nothing works"

Each fix changed the system's behavior in ways that affected other subsystems. The 1878-line `BleManager.ts` god object meant that every change touched shared state. Without tests, there was no safety net.

### Alefita's exhaustion

> "eu ia no jogo do corintinhans hoje la em itaquera, mas n dava pra sair com esses problemas"
>
> -- Alefita, Aug 20, 18:09

> "Acordei vomitando, acho que peguei uma infecao alimentar comendo uns espetinho duvidoso ontem"
>
> -- Alefita, Aug 22, 06:52

### Felipe's steadying hand

> "Descansa. Depois do almoco a gente fala"
> "O que for bug a gente acerta depois que vc descansar"
> "Nao adianta deixar isso abater a gente agora"
>
> -- Felipe, Aug 22, 06:51-06:54

---

## Cross-References

- [[camdom]] -- Project overview
- [[camdom-system-design]] -- RSSI/zombie features context, disconnect protocol state machine, alarm trigger paths
- [[camdom-code-quality]] -- Technical debt, BleManager god object, 115 lines of commented-out code, 0% test coverage
- [[camdom-architecture]] -- BLE architecture, GATT service design, packet protocol
- [[camdom-packet-protocol]] -- The custom packet format that every bug above involved
- [[camdom-development-timeline]] -- The chronological progression from first commit to release
- [[camdom-people]] -- Alefita, Felipe, Victor, and Mikael -- the team behind the debugging
