Files
kotlin_phone_app_admin/AGENTS.md

199 lines
7.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Kotlin Phone App + Admin
> Build spec for Paperclip. Follow the phases in section 11 in order. Do not skip a
> phase's Definition of Done. When something is unclear, ask Sam — do not guess.
---
## 1. Purpose and outcome
Sam's Android app. It is the **Hub's way in from the phone**, and the **trusted
admin surface** for approvals and parental controls.
**Outcome (the single success statement):**
> Sam talks to the Hub from his phone, hears the answer, and can approve or change
> anything from the same app.
## 2. Scope
**In scope**
- Capture voice and send it to the Hub.
- Show the Hub's reply: text, spoken audio, and notifications.
- Show approval prompts ("Did you ask for this?") and answer them.
- Admin controls: parental controls, by calling the internet control service.
- Native phone actions the Hub requests (call, open a link, open an app by deep link).
**Out of scope**
- The **kids' phone client.** That stays under `internet_use_control`.
- **Artemis** (screen control). Separate project.
- The **Hub** service itself.
- Understanding speech. The Hub does that.
**Decision to confirm:** this is **one app with two areas** — Hub (voice) and Admin.
Not two apps.
## 3. Users and roles
- **Sam only.** This app is not for Jo, Harry, or Finn.
- The kids' client is a different app, built under `internet_use_control`.
## 4. Functional requirements
| ID | Requirement |
|---|---|
| FR-1 | Capture voice. Start with **hold-to-talk**; add a wake word later. |
| FR-2 | Send the transcript to the Hub (`POST /ask`) and show the reply as text. |
| FR-3 | Play the Hub's reply as **TTS on the phone**, when the reply asks for it. |
| FR-4 | Show **approval prompts** and return approve or deny to the Hub. |
| FR-5 | Admin: list the kids' devices and their state, and lock, unlock, or grant time. |
| FR-6 | Admin calls the **internet use control** API directly. The app holds no control logic. |
| FR-7 | Gate admin actions behind a **keycode or biometric**. |
| FR-8 | Perform native actions the Hub requests: place a call, open a deep link. |
| FR-9 | Show Hub `/notify` messages as Android notifications. |
| FR-10 | Settings: Hub URL and token. Test the connection. |
| FR-11 | Offline: fail cleanly, and never claim a command succeeded. |
## 5. Screens and UX flow
**Screens**
1. **Home (Hub)** — a large talk button, the last reply, and recent history.
2. **Approvals** — pending "Did you ask for this?" items with Approve / Deny.
3. **Admin** — the kids' devices, each with its state and quick actions.
4. **Settings** — Hub URL, token, keycode, test connection.
**Main flow**
```
Sam holds the button and speaks → the Hub replies → the phone speaks it
If the Hub needs approval → a card appears → Sam taps Approve
If Sam opens Admin → he locks a phone → the internet control service applies it
```
## 6. Look and feel
Reuse the Family Home Lab design system, adapted to Android.
- Source: `/home/sam/Docker/Containers/family-home-lab/DESIGN.md`
- Tokens: `/home/sam/Docker/Containers/family-home-lab/portal/static/tokens.css`
Map the tokens to the app theme: canvas `#f6f5f4`, one action blue `#0075de`, Inter,
8px spacing, 4px inputs, pill buttons, hairline dividers, dark mode. The login/consent
moment is the one dark indigo band.
## 7. Data model
On the device only. No server database.
| Store | Holds |
|---|---|
| Settings | Hub URL, Hub token (encrypted), keycode hash |
| Cache | recent replies, pending approvals |
| Log | last actions, for debugging |
## 8. Integrations
| System | Use |
|---|---|
| **Hub** | `POST /ask`, approval callbacks, `/notify`. Frozen in `central_ai_llm_master/docs/hub-api.md`. |
| **Internet use control** | Admin lock, unlock, grant. Direct API call. See `internet_use_control/SYSTEM_MAP.md`. |
| **Android telephony** | Native calls and contacts. |
| **Deep links** | Opening a specific song or place. |
| **Artemis** | Not called from here. The Hub calls it. Native actions stay in this app. |
## 9. Technology (locked)
| Item | Value |
|---|---|
| Language | **Kotlin** (Android) |
| Voice | `SpeechRecognizer` first; a wake-word engine later |
| Transport | HTTP to the Hub. **LAN, or Tailscale when away** — decide in Phase 1. |
| Push | FCM, or polling for approvals — decide in Phase 4 |
| Min SDK | Decide in Phase 1. Suggest API 29 (Android 10). |
| Repo | `sam/kotlin_phone_app_admin` |
## 10. Rules and constraints
1. **Admin actions need the keycode or biometric.** Never a spoken keycode alone.
2. The app **never** holds control logic. It calls the service.
3. The Hub token lives in encrypted storage. Never in logs.
4. The app never claims success on a failed call.
5. Models go through **OmniRoute** — but this app talks to the Hub, not to models.
6. Own folder, own repo.
## 10a. Prerequisites (verify before Phase 1)
| Need | Status / note |
|---|---|
| **Android toolchain** | **MISSING on `.13`** — no `java`/`javac`, `gradle`, `kotlinc`, `adb`, `sdkmanager`. Phase 1 cannot start without a JDK + Android SDK + Gradle. Decide: install on `.13`, or build on `.51`. |
| **A device to test on** | `sams-pixel-8a` is on the tailnet (`100.101.49.17`). |
| **The Hub** | Live on `.13:8788`. Frozen API: `central_ai_llm_master/docs/hub-api.md`. |
| **Internet use control API** | **Not built yet.** Required from **Phase 5** (admin lock/unlock). Until then, stop at Phase 4. |
**Build order note:** this project depends on `internet_use_control` for its admin
half. That project is being built first. Phases 1–4 (voice + approvals) do not
need it; Phase 5 does.
**Task 1 should be: stand up the Android build environment and prove it** — JDK,
SDK, Gradle, then a hello-world app that calls the Hub's `/health` and shows the
result. No UI, no features. This turns "build an Android app" from an unknown
into a known.
## 11. Build order
**Phase 1 — Skeleton and settings**
Empty app, settings screen, Hub URL + token, test connection.
*DoD:* the app reaches the Hub and shows its health.
**Phase 2 — Voice in**
Hold-to-talk, send to the Hub, show the text reply.
*DoD:* a spoken question returns a Hub answer on screen.
**Phase 3 — Voice out**
Play the reply as TTS on the phone.
*DoD:* the phone speaks the Hub's reply.
**Phase 4 — Approvals**
Fetch pending approvals, show the card, approve or deny.
*DoD:* a real "did you ask for this?" from the Hub is answered from the phone.
**Phase 5 — Admin**
List kids' devices via the internet control API. Lock, unlock, grant.
*DoD:* a real device locks in under one second from the app.
**Phase 6 — Security gate**
Keycode or biometric before admin actions.
*DoD:* admin actions are blocked without the gate.
**Phase 7 — Notifications and deep links**
Hub `/notify` as notifications. Native actions and deep links.
*DoD:* a Hub notification arrives, and a deep link opens the right song.
**Phase 8 — Wake word (optional)**
Replace hold-to-talk with a wake word.
*DoD:* the wake word starts listening without touching the phone.
**Phase 9 — Documentation**
*DoD:* project-docs run; docs updated.
## 12. Acceptance criteria
- Sam asks a question by voice and hears the answer on the phone.
- An approval prompt can be answered from the phone.
- A kids' device can be locked from the app in under one second.
- Admin actions require the keycode or biometric.
- A Hub notification arrives on the phone.
- The app never reports success after a failure.
## 13. Decisions and remaining questions
Resolved:
1. **One app for Sam** (Hub + Admin). The kids' client is under `internet_use_control`.
2. **Kotlin**, Android.
3. Admin calls the internet control API directly. No Hub in that path.
Remaining:
1. **Hold-to-talk first, wake word later** — agree?
2. How does the app reach the Hub: LAN only, or Tailscale when away?
3. Approvals by FCM push, or polling?
4. Locked-screen behaviour: can approvals be answered from the lock screen?
5. Min SDK version.