# GovOPlaN UI/UX Decision Ledger

This ledger records product UI/UX decisions that affect admin, settings,
configuration, connector, policy, and module-management surfaces. It is a
binding design reference: future implementation should follow these decisions
unless the decision is explicitly revised here and affected screens are updated
to match.

Active tracking issue: `GovOPlaN/govoplan-core#225`.

## Operating Rule

GovOPlaN must expose advanced platform capability without presenting the user
with every option at once. Non-technical users should be able to complete common
workflows through guided, plain-language flows. Expert and diagnostic detail may
exist, but it must be deliberately layered.

The ethical design doctrine in `INTERFACE_ETHICS_AND_DESIGN_DOCTRINE.md` is the
normative baseline for this ledger. A screen can be visually quiet and still be
ethically complete only when it preserves context, consequence,
contestability, responsibility, and traceability at the point of action.

## Binding Decisions

| ID | Decision | Status | Applies To |
| --- | --- | --- | --- |
| UX-001 | Use progressive disclosure by default. Common decisions stay visible; advanced or hazardous options live in collapsed panels, later wizard steps, or explicit advanced sections. | Accepted | Admin, settings, connector setup, policy editors, module operations |
| UX-002 | Raw JSON is not a primary configuration editor. Every normal configuration path needs typed controls, validation, and help text. JSON may be shown for import/export, diagnostics, or expert inspection only. | Accepted | All admin/configuration UIs |
| UX-003 | Prefer guided workflows over option dumps for setup and risky changes. Use wizards for connector setup, configuration package import, module install/uninstall, destructive actions, and policy changes with broad impact. | Accepted | Connectors, package import, module lifecycle, governance/policy |
| UX-004 | Discover technical values when possible. Ask for the smallest user-known input, then discover and prefill technical fields for review. | Accepted | File connectors, mail/groupware, public URLs, future external providers |
| UX-005 | Disabled actions and failed steps must explain why they are unavailable, who can fix them, and where to go next. Silent disabled states are not acceptable for primary actions. | Accepted | All primary actions |
| UX-006 | Explanations must be available but quiet. Use short inline text and expose richer explanations through help affordances, side panels, expandable sections, or review steps. | Accepted | All complex forms and flows |
| UX-007 | Creation/editing should prefer modals or focused step flows when it reduces page clutter. Overview and comparison screens remain full-page. | Accepted | Settings/admin surfaces |
| UX-008 | Similar concepts must use shared placement and components: server/credential/policy rows, problem lists, review steps, advanced panels, confirmation modals, and empty/error states. | Accepted | Core WebUI and module WebUIs |
| UX-009 | Preflight and diagnostics are product UX. Validation, policy, permission, dependency, and capability failures must be written for operators before exposing internal details. | Accepted | Installer, connectors, policy, package import |
| UX-010 | Context, decision, and consequence must be visible together for actions that affect rights, duties, records, money, communication, retention, external systems, or workflow state. | Accepted | Workflow, admin, portal, policy, connector, records, payments |
| UX-011 | Navigation is not consent. Route changes, panel switches, and passive selection must not execute consequential actions without an explicit action surface. | Accepted | All WebUI surfaces |
| UX-012 | Automated actions must remain inspectable. The UI must show the system actor, trigger, policy result, observed effects, and failure/manual-intervention state when automation changes administrative state. | Accepted | Workflow, automation, connectors, tasks, audit |
| UX-013 | Contestable decisions must expose provenance. Denials, locks, generated outputs, calculated defaults, policy decisions, access decisions, and retention decisions need a reachable source path. | Accepted | Policy, access, templates, workflow, retention, records |
| UX-014 | Retraction, expiry, undo, rollback, and delete controls must state the real limit of the operation. Corrective or future-only actions must not be described as if they undo already observed effects. | Accepted | Postbox, files, records, installer, workflow, payments |
| UX-015 | Core owns the platform appearance contract. Modules must use shared CSS tokens and shared controls for theme-aware UI; they must not define independent light/dark palette systems. | Accepted | Core shell and all module WebUIs |
| UX-016 | Full-page create/edit surfaces keep `Discard` and the named `Save …` action in the upper-right page action cluster, with Save at the far right. Their position remains stable through validation and loading states. | Accepted | All full-page create/edit surfaces |
| UX-017 | Table row actions use icon-only controls in a stable rightmost column and intent order: inspect/open, edit, copy, transfer/share/download, retry/restore, destructive action last. Every icon requires a translated accessible name and tooltip. | Accepted | All structured tables |
| UX-018 | A collapsible card containing only one table gives the table the card's full available body, without decorative inner wrappers, duplicate padding, max-widths, or nested scrolling. | Accepted | List, detail, workflow, and configuration surfaces |
| UX-019 | Focused-view precedence is manual session pin, current-task suggestion, user default, role/tenant default, then the full interface. The active source and a full-interface escape remain visible; a suggested view never changes authorization or implies consent. | Accepted | Shell, modules, future workflow composition |
| UX-020 | Centrally exported Core components are mandatory wherever their contract covers the interaction. A custom reusable control, presentation primitive, or module-local substitute requires explicit product-owner authorization, a narrowly specific purpose, and documented rationale and scope; it must not duplicate a central component. | Accepted | Core WebUI and all module WebUIs |
| UX-021 | A collection-wide create action belongs in that collection's page heading and is not duplicated in a persistent side panel. When a side panel is the creation surface, it is present for the creation view only. | Accepted | List-detail, directory, and create surfaces |
| UX-022 | Use central `Card` components for logical sections, `DataGrid` for tabular row collections and their ordered actions, and `ToggleSwitch` for boolean settings. Repeatable people/contact editors use one structured row per person with name, email address, and actions; free-form address parsing is reserved for an explicitly designed bulk-import flow. | Accepted | All WebUI forms and collection editors |
| UX-023 | `FieldLabel` is the standard label/help surface for every field that is not self-explanatory. Any field rendered without it must be recorded in the omission register below, including its accessible-name source and rationale. Users may hide inline help markers through their persisted interface preference; the field label itself remains visible. | Accepted | All Core and module forms |
| UX-024 | Explicit `Discard` actions and dirty in-application navigation use the shared `UnsavedChangesProvider` dialog. A page registers save/discard behavior with `useUnsavedDraftGuard`; its Discard button calls `requestDiscard`, and route changes use `useGuardedNavigate` or `requestNavigation`. | Accepted | All create/edit surfaces |
| UX-025 | `window.alert` and the global `alert` function are prohibited. A narrowly necessary exception requires product-owner authorization and an entry in the alert exception register before implementation. | Accepted | All WebUI code |
| UX-026 | A table defines one stable ordered action set. A row-level unavailable action remains in its normal position and is disabled, preferably with `disabledReason`; structurally irrelevant actions are omitted for the entire table. Empty rows reserve the same slots so their Add action stays in the normal left-most action position. | Accepted | All structured tables |

