---
{
  "n": 30,
  "title": "The viewer has one API client, and every piece of it does one job",
  "abstract": "",
  "refs": [],
  "seen": [
    "agent",
    "user"
  ],
  "data": {},
  "created": 1789960834.31165,
  "updated": 1790167457.54352,
  "deleted": 0.0,
  "completed": 0.0,
  "outcome": "",
  "type": "rule"
}
---
For building web/src in this project:
- Nothing is written twice. A second copy of logic, markup or style becomes one component, composable or module.
- One funnel for everything. web/src/api/transport.js is the only place fetch is called, and it holds the rule of one request per endpoint in flight. web/src/api/client.js is the only place an endpoint, URL or EventSource is written: one method per endpoint. scripts/checks/one_client.py fails on a path written anywhere else. Page routes belong to route.js.
- Components talk to the API client. The store holds state and does not fetch, poll, page or decide what to refetch.
- Single responsibility: a poller polls, a paginator pages, the client talks to the server, the store holds state, a component renders. Reusable behaviour (dropdowns, remembered UI state, scroll-to-load, a ticking clock) is a composable, written once.
- Pages register their own polls through usePoll, which pauses while the tab is hidden. A listing is one page of 25, loaded lazily. What is already loaded is never fetched again without an event naming it.
This binds only the development of the agent journal itself. It is not injected into projects that install the journal.
