Agents are the only end users
ADR-2026-0004: Agents are the only end users
Section titled “ADR-2026-0004: Agents are the only end users”Context and problem statement
Section titled “Context and problem statement”Citizenry shipped with two SvelteKit Pages projects:
apps/web- a user-facing surface scaffolded as a public landing / account page.apps/admin-web- an operator-facing console for inspecting the deployment.
In practice, the users of a citizenry deployment are AI agents, not
humans. Agents enroll, authenticate, and call the service through
apps/api and apps/mcp. They
never load HTML. The only humans who legitimately need a browser
session are the operator running the deployment - and their needs
(observe state, manage admin credentials, audit activity) are served
by apps/admin-web, which authenticates against its own admin
ID/password independent of the agent identity model.
apps/web therefore had no real consumer. Keeping it cost CI build
time, a Pages project on every adopter’s Cloudflare account, a slot in
the deploy matrix, and a piece of public surface area that invited
contributors to design human user flows that the product does not
have. Worse, its presence implied a user model - accounts, sessions,
sign-in - that is actively at odds with the agent-native design and
would mislead readers about who the system is for.
We need a clear, recorded decision so future contributors do not “complete” the empty user web or treat its absence as a gap.
Decision drivers
Section titled “Decision drivers”- Product surface honesty. End users are agents. A scaffolded human-user web app misrepresents the product and creates a magnet for off-strategy contributions.
- Adopter deploy cost. Every Pages project a fork ships adds to Cloudflare resource count, deploy time, and the surface adopters have to reason about. Smaller is better when the project provides nothing.
- Admin separation. Operators need an observability/control console. That console authenticates with its own admin credentials and is unrelated to agent identity - folding it into the “user web” story would have conflated two very different access models.
- Reversibility. Removing an app that has no consumers is cheap and reversible (any future need can scaffold a fresh app); leaving it costs every deploy and every reader’s attention indefinitely.
- Spec / documentation drift. Public docs and the layout sections
of
README.md/CLAUDE.mdmust reflect reality, or new contributors form a wrong mental model on first read.
Considered options
Section titled “Considered options”- Keep
apps/webas an empty scaffold. Reserve the slot in case a human-user surface emerges later. - Repurpose
apps/webas the admin console and retireapps/admin-web. Single SvelteKit Pages project covers everything. - Delete
apps/web; keepapps/admin-webas the ops-only console (chosen). Make explicit that agents use api/MCP and only operators use a browser.
Pros and cons
Section titled “Pros and cons”1. Keep empty web | 2. Merge into one web | 3. Delete web, keep admin-web (chosen) | |
|---|---|---|---|
| Honest about the product | ✗ implies a human user flow that doesn’t exist | ✗ same scaffold encourages human user features alongside admin | ✓ shape of the repo matches the access model |
| Adopter resource count | one extra Pages project per fork | reduced from two to one - but admin URL now public-looking | reduced from two to one; admin URL clearly operator-facing |
| CI cost | builds + deploys an empty app every run | one build (and one project) | one build (and one project) |
| Contribution magnet | ✗ “make the user web do something” PRs | partial - invites mixing user and admin concerns | ✓ admin scope is unambiguous |
| Migration cost | none today, debt later | rename + redirects + auth-model rework | trivial - remove app + cleanup CI / docs |
| Reversible if a human surface ever appears | n/a - already kept | requires re-splitting | scaffold a fresh app from apps/admin-web as a template |
Decision
Section titled “Decision”We chose option 3: delete apps/web and keep apps/admin-web as
the only browser-facing Pages app for citizenry itself
(apps/docs continues to ship the public docs site, unchanged). The
agent-facing surface remains apps/api + apps/mcp.
Concretely:
apps/web/is removed from the repository.@citizenry/webis removed from the build, the Pages deploy matrix, and thedeploy-pages.shdispatcher.- The architecture / “what gets deployed” tables in
README.md,CLAUDE.md, anddocs/deploy.mddrop thecitizenry-webrow and reframe the layout as “admin console + API/MCP for agents.” apps/admin-webis documented as an ops-only console authenticated by its own admin ID/password, distinct from the per-agent identity model.
Consequences
Section titled “Consequences”Positive
Section titled “Positive”- Repository layout is honest about the product: agents talk to the service through api/MCP; humans (operators) use admin-web.
- One fewer Pages project per fork → smaller adopter footprint,
shorter deploy time, less to explain in
docs/deploy.md. - Eliminates the contribution magnet for “complete the user web” PRs that would have pulled scope away from the agent-native design.
- The admin/agent boundary is now visible in the file tree, not just in code comments.
Negative / cost
Section titled “Negative / cost”- Anyone with a fork that depended on the
apps/webscaffold (none observed in the open-source flow today) has to remove their fork’s Pages project manually. Stalecitizenry-web.pages.devdeployments on existing adopter accounts are left orphaned until the operator deletes them - there is no CI step that prunes Pages projects on removal. - The historic
ADR-2026-0002namesapps/webalongsideapps/admin-webas the example justifying Pages support in the deploy pipeline. That ADR is preserved unchanged as a historical record; this ADR narrows the example post-hoc. The decision in 0002 still holds - Pages support is required foradmin-webanddocs. ADR-2026-0001§Implementation mentionsapps/webin PR-6 as the host for the public error-code catalog. That responsibility now belongs toapps/docs(or the docs site of the operator’s choice); the catalog content is unchanged.
Neutral
Section titled “Neutral”- No spec or wire-protocol change.
apps/webhad no API contract; removing it does not affect any token, JWKS, MCP, or admin-api route. pnpm-lock.yamlregenerates on the nextpnpm installto drop theapps/webworkspace entry.
Implementation
Section titled “Implementation”Implemented in this commit:
- Remove
apps/web/from the repository. - Drop
@citizenry/webfrom.github/workflows/build.yml(build step + artifact path). - Drop
webfrom the deploy-pages matrix and from the summary table in.github/workflows/deploy.yml. - Remove the
web)case fromscripts/ci/deploy-pages.shand the usage / error strings. - Update
README.md,CLAUDE.md, anddocs/deploy.mdto drop thewebrow from the layout / architecture / “what gets deployed” tables and to describeadmin-webexplicitly as an ops-only console. - Land this ADR.
Compliance / follow-up
Section titled “Compliance / follow-up”- If a future need for a public, human-facing surface emerges (a
marketing site, a landing page, a status page), scaffold a fresh
app under
apps/and write a new ADR establishing its scope and audience. Do not resurrect the emptyapps/webslot as a catch-all. - The error-code catalog (referenced by
ADR-2026-0001PR-6) now lives inapps/docs. When that PR lands, route/errors/[code]fromapps/docsrather thanapps/web. - Adopters with an existing fork should delete the orphaned
citizenry-webPages project from their Cloudflare account after pulling this change. We do not auto-prune Pages projects.
References
Section titled “References”apps/admin-web- surviving operator consoleapps/api,apps/mcp- the agent-facing surfaceADR-2026-0001- error-code catalog, originally routed viaapps/webADR-2026-0002- Pages support in the deploy pipeline (historical context; decision unchanged)