# AGENTS.md — D3RO Voice Operating guide for any AI agent (or human) working in this repository. Read this first. It tells you where the infrastructure map lives and what you must update when you change the product. --- ## 1. What this repo is `D3RO Voice` is a multi-platform AI voice assistant monorepo: - `apps/desktop` — Electron desktop app (local-first: SoX + faster-whisper sidecar + Ollama + SQLite) - `apps/web` — Next.js cloud console - `apps/mobile-rn` — React Native product mobile app (cloud-first; **active**) - `apps/api-server` — ASP.NET Core 10 AI proxy + back office backend - `apps/admin` — Next.js admin back office - `server/supabase` — Postgres + RLS + Auth + Storage + ~27 Deno Edge Functions (canonical product backend) - `server/cloudflare-worker` — edge gateway - `site/` — landing/marketing/legal site - `packages/*` — shared `@d3ro/core`, `ui`, `ui-native`, `i18n`, `api-client` Full detail: [`docs/map/01-system-overview.md`](./docs/map/01-system-overview.md). --- ## 2. The map — read before you work **`docs/map/` is the authoritative feature & infrastructure map.** Use it to learn what infrastructure exists and how far each feature is developed before writing code. Start at [`docs/map/00-index.md`](./docs/map/00-index.md). Key files: | Need | File | |---|---| | Map index + status legend + how to use | `docs/map/00-index.md` | | Product / IA / identity / pipeline | `docs/map/01-system-overview.md` | | Repo layout, build, CI/CD, Docker, deploy | `docs/map/02-infrastructure.md` | | Shared packages | `docs/map/03-shared-packages.md` | | Desktop services/IPC/pages | `docs/map/04-desktop-app.md` | | Web routes/components | `docs/map/05-web-app.md` | | Mobile screens/features | `docs/map/06-mobile-app.md` | | .NET API | `docs/map/07-api-server.md` | | Admin console | `docs/map/08-admin-console.md` | | Supabase + Cloudflare | `docs/map/09-supabase-backend.md` | | **Feature map (per platform, with status)** | `docs/map/10-feature-catalog.md` | | **Gaps / backlog / external blockers** | `docs/map/11-gap-backlog.md` | | **How to keep the map current** | `docs/map/12-update-protocol.md` | Recommended sequence for a new task: 1. Read the surface doc for the area you will touch (`04`–`09`). 2. Find the feature in `10-feature-catalog.md` and check its status per platform. 3. Check `11-gap-backlog.md` so you do not re-plan tracked work. 4. Do the work. 5. Update the map **before** declaring done (see §4). Mobile status is authoritative in `docs/v3/MOBILE_APP_COMPLETION_SSOT.md`; the map mirrors and rolls it up. When they disagree, fix the SSOT first, then the map. --- ## 3. Non-negotiable repo rules These come from [`CLAUDE.md`](./CLAUDE.md) and are enforced here too: - TypeScript strict, no `any`, no `console.log` (use `logger`). - Services are singletons + `EventEmitter`. IPC channels are `${feature}:${action}` from the SSOT `packages/core/src/ipc-channels.ts`; wrap errors in `D3ROError`/`ErrorCode` and `IPCResult`. - Theme SSOT: only `d3roPalette`/`d3roShadow`/`d3roTypo`/`d3roRadius`; no hardcoded hex outside `theme.ts`. - i18n: use `t()`; no hardcoded Korean/English strings in UI. - No `Co-Authored-By` or Claude-related wording in commits. Follow the commit format in the shell tool instructions. - **Desktop GUI**: do not background-launch Electron from an agent subshell (virtual desktop). Tell the user to run `run-desktop.bat` or an external terminal. - **Audio device discovery**: never `execSync` (use async `exec`). - **GPU**: keep `app.disableHardwareAcceleration()` and `--disable-gpu`. - Desktop: keep `app.setName('d3ro-voice')` + `app.setAppUserModelId` at the top; prevent dev silent exit. - Never hardcode secrets; production secrets are injected at build/CI time. Fail closed, never fake success. - Security-first: only defensive security work. Build/test commands: [`docs/map/02-infrastructure.md`](./docs/map/02-infrastructure.md) §3. Root: `npm run typecheck`, `npm run test`, `npm run lint`. Mobile: `npm --prefix apps/mobile-rn run lint|typecheck|test`. --- ## 4. MANDATORY: keep the map updated > **Feature change ⇒ map change, in the same commit / PR.** > A feature is not done until the map reflects it. Whenever you **add, remove, change, or defer** a feature, or touch infrastructure, you MUST update the map: 1. Edit `docs/map/10-feature-catalog.md` (status per surface + notes/anchors). 2. Edit the relevant surface doc (`04`–`09`) if you changed services, routes, screens, IPC channels, tables, Edge Functions, or build/CI. 3. Edit `docs/map/11-gap-backlog.md`: - resolved gap → `[x]` + date + evidence; - newly deferred item → new row + next step; - cleared external blocker → `[x]` + evidence. 4. Reconcile with `docs/v3/MOBILE_APP_COMPLETION_SSOT.md` for mobile changes. 5. Update `docs/map/02-infrastructure.md` and the `00-index.md` header dates when build/release/CI or the product version changes. Full procedure and templates: [`docs/map/12-update-protocol.md`](./docs/map/12-update-protocol.md). **Status semantics:** `[x]` verified · `[~]` partial/unverified · `[ ]` planned/absent · `[!]` blocked externally · `[-]` N/A. Never mark `[x]` on typecheck alone. --- ## 5. Definition of done (feature work) - [ ] Code implemented, wired end-to-end (no TODOs / placeholders / fake success). - [ ] Tests added/updated and passing; lint/typecheck clean on the touched scope. - [ ] Relevant surface doc (`04`–`09`) updated if interfaces changed. - [ ] `10-feature-catalog.md` row updated (status per platform + anchors). - [ ] `11-gap-backlog.md` updated (closed/opened/external). - [ ] Mobile SSOT reconciled if mobile was touched. - [ ] No secrets committed; fail-closed preserved. --- ## 6. Update & release system Desktop auto-update runs `electron-updater` against the **canonical Forgejo feed** `https://git.chanpaca.net/api/packages/yunchan/generic/d3ro-voice/latest`. The GitLab project 1172 registry is a **legacy mirror** for pre-Forgejo installs; runtime must never point at it. Full evaluation and 2026 methodology: [`docs/deployment/update-system-assessment.md`](./docs/deployment/update-system-assessment.md). These files move together and are enforced by `npm run release:metadata:test`: | Concern | SSOT | |---|---| | runtime feed + channels | `apps/desktop/src/main/update-feed.ts` | | builder publish URL | `apps/desktop/electron-builder.yml` | | update policy (channels, min version, force, delta/full, rollout, kill switch) | `release/update-policy.json` | | policy runtime logic | `apps/desktop/src/main/update-policy.ts` | | canonical publisher | `scripts/ci/publish-forgejo-release.mjs` | | legacy mirror publisher | `scripts/ci/publish-gitlab-release.mjs` | | version SSOT | `release/product-version.json` | Rules: - **Tag-only, immutable releases.** Publish runs only on annotated `vX.Y.Z` tags. Tags are never moved or deleted; a bad release is fixed forward with a higher patch. Same-version re-release is forbidden by SemVer. - **Feed contract.** `UPDATE_FEED_URL` == builder `publish.url` == canonical Forgejo URL. `LEGACY_UPDATE_FEED_URL` must stay the GitLab mirror. - **Version discipline.** SemVer: `feat`=MINOR, `fix`=PATCH, breaking=MAJOR. Run `npm run version:sync` when syncing surfaces; `npm run version:check` must be clean. - **Channels.** `latest` (stable), `beta`, `alpha`. Stable clients never receive a prerelease unless their channel allows it. - **Major vs incremental.** `release/update-policy.json` decides whether an update is mandatory (`minimumSupportedVersion`, `forceInstallBelow`) and whether to use a full installer instead of a blockmap delta (`fullInstallOnMajorChange`, `fullInstallVersionGap`). `stagingPercentage` limits rollout; `killSwitch` stops update checks remotely. - **Secrets stay fail-closed.** Every publisher needs `FORGEJO_TOKEN` (`write:package` + `write:repository`). Desktop releases additionally require the `WIN_CSC_*` Authenticode material; missing signing data must fail the pipeline. Commands: `npm run release:metadata`, `npm run release:metadata:test`, `npm run release:forgejo:check`, `npm run release:tag -- --dry-run` (then `npm run release:tag`, then `git push origin vX.Y.Z`). Use `--sign` when GPG is configured; tags are annotated and immutable. Locally, `npm run release:forgejo:local` loads `FORGEJO_TOKEN` from `.env` (gitignored); CI reads the secret from its store. --- ## 7. Where deeper context lives (optional) - Design (historical desktop architecture): `docs/design/*` - Build-out phases: `docs/phases/*` - Multi-platform plan: `docs/v2/*` - Mobile SSOT + Play package: `docs/v3/*` - Monetization: `docs/monetization-plan.md` - Deployment/release: `docs/deployment/*`, `release/*`, `scripts/ci/*` - Refactor policy + reports: `docs/REFACTOR_POLICY.md`, `docs/REFACTOR_WAVE*_REPORT.md` - Project log/handoffs: `memory/*`, `CHANGELOG.md` Treat those as history; treat `docs/map/*` and the code as current.