## Confirmed Implementation Decisions

These decisions were accepted on 2026-07-09 for the first implementation slice.
They shape reusable components and screen structure. Future changes must revise
this section and update affected screens.

### DUE-001: Primary Admin Configuration Shell

Decision: admin/configuration surfaces should standardize on a two-zone layout.

- Left or top area: searchable overview/list, status, and primary actions.
- Main area: selected item summary and common settings.
- Modal/wizard: create, connect, edit, test, review, and confirm actions.
- Collapsed advanced panels: rarely used technical fields.

Use this for file connectors and mail servers first, then migrate policy,
retention, API keys, and module operations.

### DUE-002: Wizard Step Model

Decision: standard wizard steps and names use this baseline:

1. Choose type or scope.
2. Enter essentials.
3. Discover or test.
4. Configure ownership and policy.
5. Review changes and blockers.
6. Save or submit for operator action.

Not every wizard needs every step, but flows should use these names and order
where applicable.

This applies to explicit assisted setup, onboarding, import, preflight, and
risky multi-step operations. It is not the default shape for ordinary create or
edit dialogs.

### DUE-003: Explanation Placement

Decision: richer explanations should use this placement model:

- Short helper text below labels only when it prevents common mistakes.
- Tooltips for icon-only controls and compact terms.
- Expandable "Why?" or "Details" blocks for contextual explanations.
- Right-side detail panel or review step for preflight/provenance/diagnostics.

