Rituals pattern: slot form (fill in what a ritual needs) #178

Closed
opened 2026-10-04 20:20:32 +00:00 by peterswimm · 3 comments
Owner

Part of #177.

What it's for: the standard form for "fill in what this ritual or intent needs". The fields come from the ritual's slot list rather than a hand-built screen. This is the most-used pattern, and most of the others build on it.

Where it shows up today (four different versions)

What the field list can describe today

key, label, type (text / textarea / enum / int / bool), required, mutability (sealed / fungible) and default_value. That's thin. Please design for these as well (engineering will add them to the API):

  • Field kinds: multi-line text, single or multiple choice, date/time, pick a wish or entity, file, and secret (masked, never echoed back)
  • Help text under a field, and a placeholder
  • Validation messages (too short, wrong format), and when they appear
  • Sealed fields: set by the ritual's author, shown read-only, so the person sees them without being able to edit them
  • Conditional fields: only shown when another field has a certain value
  • Grouping long forms into sections

Acceptance criteria

  • Spec covers each field kind, the default/filled/invalid/disabled/sealed states, and conditional fields.
  • Says which GateExpression rules are needed (today there are only always and required_filled; candidates are length_at_least, matches_pattern, equals).
  • CLI version: how each field kind is asked as a terminal prompt, including secret input.
  • Compact layout.
Part of #177. **What it's for:** the standard form for "fill in what this ritual or intent needs". The fields come from the ritual's slot list rather than a hand-built screen. This is the most-used pattern, and most of the others build on it. ## Where it shows up today (four different versions) - Flutter: hand-built `_ParametersPanel` in [`ritual_execution_screen.dart:253`](https://git.toilville.dev/toilville/toilville_rituals/src/branch/main/lib/ui/screens/ritual_execution_screen.dart#L253) - macOS: the form preview in [`RitualComposerView.swift`](https://git.toilville.dev/toilville/toilville_rituals/src/branch/main/apps/rituals-macos/Views/RitualComposerView.swift) - Dart CLI: one `Enter <field>:` text prompt per missing field ([`run_command.dart:88`](https://git.toilville.dev/toilville/toilville_rituals/src/branch/main/packages/ritual_cli/lib/src/commands/run_command.dart#L88)) - `ritual run`: `--field key=value` flags only, no prompting ## What the field list can describe today `key`, `label`, `type` (text / textarea / enum / int / bool), `required`, `mutability` (sealed / fungible) and `default_value`. That's thin. Please design for these as well (engineering will add them to the API): - Field kinds: multi-line text, single or multiple choice, date/time, **pick a wish or entity**, file, and **secret** (masked, never echoed back) - Help text under a field, and a placeholder - Validation messages (too short, wrong format), and when they appear - **Sealed** fields: set by the ritual's author, shown read-only, so the person sees them without being able to edit them - **Conditional fields**: only shown when another field has a certain value - Grouping long forms into sections ## Acceptance criteria - [ ] Spec covers each field kind, the default/filled/invalid/disabled/sealed states, and conditional fields. - [ ] Says which `GateExpression` rules are needed (today there are only `always` and `required_filled`; candidates are `length_at_least`, `matches_pattern`, `equals`). - [ ] CLI version: how each field kind is asked as a terminal prompt, including secret input. - [ ] Compact layout.
Member

Decisions for #178 (slot form). They answer the four acceptance criteria. The schema and API design belong to engineering.

