docs(agents): add repository development guidance

This commit is contained in:
2026-10-06 18:49:44 -03:00
parent 1146dec1ac
commit 8cc080998f
+239
View File
@@ -0,0 +1,239 @@
# Repository guidance
## Scope and working conventions
This repository is Vocero: a browser voice client, its FastAPI integration
server, a speech pipeline, and optional Node-RED flows. Use the code, commands,
configuration, and documentation within this checkout. Do not assume other
repositories or developer-specific directories are available.
All file and directory references in this guide are relative to the repository
root. HTTP routes mentioned below are URLs, not filesystem paths.
- Check the current branch, worktree, and working-tree changes before editing.
Honor the checkout requested by the user, including direct work on `main`.
Use an isolated worktree when requested and preserve other worktrees.
- An analysis-only or planning request authorizes inspection, not implementation.
Return findings with source locations. Present plans in the conversation
unless the user asks to save a file; follow any explicit approval gate.
- Write clear explanations and ask one conceptual question at a time. Carry out
already authorized work without repeatedly asking for permission.
- Use the native patch/edit tool for manual changes. Prefer `rg`, `fd`, `jq`,
`git`, and standard CLI tools for inspection; check availability before
assuming a tool is missing. Do not install system-wide packages unnecessarily.
- When commits are requested, make coherent, scoped Conventional Commits as work
progresses. Avoid excessive micro-commits and unrelated cleanup.
- Invoke `work-on-feature` only when explicitly requested. For a requested PR,
follow the available `pr-description` skill exactly and put verification
details in the handoff when its format excludes them. Issues are optional;
create native Gitea issues or post comments only when requested.
- When asked to merge locally, use the requested rebase/fast-forward flow;
preserve a recovery ref before rewriting and verify retained patches. A local
merge does not imply a push, deployment, or restart. Report those separately.
- Treat old conversations and proposals as context. Verify claims against the
current code and distinguish proposed authentication/deployment changes from
implemented behavior.
## Project map
| Location | Responsibility |
| ---------------------------------- | ------------------------------------------------------------------------------------------- |
| `s2s-client/` | The complete uv project: manifests, environment, Python tooling, tests, and FastAPI package |
| `s2s-client/s2s_client/main.py` | Application assembly, documentation mount, and retained redirects |
| `s2s-client/s2s_client/api/` | HTTP routes, route registration, and response validation helper |
| `s2s-client/s2s_client/models/` | Pydantic request and response contracts |
| `s2s-client/s2s_client/core/` | Environment parsing, logging, public URLs, and startup diagnostics |
| `s2s-client/s2s_client/providers/` | ApiPilot and caller/profile/report HTTP integrations |
| `s2s-client/s2s_client/services/` | Prompt/context construction, reasoning, and tool-context rendering |
| `s2s-client/s2s_client/realtime/` | WebSocket relay, event handling, marker filtering, and audio forwarding |
| `s2s-frontend/` | React, TypeScript, Tailwind, and Vite browser application |
| `speech_to_speech/` | Separately run VAD → STT → LLM → TTS pipeline and realtime API |
| `s2s-reasoning-engine/` | Optional Node-RED reasoning/logging service |
| `start_ui.sh` | Dependency sync and supervision of FastAPI plus Vite |
| `docs/` | Maintained technical documentation, examples, and historical proposals |
`s2s-client/server.py` is a compatibility shim for `server:app`; new backend
implementation belongs in the package. Keep HTTP handlers focused on transport
and reuse providers/services for shared behavior.
The old static browser site, daily talk-time quotas, quota database/accounting,
and Hugging Face account UI were deliberately removed. Do not restore their
files, routes, dependencies, or compatibility layers as incidental cleanup.
Load-balancer allocation/queues and upstream caller authentication remain.
## Tooling and checks
The uv project must stay inside `s2s-client/`, including
`s2s-client/pyproject.toml`, `s2s-client/uv.lock`, `s2s-client/.python-version`,
`s2s-client/.venv/`, `s2s-client/Makefile`, and
`s2s-client/.pre-commit-config.yaml`.
Use Python 3.12 and the uv version required by `tool.uv.required-version`
(currently 0.12.5). Update dependencies through uv and commit the lockfile;
do not use pip to modify this environment or move it to the repository root.
Keep uv pins in `s2s-client/Dockerfile` and setup documentation aligned when
changing the required version.
The frontend uses npm and `s2s-frontend/package-lock.json`. Its Node requirement is
`^22.18.0 || >=24.0.0`; consult `s2s-frontend/package.json` for current scripts.
Keep application code and supported configuration files in TypeScript.
Commands below run from the repository root:
| Command | Purpose |
| ------------------------------------- | --------------------------------------------------------------------- |
| `make -C s2s-client install` | Sync the locked Python environment and development tools |
| `make -C s2s-client format` | Apply Ruff fixes and formatting |
| `make -C s2s-client check` | Check lock freshness, formatting, Ruff lint, mypy, and pytest |
| `make -C s2s-client test-launcher` | Test readiness, failures, dependency sync, and process-group shutdown |
| `make -C s2s-client test-image` | Build and smoke-test the actual backend Docker image |
| `npm --prefix s2s-frontend ci` | Install locked frontend dependencies |
| `npm --prefix s2s-frontend run check` | Check formatting, lint, TypeScript, Vitest, and launcher integration |
| `npm --prefix s2s-frontend run build` | Type-check and build frontend assets |
Python uses Ruff with a 120-character line limit, double quotes, import ordering,
and typed application functions checked by mypy. Frontend formatting and lint
rules live in `s2s-frontend/prettier.config.ts` and
`s2s-frontend/eslint.config.ts`; use those tools rather than introducing
competing conventions.
Run checks appropriate to the change: backend checks for Python/API work,
frontend checks and build for browser work, launcher integration for supervisor
changes, and image tests for packaging/Docker changes. Documentation-only work
normally needs link/path and diff checks, not service startup. Wait for commands
to finish before reporting success, and state skipped coverage or blockers.
Tests should use mock upstream HTTP and local WebSocket fixtures, isolating
environment configuration. Do not rely on deployed services, real credentials,
or model downloads for client tests. The client gate does not test the separate
speech pipeline, microphone hardware, or real model output. That pipeline has
no complete dependency manifest or general test gate in this checkout; inspect
the selected backend and explain the verification limits for speech changes.
## API and compatibility rules
Read [backend development and compatibility](docs/client/development.md) before
refactoring the server or changing schemas. Existing wire behavior is the
baseline, including behavior that looks unsafe, inconsistent, or buggy.
Intentional bug fixes or contract changes need their own scope and regression
evidence; do not bundle them into tooling or structural refactors.
- Preserve paths, methods, statuses, error shapes, headers, field names,
omissions, coercion, authentication precedence, and outgoing provider calls.
Trace both frontend consumers and provider parsing; a permissive consumer is
not evidence that an upstream schema can safely be tightened.
- Retain existing request-model defaults and extra-field behavior. Do not make
new fields required, forbid previously accepted extras, or normalize loosely
parsed provider values without evidence and an intentional behavior change.
- Response models validate fields constructed by this service. Upstream-owned
values remain permissive where the old parser was permissive: `Any`, optional
fields, open objects, and arbitrary profile JSON are deliberate boundaries.
- `validated_json_response` in `s2s-client/s2s_client/api/responses.py` validates
with Pydantic, then serializes the original payload. Do not substitute
`model_dump()` or automatic response-model serialization: those can insert
defaults, filter unknown keys,
coerce values, or alter serialization. Keep OpenAPI declarations aligned with
the actual validation boundary.
- Preserve environment/dotenv parsing, default flags, and import-time behavior
during refactors. Do not introduce stricter configuration validation or change
authentication behavior as part of a tooling change.
- `s2s-client/tests/fixtures/legacy-contracts.json` captures the server before the
migration at `3d42df5`. `s2s-client/tests/test_compatibility.py` compares HTTP
response bytes, statuses, selected headers, outgoing requests, request schemas,
and realtime frames. Do not regenerate captures merely to make a refactor pass.
- Known reasoning failures, ApiPilot authentication/cache quirks, and relay
behavior are documented in the compatibility guide. Preserve narrow lint/type
exceptions that retain those behaviors until a deliberate fix is in scope.
- The pytest warning filter is only for Starlette TestClient's exact deprecated
AnyIO `BlockingPortal` alias. Do not broaden warning suppression or upgrade
runtime frameworks just to silence that warning without a compatibility review.
## Browser and realtime behavior
- React renders the UI; `s2s-frontend/src/realtime/controller.ts` owns observable
application state, `s2s-frontend/src/realtime/client.ts` owns protocol/session
lifecycle, and `s2s-frontend/src/audio/` owns the audio graph and worklets.
Keep high-frequency audio/WebSocket work outside React rendering and preserve
cleanup across unmounts and reconnects.
- Preserve the existing 16 kHz PCM16 audio path, worklet registration, interruption
behavior, and teardown. Session startup initializes audio, waits for
`session.created`, and sends `session.update` before audio or the greeting.
A `session.updated` acknowledgment is optional; requiring it previously broke
startup with this speech service.
- Preserve query parameters such as `phoneNumber` and `episode_id`, existing
`s2s.ws.*` preference keys, session-only RAG, and TALK/TALKFIRST behavior unless
the task explicitly changes them. Context comes from browser instructions and
server/provider calls; do not assume a vector database or a completed magic-link
authentication flow exists.
- Assistant Markdown is parsed with Marked and sanitized with DOMPurify in
`s2s-frontend/src/lib/messages.ts`. User text stays literal, and exports retain
raw message text. Keep sanitization and unsafe-link regression coverage when
changing it.
- Reuse the existing design tokens, Tailwind integration, and
`s2s-frontend/src/styles/legacy-theme.css`. Verify both light and dark themes,
narrow screens, keyboard focus, dialog dismissal, and voice-state feedback for
UI changes.
Preserve intentional `?debug=true` controls and browser audio-permission flows.
- User-visible times use `America/Santiago` where no saved zone exists; avoid
browser-local timezone assumptions. Keep raw timestamps in their existing API
and export formats.
## Startup and deployment boundaries
`./start_ui.sh` sources `.env`, syncs `s2s-client/.venv/` with
`uv sync --project s2s-client --locked`, and installs frontend packages when the
manifests change.
It starts Vite on `0.0.0.0:8420` and FastAPI on `127.0.0.1:8421`, with
`UI_HOST`, `UI_PORT`, and `S2S_BACKEND_PORT` overrides. Use distinct ports for
concurrent worktrees. Preserve readiness checks, failure propagation, and
shutdown of both process groups, including Uvicorn's reload worker.
Staging intentionally retains Vite HMR and Uvicorn reload. The Vite child uses
`NODE_ENV=development` even under PM2. Do not replace this with static production
serving unless that deployment change is requested. Vite proxies `/api`,
`/v1/realtime`, and documentation routes to FastAPI; preserve public Host headers
used to construct WebSocket URLs. Remote staging needs explicit
`S2S_DEV_ALLOWED_HOSTS` hostnames and reverse-proxy WebSocket forwarding for both
`/v1/realtime` and `/__vite_hmr`.
The established PM2 process name is `s2s_ui`. When it points to this launcher's
deployed checkout, `pm2 restart s2s_ui --update-env` also performs the locked uv
sync; a separate sync is optional to prepare dependencies before restarting.
Ensure the required uv and Node/npm are on that process's PATH. Verify the live
PM2 script path and environment before making claims about a deployment.
The launcher does not start or restart `speech_to_speech/` or Node-RED. A configured
speech URL does not prove which source revision or implementation runs there.
`SPEECH_TO_SPEECH_URL` pins a direct browser connection; `S2S_PROXY_TARGET` enables
the local relay. Either disables load-balancer mode. The Node-RED flows in
`s2s-reasoning-engine/flows.json` are log-only; preserve the meaning of `log_only`
responses.
The backend Docker image serves APIs, WebSockets, and root `docs/`, not the React
UI. Build with the repository root as context and `s2s-client/Dockerfile`; the
image listens on 7860. FastAPI's root remains 404, `/docs` is the API explorer,
and project documentation is served below `/docs/` with retained legacy redirects.
## Language, documentation, and secrets
- Use English for code, identifiers, comments, logs, internal instructions, and
developer documentation. Spanish is the default for visible UI, spoken output,
and user-facing API messages. Preserve literal wake phrases and protocol names;
translating an existing response or prompt can change a contract.
- Keep maintained documentation under `docs/client/`, `docs/speech/`, `docs/adr/`,
`docs/examples/`, or `docs/proposals/`. Update existing documents first and keep
`docs/README.md` current when adding a document. Use one canonical English file
without language suffixes. Historical proposals do not define current behavior.
- Reserve root Markdown for established files such as `README.md` and `AGENTS.md`.
Keep task summaries in the conversation unless a saved artifact is requested.
Record notable changes under `Unreleased` in `s2s-client/CHANGELOG.md`.
- Keep credentials and deployment values out of commits and examples. Never copy
the root `.env` into the frontend or expose backend secrets through `VITE_*`.
The launcher sources `.env` before startup, so it can override shell values;
direct Python startup uses `load_dotenv(..., override=False)`. Preserve both
behaviors and avoid importing live configuration when a test can isolate it.
Start with [usage](docs/client/usage.md), [architecture](docs/client/architecture.md),
[development](docs/client/development.md), and [troubleshooting](docs/client/troubleshooting.md).
For speech work, read [the realtime protocol](docs/speech/realtime-api.md) and the
relevant [STT](docs/speech/stt.md), [LLM](docs/speech/llm.md), or
[TTS](docs/speech/tts.md) guide before changing pipeline behavior.