Avoid permanently visible paragraphs inside dense admin cards.

### DUE-004: Advanced Options Contract

Decision: advanced options are fields that are needed for control, compatibility,
or diagnostics but are not part of the common setup path.

- protocol-specific endpoints, ports, path overrides, TLS/signing toggles,
  timeout/retry tuning, raw headers, migration/destructive flags, and fallback
  compatibility settings are advanced.
- names, descriptions, provider type, base URL, ownership, policy mode, and
  basic credentials are not advanced.

Advanced fields must still be editable through typed controls.

### DUE-005: Blocker Language Contract

Decision: all disabled or blocked primary actions should use a shared structured
reason object.

Shape:

- `summary`: short plain-language reason.
- `details`: optional explanation.
- `required_action`: what needs to happen.
- `actor`: who can do it, for example system administrator or tenant admin.
- `target`: where to go or which setting/capability is missing.
- `technical_details`: optional expandable developer/operator data.

For simple missing required fields, highlight the fields and put the structured
reason in a compact hover/focus bubble on the disabled primary action instead of
adding a large persistent warning block.

### DUE-006: First Migration Surface

Decision: convert file connectors first, then mail servers. They share the same
server/credential/policy model, are high-value, and will prove the reusable
patterns quickly.

### DUE-007: Adaptive Create/Edit Forms

Decision: ordinary create/edit dialogs should show the full editable state in an
adaptive form, not force a linear wizard.

- The user chooses a type, provider, credential mode, or policy mode.
- The dialog immediately adjusts to show only the fields relevant to that state.
- Editing an existing object uses the same field grouping and layout as creating
  it, so users can recognize the state they configured.
- Discovery and test actions must give visible feedback in the same dialog:
  directly usable, discovered alternative, credentials needed/rejected, or not
  found with the next action the user can take.
- Wizard shells remain available for assisted setup, first-run guidance,
  imports, discovery-heavy flows, and operational preflight workflows.

### DUE-008: Platform Theme Contract

Decision: the WebUI shell exposes a small, stable appearance contract based on
shared CSS tokens and persisted user preference selection.

- Core applies `system`, `light`, and `dark` preferences at the document root.
- Core owns shared tokens such as `--bg`, `--bar`, `--panel`, `--surface`,
  `--line`, `--line-dark`, `--text`, `--text-strong`, `--muted`, semantic
  status colors, radii, shadows, and disabled-control colors.
- Modules must style new UI with these tokens and shared controls. Module-local
  CSS may tune layout and spacing, but it must not introduce a separate
  appearance system.
- Appearance controls live in user settings first. Tenant defaults and policy
  enforcement can be added later without changing the token contract.
- Visual preview in settings is illustrative; it must reflect token families,
  not become a second theme implementation.

### DUE-009: Central Component And Exception Contract

Decision: module interfaces are compositions of the components exported by
`@govoplan/core-webui`. When Core already owns the matching interaction, using
the central component is required rather than preferred.

A route or domain-specific page composed from central components is ordinary
module composition. A new reusable UI control, presentation primitive, or
module-local substitute is a custom component. Before one is implemented, the
product owner must explicitly authorize it and the owning decision or issue must
record:

- its single, narrowly defined purpose and intended consumers
- why central components or their composition cannot meet that purpose
- the permitted scope and the boundary it must not grow beyond
- accessibility, reachable states, theme behavior, and test expectations
- whether the component remains domain-owned or is a candidate for Core