Layout and marking

  • The label is above the field at every width. Help text is below the field and separate from the label. The placeholder is an example only. It never carries required or help information.
  • Required fields have *. Optional fields have no marker. Each form says "* Required" once. The * has the accessible name "required" and uses the text colour, not the error colour.
  • All fields are visible. Optional fields do not go behind "More options". If forms get long, use field groups.
  • Long forms may use named field groups. The ritual definition says which fields belong to each group. Groups are open by default. Short forms need none. A field group is not the section block.
  • Compact layout: one column, groups stack, radio and checkbox groups wrap. Controls follow the lens Corners and Density (#189). Targets stay at least 44 px.

Field kinds

  • text, textarea, number and yes/no use the normal field patterns.
  • Single choice: a small, easily scannable set uses radio buttons. A longer set uses a select.
  • Multiple choice: a small set uses checkboxes. A longer set uses a multi-select picker. The renderer sets the practical threshold.
  • date and time are separate kinds, so a ritual can ask for either or both. Both accept typing, not only a picker.
  • wish or entity: a searchable picker. The selected entity shows as the field value.
  • file: the system file picker, with the selected filename.
  • secret: masked by default, with a text show/hide control for the value being entered. A stored secret is never shown in clear text.

States

  • default, focused and filled follow the standard field pattern.
  • invalid: error text below the field, with an icon. Not colour alone.
  • sealed: the value is shown clearly and is read-only. It is labelled with the word "Sealed", with no lock icon. It keeps normal text contrast and is readable by assistive technology. It does not look disabled. Optional help text: "Set by the ritual author."
  • disabled: dimmed, not editable, announced as unavailable. It is different from sealed. When a field becomes disabled is not defined here, because there is no current requirement.
  • secret, saved: shows "Saved, hidden" with a Replace action.
  • A field-level sealed (the author fixed the value) is separate from the sealed block in #189 (Grammar stops a lens from hiding it).

Validation

  • No error appears while the person is first typing. The first error appears when the person leaves the field. After that the error updates live as the person corrects it.
  • Submit and Run validate again. Focus moves to the first invalid field, and a summary lists the errors. Screen readers announce errors.
  • Messages say what to do, for example "Use at least 8 characters".

Conditional fields

  • A field is hidden until its condition is met. It shows when it becomes relevant. A field that appears is announced to screen readers, keeps its place after its controlling field and does not take focus.
  • Hidden fields are ignored by required checks, validation and step gates. They are not submitted. Their entered value is kept locally in case the condition becomes true again.

Rules

  • Validation, conditional visibility and step gating are separate behaviours. GateExpression stays a step-level mechanism. #178 needs no new GateExpression types.
  • #178 needs three field-level rules: equals (conditional visibility, for example Audience = My team), and length_at_least and matches_pattern (validation). They may share one rule vocabulary, so authors do not write the same check twice.

CLI

  • The CLI has the same meaning as the GUI. Required prompts carry *, and * Required is printed once at the start. Empty input skips an optional field. A required field is asked again.
  • text: a normal prompt. textarea: a clear multiline interaction. number and yes/no: a prompt that states the accepted answers.
  • single choice: a numbered list, choose one. multiple choice: a numbered list, choose several.
  • date and time: the prompt states the accepted format, for example YYYY-MM-DD and HH:MM.
  • wish or entity: select or search an existing object where possible. Otherwise accept an unambiguous name or ID. file: a file path.
  • secret: input is fully hidden. No echo and no asterisks. The prompt says "input hidden". A secret is never accepted through --field.
  • Sealed values are shown as Sealed and not prompted. Conditional fields are asked only when their condition is met, after their controlling field.
  • Invalid input shows a clear message and asks for the same field again.

Accessibility (Grammar)

  • All controls work with the keyboard and a screen reader. Radio and checkbox groups have a group label and arrow keys. Date controls accept typing. The show/hide control has a text label and a state.

Engineering follow-up, not part of this decision: schema fields for help text, placeholder, options, rules, visibility and field groups; renderers for radio, checkbox, secret, file and entity; evaluateGate in Flutter; the required check ignoring hidden fields; existing labels that contain "(optional)" or "e.g."; blank optional fields in the Dart CLI.

@peterswimm

Decisions for #178 (slot form). They answer the four acceptance criteria. The schema and API design belong to engineering. Layout and marking - The label is above the field at every width. Help text is below the field and separate from the label. The placeholder is an example only. It never carries required or help information. - Required fields have `*`. Optional fields have no marker. Each form says "* Required" once. The `*` has the accessible name "required" and uses the text colour, not the error colour. - All fields are visible. Optional fields do not go behind "More options". If forms get long, use field groups. - Long forms may use named field groups. The ritual definition says which fields belong to each group. Groups are open by default. Short forms need none. A field group is not the `section` block. - Compact layout: one column, groups stack, radio and checkbox groups wrap. Controls follow the lens Corners and Density (#189). Targets stay at least 44 px. Field kinds - text, textarea, number and yes/no use the normal field patterns. - Single choice: a small, easily scannable set uses radio buttons. A longer set uses a select. - Multiple choice: a small set uses checkboxes. A longer set uses a multi-select picker. The renderer sets the practical threshold. - date and time are separate kinds, so a ritual can ask for either or both. Both accept typing, not only a picker. - wish or entity: a searchable picker. The selected entity shows as the field value. - file: the system file picker, with the selected filename. - secret: masked by default, with a text show/hide control for the value being entered. A stored secret is never shown in clear text. States - default, focused and filled follow the standard field pattern. - invalid: error text below the field, with an icon. Not colour alone. - sealed: the value is shown clearly and is read-only. It is labelled with the word "Sealed", with no lock icon. It keeps normal text contrast and is readable by assistive technology. It does not look disabled. Optional help text: "Set by the ritual author." - disabled: dimmed, not editable, announced as unavailable. It is different from sealed. When a field becomes disabled is not defined here, because there is no current requirement. - secret, saved: shows "Saved, hidden" with a Replace action. - A field-level `sealed` (the author fixed the value) is separate from the sealed block in #189 (Grammar stops a lens from hiding it). Validation - No error appears while the person is first typing. The first error appears when the person leaves the field. After that the error updates live as the person corrects it. - Submit and Run validate again. Focus moves to the first invalid field, and a summary lists the errors. Screen readers announce errors. - Messages say what to do, for example "Use at least 8 characters". Conditional fields - A field is hidden until its condition is met. It shows when it becomes relevant. A field that appears is announced to screen readers, keeps its place after its controlling field and does not take focus. - Hidden fields are ignored by required checks, validation and step gates. They are not submitted. Their entered value is kept locally in case the condition becomes true again. Rules - Validation, conditional visibility and step gating are separate behaviours. `GateExpression` stays a step-level mechanism. #178 needs no new `GateExpression` types. - #178 needs three field-level rules: `equals` (conditional visibility, for example Audience = My team), and `length_at_least` and `matches_pattern` (validation). They may share one rule vocabulary, so authors do not write the same check twice. CLI - The CLI has the same meaning as the GUI. Required prompts carry `*`, and `* Required` is printed once at the start. Empty input skips an optional field. A required field is asked again. - text: a normal prompt. textarea: a clear multiline interaction. number and yes/no: a prompt that states the accepted answers. - single choice: a numbered list, choose one. multiple choice: a numbered list, choose several. - date and time: the prompt states the accepted format, for example YYYY-MM-DD and HH:MM. - wish or entity: select or search an existing object where possible. Otherwise accept an unambiguous name or ID. file: a file path. - secret: input is fully hidden. No echo and no asterisks. The prompt says "input hidden". A secret is never accepted through `--field`. - Sealed values are shown as Sealed and not prompted. Conditional fields are asked only when their condition is met, after their controlling field. - Invalid input shows a clear message and asks for the same field again. Accessibility (Grammar) - All controls work with the keyboard and a screen reader. Radio and checkbox groups have a group label and arrow keys. Date controls accept typing. The show/hide control has a text label and a state. Engineering follow-up, not part of this decision: schema fields for help text, placeholder, options, rules, visibility and field groups; renderers for radio, checkbox, secret, file and entity; `evaluateGate` in Flutter; the required check ignoring hidden fields; existing labels that contain "(optional)" or "e.g."; blank optional fields in the Dart CLI. @peterswimm
Author
Owner

Accepted (2026-10-20). The 2026-10-16 decision covers all four acceptance criteria:

  • the field kinds;
  • the default, filled, invalid, disabled and sealed states;
  • the conditional fields;
  • the CLI prompts.

Keep the 44px target. It's stricter than the WCAG 2.2 AA minimum of 24px, which is fine. The engineering follow-up is tracked separately, so this can close.

**Accepted (2026-10-20).** The 2026-10-16 decision covers all four acceptance criteria: - the field kinds; - the default, filled, invalid, disabled and sealed states; - the conditional fields; - the CLI prompts. Keep the 44px target. It's stricter than the WCAG 2.2 AA minimum of 24px, which is fine. The engineering follow-up is tracked separately, so this can close.
Author
Owner

Closing — decided, and the engineering follow-up now exists.

Accepted on 2026-10-20. All four acceptance criteria are answered by the 2026-10-16 decision: the field kinds, the default/filled/invalid/disabled/sealed states, conditional fields, and the CLI prompts. The 44px target stays.

My acceptance said the engineering follow-up was "tracked separately". It was not — it is now: Toilville/rituals-api-spec#23.

Two things I found filing it, both worth you knowing because they affect what can be built from this decision rather than the decision itself:

  • A sealed slot is not expressible today. There is no slots column on core.patterns and no route emits one, so macOS always falls through to synthesising slots from attributes.input_schema with mutability hardcoded to fungible. The sealed state you specified is undeliverable until the contract carries it. The client's own test records this as an assertion rather than a bug (ForgeAPIClientPatternsTests.swift:131).
  • form_schema is untyped in the API spec (additionalProperties: true), and attributes — the field the slot list actually travels in — is not declared at all. So there is no contract to add help text, option descriptions, rules, visibility or field groups to. That is the first item in #23.

Neither needs a design answer. GateExpression stays step-level and needs no new types, as you decided.

**Closing — decided, and the engineering follow-up now exists.** Accepted on 2026-10-20. All four acceptance criteria are answered by the 2026-10-16 decision: the field kinds, the default/filled/invalid/disabled/sealed states, conditional fields, and the CLI prompts. The 44px target stays. My acceptance said the engineering follow-up was "tracked separately". It was not — it is now: **Toilville/rituals-api-spec#23**. Two things I found filing it, both worth you knowing because they affect what can be built from this decision rather than the decision itself: - **A sealed slot is not expressible today.** There is no `slots` column on `core.patterns` and no route emits one, so macOS always falls through to synthesising slots from `attributes.input_schema` with `mutability` hardcoded to `fungible`. The sealed state you specified is undeliverable until the contract carries it. The client's own test records this as an assertion rather than a bug (`ForgeAPIClientPatternsTests.swift:131`). - **`form_schema` is untyped in the API spec** (`additionalProperties: true`), and `attributes` — the field the slot list actually travels in — is not declared at all. So there is no contract to add help text, option descriptions, rules, visibility or field groups *to*. That is the first item in #23. Neither needs a design answer. `GateExpression` stays step-level and needs no new types, as you decided.
Sign in to join this conversation.
No description provided.