Skip to content

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 (the form, submission, dashboard and app kinds, and ask_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__ of artifactr.core, artifactr.agent and artifactr.workspace. tests/ui/test_layering.py holds both, and the layering test lists it as a layer.
  • It is installed by the forms extra, artifactr-ai[forms], which holds what artifactr.ui imports 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.ui behind relayr's own forms extra (relayr ADR-0038): artifactr can't depend on relayr.
  • Its wire schema is artifactr's, schemas/artifactr_ui.v1.json, regenerated by moon run artifactr:schema beside the protocol's and drift-tested as they are; portalr generates its types from it. Its fixed fields, an implied type among 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 and ARTIFACTR_TEST_POSTGRES_URL, with the vectors portalr's TypeScript tests read in tests/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]" and from artifactr.ui import UI_TYPES, ui_checks.
  • Harder: relayr's floors on artifactr, artifactr-ai>=0.2.0 in its dependencies and its forms extra, name a release without DeferredAnswered.tool_name or artifactr.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 on main, and relayr's floors, its dependencies' and its forms extra's, rise to it first, as lattice ADR-0023's amendment holds artifactr-files-ai's release (relayr ADR-0038).