0019. Services describe their screens
Status: accepted, 2026-10-21
Context
Full parity in two native apps (0018) covers about 680 routes in
nineteen services, most of them configuration: secret sets, backup plans, DNS records, hooks, tunnels,
shares, aliases, mailboxes, tokens. The panels already treat those uniformly (pages is the quality bar: a
filtered table, a row menu, a detail sheet, a form). Built by hand twice they would cost months, lag every
panel change, and need a third copy for any other client. Generated API clients remove the typing, not the
screens.
Decision
- Every service serves
GET /api/v1/ui: a JSON document in the format xvii-ui/1 describing its
resource kinds as lists, details, forms and actions over its own operations (the operation ids of its
contract), with labels in English and Russian. It carries structure only: no data, no secrets, and it
can name only its own service's operations.
- A fixed vocabulary: sixteen components (three screens and thirteen sections), a fixed list of value
formats and form widgets, lucide icon names from an allow-list. A component joins only when three
services need it. Within
xvii-ui/1 changes are additive and clients ignore what they don't know,
falling back to the panel; a breaking change is xvii-ui/2, served side by side.
- Written in Go next to the operation registry (
kit/ui) and compiled with it, so operations and their
screens change in one commit. A service's tests fail when its document names a missing operation or
field, lacks a translation, lowers an operation's risk, or leaves an operation a person can call
uncovered: drawn as a screen or action, or marked as covered by a hand-built native screen or as handed
off to the panel. Parity is enforced where operations are added.
- Rendered natively by both apps. Hand-built screens may replace any kind's screens and still list the
kind's other actions from the document. Actions run with the person's token under the same RBAC and
approvals as anywhere; risk drives the confirmation.
- Golden fixtures in kit (
kit/conformance) define how a document plus responses must lay out; kit's
reference resolver in Go writes them, and the Swift and Kotlin resolvers must match.
Consequences
- Long-tail screens reach the phones with the service's release, without an app release.
- Generic screens look generic: right for configuration, wrong for chat, logs, a terminal or deploys,
which stay hand-built.
- Each service maintains one more contract, versioned like its API, a few kilobytes in its binary.
- The same documents can later drive other clients (home's hub, the Telegram Mini App) through a React
renderer; that is not decided here.
- Design:
superpowers/specs/2026-10-21-native-apps-design.md in this repository.