ADR 0007: Typed custom fields and one field editor
Date: 2026-10-30 Status: Accepted
Decision
Section titled “Decision”Use one create/edit dialog for custom fields. Definitions contain a stable ID and technical name, translated labels, kind, required flag and kind-specific JSONB configuration. Shared Keyword/Type/Status/Category keep their protected kinds and existing rules.
Map short/multiline text, email, international phone and HTTP(S) URL to PostgreSQL text; integers to bigint; exact decimals to numeric(38, configured scale); Boolean to boolean; date to date; timestamp to timestamptz; single choice to uuid; multiple choice to uuid[]. Money uses JSONB {amount, currency} with an exact decimal string and ISO currency code. User/group references use JSONB objects {kind, id} or arrays of these objects. JSONB collections are validated by the application; they do not imply relational foreign keys. Groups belong to a workspace; referenced users must exist. Reference assignment grants no access rights or group membership.
Integer/decimal amounts cross API/MCP as strings to preserve precision. Missing values are null, false and zero are present values. Date-only values retain their ISO date. Timestamps support millisecond precision and require an explicit offset at the API boundary and are returned in UTC; the web form uses the effective personal/workspace named time zone under ADR 0014, rejects DST gaps and requires an explicit occurrence for overlaps. Multiline formatting is optional Markdown, without raw HTML execution. Supplied long-form notes and multiline demo narratives use Markdown by default; the short Distribution-list description is explicitly plain text with separate Markdown notes; ordinary custom-field creation retains its configurable formatting choice. Editable previews and read-only record rendering share one explicitly styled renderer for heading levels 1–6, lists, quotes, links, code and separators; paragraph line breaks are retained. Tables, images and task-checkbox extensions are not enabled. Multiline form controls offer Insert timestamp: insert ----- DATE TIME (DISPLAY NAME) ----- as a complete text line at the current selection, followed by one newline, then focus the caret at the next line. Date/time use the effective regional settings and named time zone; the name comes from the current authenticated session. Inserting replaces selected text, retains surrounding contents and refuses changes exceeding the field limit. It remains an ordinary unsaved field edit; the saved journal independently records the actual actor/time. Markdown previews preserve the line break.
Choice options retain stable IDs and translated labels and may be deactivated. Existing inactive selections may remain; new assignments are rejected. Supported references can target users, groups or both, with single/multiple selection. Currency and decimal scale are explicit field settings.
Definition changes and schema/value conversion run under the existing model lock in one transaction, with preflight of all current values. Conversion must preserve values on a round trip; incompatible or lossy conversion fails atomically. Collection/reference/choice family changes are restricted to compatible mappings. Changed record representations increment record versions and get per-field journal entries under the same change ID as the definition change. Historic journals remain untouched.
Consequences
Section titled “Consequences”Field rendering, requiredness, validation and display use shared kind definitions. Business-content access follows ADR 0026. ADR 0008 permits workspace Designers to configure fields and Users to inspect their definitions. ADR 0013 adds direct group membership management. Ordinary reference assignment remains separate from permissions; the protected Access principals field has the ownership/foreign-access semantics implemented under ADR 0027 and specified in PRD section 4. ADR 0030 adds translated descriptions/hints, typed defaults and supported scalar uniqueness to the shared editor. Production migration and scalability remain separate work.
Selection trees extend this decision under ADR 0037: tree_choice stores an ancestor-closed UUID array with same-dictionary parent metadata. Multiple/tree field values and defaults use the shared selection dialog.