ADR-0074: Forms and dashboards in artifactr.ui, behind the forms extra¶
Status: Accepted Date: 2026-10-17 Deciders: Alex Nodeland
Context¶
lattice RFC-0005 puts forms, dashboards and, later, mini apps in the chat: a JSON Schema subset and its validator, four artifact types, an agent toolset, and the checks that refuse a bad submission. lattice ADR-0026 decided that they are an extra of artifactr-ai, a subpackage the rest of artifactr never imports, and left the extra's name and import path to where they are built. This records them, and what follows from them.
Decision¶
- The module is
artifactr.ui, a layer of artifactr's:schema(the subset and its hints),types(theform,submission,dashboardandappkinds, andask_form's request),ids(a submission's derived id),integrations(which are on in a workspace, ADR-0080),checks(ADR-0075),agent(the toolset, ADR-0078),wire(the wire schema), and a private_common. - It is a plugin. Nothing else in artifactr imports it, so an application that doesn't import it registers none of its types and runs none of its checks. It depends inward only, on names in
__all__ofartifactr.core,artifactr.agentandartifactr.workspace.tests/ui/test_layering.pyholds both, and the layering test lists it as a layer. - It is installed by the
formsextra,artifactr-ai[forms], which holds whatartifactr.uiimports beyond artifactr's own dependencies: today only Pydantic's core, whose custom errors it raises, and which Pydantic installs already, so the extra adds nothing to an install. It is the stable name an application installs the forms by, which any dependency they ever need joins, so an application never changes how it asks for them. - relayr bridges it, in
relayr.uibehind relayr's ownformsextra (relayr ADR-0038): artifactr can't depend on relayr. - Its wire schema is artifactr's,
schemas/artifactr_ui.v1.json, regenerated bymoon run artifactr:schemabeside the protocol's and drift-tested as they are; portalr generates its types from it. Its fixed fields, an impliedtypeamong them, are required, as ADR-0063's test holds every committed schema to. - Its docs are artifactr's own: a guide among artifactr's, a reference page, a section of its architecture, and ADRs in artifactr's series (ADR-0074 to ADR-0080), since the forms have no series of their own.
- Its tests are artifactr's, in
tests/ui/, on artifactr's storages andARTIFACTR_TEST_POSTGRES_URL, with the vectors portalr's TypeScript tests read intests/ui/vectors/.
Options considered¶
| Option | For | Against |
|---|---|---|
The forms extra, and artifactr.ui (chosen) |
The extra names what an application asks for; the module holds dashboards and, later, apps too, all UI in the chat | Two names for one integration |
ui for both |
One name | artifactr-ai[ui] reads as portalr's UIs |
forms for both |
One name | Dashboards and apps aren't forms |
relayr's bridging in artifactr.ui |
One home for the forms | artifactr can't import relayr |
Consequences¶
- Easier:
uv add "artifactr-ai[forms]"andfrom artifactr.ui import UI_TYPES, ui_checks. - Harder: relayr's floors on artifactr,
artifactr-ai>=0.2.0in its dependencies and itsformsextra, name a release withoutDeferredAnswered.tool_nameorartifactr.ui, and can't rise in the change that adds them: uv locks no floor above a workspace member's own version. So relayr's next release pull request stays unqueued until artifactr's next release is onmain, and relayr's floors, its dependencies' and itsformsextra's, rise to it first, as lattice ADR-0023's amendment holdsartifactr-files-ai's release (relayr ADR-0038).