Custom components must not duplicate, fork, or cosmetically replace a central
component. An existing local implementation does not grant an exception. If a
central contract later covers the need, migrate to it unless the product owner
explicitly retains the exception.

### DUE-010: Scheduling Request Reference Composition

Decision: Scheduling requests provide a concrete reference application of the
universal placement and component rules.

- The persistent left panel stacks `My scheduling requests` and `Scheduling
  requests for me`; it is list context, not a second creation affordance.
- The left panel's `Scheduling requests` header owns one `Add` action. It opens
  the shared view/create/edit surface in the right main panel.
- Basic information, Calendar integration, candidate slots, and participants
  use the central `Card` component as four logical sections.
- Candidate slots and participants use the central `DataGrid`, including its
  standard row-action placement and order.
- Calendar integration uses the central `ToggleSwitch`, with its dependent
  controls shown when enabled.
- Each participant is edited as one structured row with name, email address,
  and actions. The normal editor does not parse a free-form list of addresses;
  that interaction requires a separate, explicitly designed bulk-import flow.

Equivalent list/create/edit surfaces use the same underlying rules. These are
not Scheduling-local component variants.

### DUE-011: Field Help, Discard, And Table Action Contracts

Decision: the central components own these interactions; modules compose them
instead of reproducing their behavior.

- `FormField` and `ToggleSwitch` already render `FieldLabel`. Direct field
  compositions use `FieldLabel` explicitly when the meaning or limitation is
  not self-explanatory.
- `help` content is contextual guidance, not the accessible name. The persisted
  `show_inline_help_hints` user preference hides only the `InlineHelp` marker by
  applying `ui-hide-help-hints` at the document root.
- A dirty editor registers once with `useUnsavedDraftGuard`. An explicit
  Discard button calls `useUnsavedChanges().requestDiscard(afterResolve)`; SPA
  navigation uses `useGuardedNavigate` or `requestNavigation`. Both paths show
  the same shared unsaved-changes dialog. A browser tab/window unload remains a
  browser-controlled confirmation because browsers do not permit a custom
  modal at that boundary.
- `TableActionGroup` receives the table's stable action set. Use `disabled` and
  `disabledReason` for row state; omit an action only when that action does not
  belong to the table. `minimumSlots` reserves trailing positions for an empty
  row. `DataGridEmptyAction` does this for the standard add/move/remove layout.
- A paginated `DataGrid` has exactly one query owner. Client mode receives the
  complete logical row set and applies filtering and sorting before slicing a
  page. Server mode receives only the loaded page, requires `onQueryChange`,
  and the backend applies every emitted filter/sort before pagination while
  returning `totalRows` for the filtered result. Server list filters declare
  their complete option domain instead of deriving it from the loaded page.
  External filter affordances such as summary-count shortcuts update the
  grid's `query` contract; the grid header controls and backend query therefore
  always display and execute the same filter state.
- Feedback and confirmation use `Dialog`, `ConfirmDialog`, or
  `DismissibleAlert`. They never fall back to `window.alert`.

### DUE-012: Rich HTML Editing Contract

Decision: modules that edit persisted HTML use the central
`WysiwygEditor` exported from `@govoplan/core-webui/wysiwyg`.

- The dedicated subpath is intentional: the editor and its engine remain a
  shared Core contract without adding their code to module combinations that
  never consume rich-text editing.
- Consumers provide controlled HTML and domain-specific token labels. The
  editor owns visual/source switching, formatting, links, images, safe URL
  handling, and atomic inline token rendering; it does not own template
  semantics or persistence.
- Existing HTML outside the supported visual subset opens in source mode.
  Rendering the value must not rewrite it, and users receive an explicit
  warning before choosing the visual surface.
- Domain placeholders remain their original serialized text. Atomic token
  presentation is an editing aid only, so backend renderers and existing
  templates do not need a new storage format.

#### FieldLabel Omission Register

Every Core field surface that intentionally does not render `FieldLabel` is
listed here. Module repositories keep an equivalent register in their durable
UI documentation until a central cross-repository audit is available.

