AGENTS.md¶
Rules for everyone who changes this repository, people and coding agents alike. Read this before your first change. CONTRIBUTING.md covers the workflow and docs/ONBOARDING.md covers setting up a machine.
What textwire is¶
A text-mode web for a phone with no data connection. The phone sends a request as an SMS to a number the server owns. The server fetches the page or runs the search on the real internet, reduces it to text, compresses it, and sends it back as numbered SMS frames that the Android app reassembles and renders. docs/ARCHITECTURE.md has the full picture and protocol/PROTOCOL.md has the wire format.
Layout¶
| Path | What lives there |
|---|---|
protocol/ |
The language-neutral contract: PROTOCOL.md, golden vectors, the zstd dictionary, shared fixtures. |
server/ |
The Python server: protocol reference implementation, content pipeline, transports, dispatcher, simulator, CLI. |
android/ |
The Kotlin app: :protocol and :core (pure JVM) and :app (Android, Compose). |
docs/ |
Onboarding, architecture, operations, testing, acceptance and the ADRs. |
Commands¶
| Command | What it does |
|---|---|
make doctor |
Checks this machine for every tool the repository needs and says how to fix what is missing. |
make check |
Everything CI runs: server gates, vector drift, Android gates. |
make simulate |
Runs the whole system in the terminal with a virtual phone and recorded pages. No Twilio, no phone. |
make -C server check |
Server only: ruff, mypy strict, import contracts, tests at 100% branch coverage. |
make vectors |
Regenerates the golden vectors from the Python reference. |
cd android && ./gradlew check |
Android only: ktlint, unit tests, coverage floors. |
Invariants¶
- Honesty about hardware. Never claim a result on a real phone or a real Twilio number that was not observed. Device and network claims live only in docs/ACCEPTANCE.md, each with a date and a cost.
- The wire format is a contract. A change to frames, requests, envelopes, codecs or
the dictionary regenerates the vectors (
make vectors), editsprotocol/PROTOCOL.md, and passes both languages' vector tests in the same pull request. - No test sends a real SMS unless it is marked
live.make checkpasses with no network, no Twilio account and no phone. - Every seam that touches the outside world is an interface with a hand-written fake:
Transport,Fetcher,SearchProvider,Clockon the server;SmsGateway,Clockand the stores on the phone. No mocking libraries on the server. - Secrets come from the environment only.
server/.envis gitignored. Logs never carry an auth token and show phone numbers masked (+44...1234). - Coverage floors are never lowered. The server, the Kotlin
:protocoland:coremodules and the app's logic are held at 100% line and branch coverage; the app's Compose screens at 99% of lines (ADR-0009).pragma: no coverfails review and a test. - Money is an operator setting. Never raise
TEXTWIRE_DAILY_SEGMENT_BUDGETor the page size defaults in code to make something pass. Every outbound SMS is charged to the budget before it is sent. - Files stay under 800 lines. A test enforces it for Python; review enforces it for Kotlin.
- The Android app stays small: no dependency-injection framework, no serialization library, no HTTP client. It never touches the network.
- Run the full test suites in the background while you work and never edit source while coverage is being measured: coverage reports stale line numbers against the edited file.
Where to put things¶
- A new request verb:
server/src/textwire/protocol/requests.py, the handler, the KotlinRequestFormat, the vectors,PROTOCOL.md, and the help text. - A new error: a stable
E <area> <detail>string in the handler, listed inPROTOCOL.md. - A new transport: implement
textwire.transport.base.Transport, add a fake-backed contract test, and document it in docs/OPERATIONS.md. - A decision that someone could reasonably have made differently: an ADR in
docs/adr/.