| Core scope | Why `FieldLabel` is omitted | Accessible/context label source |
| --- | --- | --- |
| `PasswordField`, `ColorPickerField`, `DateField`, `TimeField`, and `DateTimeField` input internals | These are label-neutral composite primitives and are placed inside `FormField`/`FieldLabel` by the consuming form. Rendering another label inside the primitive would duplicate it. | Enclosing label; a direct consumer must pass an accessible name and record that direct composition here. |
| `ToggleSwitch` native checkbox | The shared component already renders its visible text through `FieldLabel`; the native input must not render a second label. | The enclosing native label and derived `aria-label`. |
| `FileDropZone` hidden file input | The input is an implementation detail of the labelled keyboard-operable drop target. | Drop target text and `inputLabel`/`aria-label`. |
| `AdminSelectionList` and `DataGrid` list-filter checkboxes | Each option is self-explanatory and already enclosed by its visible option label. | Enclosing native option label. |
| `EmailAddressInput` compact Name and Email fields | These two conventional fields are self-explanatory in the compact address popover; richer address guidance belongs to the enclosing field. | Visible native labels; the free-form editor also has a descriptive `aria-label`. |
| `DataGrid` page-size, filter, and inline cell editors | The surrounding column header/filter heading supplies field context; repeating a labelled help marker in every cell would add noise. | Column header, filter heading/native label, or generated cell `aria-label`. |
| Retention-policy value controls | `PolicyRow` owns the field label, help, effective value, and provenance for its control. | The containing `PolicyRow` label/help contract. |

#### Alert Exception Register

No `window.alert` or global `alert` exception is authorized.

## Implementation Sequence

| Phase | Scope | Output |
| --- | --- | --- |
| 0 | UX inventory | List every admin/settings/configuration surface, classify it, and record whether it violates a binding decision. |
| 1 | Core primitives | Shared wizard shell, advanced panel, help affordance, blocker callout, problem list, review step, and discovery/test result components. |
| 2 | File connectors | Adaptive create/edit for provider/server, credential, discovery/test, ownership/policy, plus optional assisted wizard later. |
| 3 | Mail servers | Same adaptive pattern as files, adapted to server/credential/policy and test-send/test-login behavior. |
| 4 | Policy/retention editors | Effective value first, provenance visible, override/edit in modal, blocked edits explained. |
| 5 | Module/package operations | Step-based install/uninstall flow with preflight, maintenance, daemon handoff, migration, and rollback explanation. |
| 6 | Remaining settings/admin screens | Apply inventory findings by priority and remove one-off layouts. |

## Current Surface Inventory

This is the phase-0 inventory baseline. It should be extended as each screen is
converted or reviewed.

| Surface | Repository | UX State | Next Action |
| --- | --- | --- | --- |
| File connector settings | `govoplan-files` | First adaptive modal slice started: connections and credentials now use full-state create/edit forms with conditional fields, advanced panels, and blocker primitives. Wizard shell is retained for later assisted setup. Central policy card still needs a layered editor. | Finish provider discovery/test-in-flow, then convert policy editing. |
| Mail server settings | `govoplan-mail` / `govoplan-core` | Uses the shared server/credential model visually, but create/edit still needs the same adaptive pattern as files. | Migrate to adaptive server/credential/policy dialogs, with optional assisted wizard later. |
| Connector policy/effective rows | `govoplan-core`, module UIs | Effective-policy direction exists, but many editors still expose broad option sets. | Put effective value first, move overrides into modal, and explain blocked edits. |
| Admin module management | `govoplan-admin` | Has preflight concepts, but operational choices are still technical and dense. | Convert install/uninstall/package changes to operator wizards. |
| Configuration packages | `govoplan-admin` | Catalog/import work exists, but package editing can still drift toward technical fields. | Add guided import/review/problem-list flow. |
| Retention and privacy | `govoplan-core` | Functional editor exists; consequence language and provenance can be stronger. | Layer advanced retention options and add review for broad changes. |
| API keys | `govoplan-access` / admin UI | Security-sensitive creation needs least-privilege guidance. | Add scoped creation wizard with expiry/owner review. |
| User settings | `govoplan-core` | Preferences persistence exists; interface navigation issue was fixed earlier, but the surface still needs UX review. | Keep simple sections, remove double-click traps, and add quiet explanations. |

## Impact Index

| Surface | Current Risk | Expected Pattern |
| --- | --- | --- |
| File connectors | Too many technical fields and unclear setup order. | Adaptive create/edit form with optional assisted wizard, discovery/test, credential binding, policy review, and advanced protocol panel. |
| Mail servers | Similar server/credential/policy concepts risk diverging from files. | Same tree/list and adaptive server/credential/policy model as file connectors. |
| Connector credentials | Security-sensitive details can overwhelm users. | Separate credential flow with secret-reference language and test result explanation. |
| Policy and effective settings | Users need to know why a value is inherited or locked. | Effective row first, source/provenance, local override action, actionable blocked reason. |
| Module install/uninstall | Operationally risky, currently inherently technical. | Operator wizard with preflight, maintenance, daemon handoff, review, and rollback explanation. |
| Configuration packages | Could become package JSON editing. | Package catalog/import wizard using provider data requirements and problem lists. |
| Retention/privacy | High-risk settings need explanation and provenance. | Layered editor with plain-language consequences and review. |
| Automation/workflow commands | Hidden side effects would undermine accountability. | Action/effect preview, system-actor display, command record, retry/quarantine/manual states, and audit links. |
| Postbox and encrypted communication | Retraction and access can be misunderstood. | Honest key-fetch/decryption state, expiry limits, recipient/device access provenance, and delivery evidence. |
| API keys | Security-sensitive creation and scope selection. | Scoped creation wizard, least-privilege suggestions, clear expiry/owner explanation. |
| User settings | Needs clarity and persistence across profile/interface/preferences. | Simple settings sections with immediate feedback and no double-click navigation traps. |

## Review Checklist

Every new or changed admin/configuration surface should answer:

- What is the common path, and is it visible without noise?
- Which fields are advanced, and are they collapsed by default?
- Is every configuration value editable through typed controls?
- Does the flow avoid JSON as the primary editor?
- Does the screen explain disabled actions and failed validation in plain
  language?
- Does it say who can fix a blocker and where?
- Does it reuse existing core patterns for wizard steps, problem lists, modals,
  help, and review?
- Is there a review or preflight step before broad, destructive, or risky
  changes?
- Does the action surface show consequence, reversibility, and audit evidence
  when rights, duties, records, money, communication, external systems, or
  workflow state are affected?
- Does the surface use every applicable central Core component? If it contains
  a custom component, is the product-owner authorization, narrow purpose,
  rationale, scope, and non-duplication evidence recorded?
- Is a collection-wide create action in the collection heading rather than
  duplicated in a persistent side panel?
- Are logical sections, tabular collections, boolean settings, and repeatable
  people/contact rows composed with `Card`, `DataGrid`, `ToggleSwitch`, and one
  structured row per person respectively?
- Does every non-self-explanatory field use `FieldLabel`, and is every omission
  recorded with its rationale and accessible-name source?
- Do explicit Discard and dirty navigation use the shared unsaved-changes
  registration/dialog rather than a page-local confirmation?
- Does every row retain the table's action set in the same order, disabling
  unavailable actions and reserving the same empty-row slots?
- Is feedback rendered with a central dialog/alert component, with no
  unauthorized `window.alert` or global `alert` call?
- If automation is involved, can the user see the trigger, system actor,
  observed effects, and failure/manual-intervention state?
- Are technical details available without being the first thing the user sees?

## Revision Rule

When a UX decision changes:

1. Update this ledger.
2. Update the affected shared components.
3. Update existing screens listed in the impact index.
4. Add or update Gitea issues for any remaining surfaces that still follow the
   old decision.
5. Sync the wiki.
