Rituals views: how a wish's available commands are presented #181

Closed
opened 2026-10-04 20:20:33 +00:00 by peterswimm · 5 comments
Owner

Part of #177. Revised: this was "wish edit and status change". You no longer design the status rules. The API supplies them.

What it's for: how a wish's available commands look. GET /wishes/{id} will return available_commands[]: the actions allowed from the current state (start, complete, cancel, reopen, block, set priority, declare a dependency, patch fields), each with its input fields. Buttons map one-to-one to commands, so no client hard-codes which status can follow which.

Please design

  • How the commands are grouped and ranked: primary, secondary, overflow
  • A command with no inputs (one click), with a few inputs (inline), or with many (a form, using #178)
  • Complete: the disposition picker (done, duplicate, superseded, won't fix, obsolete), plus "replaced by which wish?" for duplicate and superseded
  • Field patches with a "here's what changed" summary before saving (can share visuals with #187)
  • Pending state, between sending the command and the confirming event arriving, and the failure state
  • Commands that exist but aren't available to this person: hidden, or shown locked with a reason?

Acceptance criteria

  • Presentation spec for command groups, inline vs form inputs, the disposition picker and the patch summary.
  • Pending, confirmed and rejected states.
  • CLI rendering (numbered choices).
Part of #177. **Revised:** this was "wish edit and status change". You no longer design the status rules. The API supplies them. **What it's for:** how a wish's *available commands* look. `GET /wishes/{id}` will return `available_commands[]`: the actions allowed from the current state (start, complete, cancel, reopen, block, set priority, declare a dependency, patch fields), each with its input fields. Buttons map one-to-one to commands, so no client hard-codes which status can follow which. ## Please design - How the commands are grouped and ranked: primary, secondary, overflow - A command with no inputs (one click), with a few inputs (inline), or with many (a form, using #178) - **Complete**: the disposition picker (done, duplicate, superseded, won't fix, obsolete), plus "replaced by which wish?" for duplicate and superseded - Field patches with a "here's what changed" summary before saving (can share visuals with #187) - Pending state, between sending the command and the confirming event arriving, and the failure state - Commands that exist but aren't available to this person: hidden, or shown locked with a reason? ## Acceptance criteria - [ ] Presentation spec for command groups, inline vs form inputs, the disposition picker and the patch summary. - [ ] Pending, confirmed and rejected states. - [ ] CLI rendering (numbered choices).
peterswimm changed title from Rituals pattern: wish edit and status change to Rituals views: how a wish's available commands are presented 2026-10-04 20:34:13 +00:00
Member

Decisions for #181 (wish commands). They answer the acceptance criteria. They are written for wish commands. #183 can reuse them for parlay commands. The command list, ranks, severities and input schemas come from the command contract. The renderer never derives them from a command name, its inputs or the wish state. Styling follows the existing tokens.

Order and placement

  • Each command carries its own semantic rank and importance. The renderer places them. At most one primary command. Secondary commands are inline as space allows. The rest go to More. Within the same rank and importance, the API order decides.
  • A command whose severity is Destructive is never the primary or default action and never takes default focus. critical importance alone does not forbid being primary.
  • Reading and focus order follow this order, with the primary first in the graphical UI. The CLI and voice keep the same order without a visual primary, secondary or overflow. The More menu follows the same focus and Escape rules as an inline input area.

Commands and inputs

  • A command with no inputs runs from its command action, under its declared #182 severity.
  • Presentation comes from the command's input schema. Simple single-value inputs may be inline when they fit at the current width. The full #178 form is used for multi-line text, multiple choice, file, secret, conditional fields, or controls that no longer fit. There is no fixed maximum number of fields. Inline and the full form render the same schema and follow the #178 rules.
  • On compact widths an inline area may expand full width below the command bar. Opening it moves focus into it and announces it. Escape closes it and returns focus to the command.

Complete

  • The Complete command's schema renders as a #178 form: a single choice of dispositions, and a wish picker shown by the equals rule for the dispositions that need a replacement. There is no special case for the command name.
  • The schema supplies the options, a short description for each, and the default (Done). The renderer does not hard-code the default. The current requirement is that Duplicate and Superseded both require "Replaced by which wish?". Each description is read with its option. The full choice is visible before submit.
  • How Cancel differs from Won't fix and Obsolete belongs to the Forge state machine. The renderer does not distinguish them.

Confirmation

  • The contract declares each command's #182 severity: none, Simple, Review, Ceremony or Destructive. #182 is the authoritative list of severity values. A missing or invalid value is a contract error. It does not mean "no confirmation".
  • none: Submit runs the command. Simple: submitting the completed form is the confirmation, with no second dialog. A Simple command with no inputs runs with one click, with no second dialog. The CLI matches. Review, Ceremony and Destructive: the inputs are collected and validated first, then the #182 step is shown. The #182 step is not part of the #178 input schema. Cancelling it sends nothing and does not enter Pending.
  • Contract validation: a missing or invalid severity, more than one primary command, and a Destructive command declared primary are contract errors. Validation rejects them. The renderer does not silently demote or repair them.

Change summary

  • A command whose contract asks for a change summary shows it after the form is submitted. The summary lists only fields whose submitted value differs from the starting value, with the label and Before and After. Multi-line text may use the #187 line-diff language. The wish is never shown as a fake file. A changed secret shows only "Changed". Fields hidden by conditions do not appear. There is no per-field accept. To change an edit, the person goes back to the form.
  • With no effective change, Save is unavailable and the UI says there is nothing to save. The summary does not rely on colour alone, and it announces the number of changed fields.
  • The summary does not set the severity. If the command declares Review, the summary is the Review step. For another severity it comes before that severity's #182 behaviour.

States after sending

  • Pending starts only after all input and confirmation steps are done and the command is sent. There is no optimistic update. The wish keeps its state until the authoritative result arrives. The sent command is disabled so it cannot be sent twice, and focus is not lost. No timeout or "still waiting" behaviour is defined here. Pending → Failed belongs to the command lifecycle contract. The API defines the timeout. The renderer does not invent one.
  • Applied: a brief non-blocking confirmation. The wish updates from the authoritative result. That is the lasting evidence. A successful wish command that changes the wish writes a lineage entry, and Applied shows the resulting lineage proof line.
  • No change or duplicate: a successful non-error outcome ("Already done", "No change"), with server wording where available.
  • Rejected: the server's reason and errors stay inline near the command area until resolved, dismissed or replaced. Errors tied to a field, where the contract links them, show at that field. The entered values are kept. A generic 409 is a rejection. The renderer does not guess that it is a concurrent-edit conflict. If the API gives a distinct conflict result, #181 may show a dedicated conflict state.
  • Failed: persistent, with a recovery action where one exists. A retry after a transport or session failure reuses the same command ID. A rejected command is corrected and sent as a new attempt.
  • Needs a higher tier: nothing was sent. It is not Pending, Rejected or Failed. Explain the requirement and give the path to consent, using the existing tier language.
  • Results are announced through a live region. No modal result dialog. Expansion, the pending indicator and transitions respect reduced motion.

Commands that are not available

  • If a command is not valid for the wish's current state, the contract does not list it, and nothing is shown.
  • If a command is relevant but unavailable to this person (permission, tier, consent or another declared restriction), the contract may list it as locked, with a reason and a next step where one exists.
  • A locked command is quieter and clearly unavailable. Its reason is text, not tooltip-only. A tier or capability restriction uses the established permission language and lock meaning. It keeps its place in the same order. It cannot be the primary or default action and cannot be activated. It stays readable by keyboard and assistive technology, with its reason. If the person can resolve the restriction, show the next step. If the primary command is locked, no other command becomes primary.
  • Below Tier 2 there is no authoritative command list. The renderer does not invent locked commands. It shows the existing capability invitation (commands require Remote allowed).

CLI

  • One flat numbered list in the same order. Executable commands are numbered. A locked command stays in its position without a number, with its unavailable reason. No command is labelled primary. There is no default. An empty answer or Return selects nothing and exits the chooser. Ctrl-C cancels and sends nothing. An invalid or unavailable choice explains the problem and asks again.
  • After the choice: inputs follow the #178 CLI rules, the summary follows the change summary above, confirmation follows the declared #182 severity, and results print as lines. Rejected and failed outcomes include the reason and errors and exit as errors. Applied, no change and duplicate are not failures.
  • Non-interactive invocation is outside this decision. Any future one must keep the #182 rule that a confirmation is never silently bypassed.

Accessibility (Grammar)

  • All controls work with the keyboard and a screen reader. Targets are at least 44 px. The CLI uses one item per line and does not rely on colour.

@peterswimm

Decisions for #181 (wish commands). They answer the acceptance criteria. They are written for wish commands. #183 can reuse them for parlay commands. The command list, ranks, severities and input schemas come from the command contract. The renderer never derives them from a command name, its inputs or the wish state. Styling follows the existing tokens. Order and placement - Each command carries its own semantic rank and importance. The renderer places them. At most one primary command. Secondary commands are inline as space allows. The rest go to More. Within the same rank and importance, the API order decides. - A command whose severity is Destructive is never the primary or default action and never takes default focus. `critical` importance alone does not forbid being primary. - Reading and focus order follow this order, with the primary first in the graphical UI. The CLI and voice keep the same order without a visual primary, secondary or overflow. The More menu follows the same focus and Escape rules as an inline input area. Commands and inputs - A command with no inputs runs from its command action, under its declared #182 severity. - Presentation comes from the command's input schema. Simple single-value inputs may be inline when they fit at the current width. The full #178 form is used for multi-line text, multiple choice, file, secret, conditional fields, or controls that no longer fit. There is no fixed maximum number of fields. Inline and the full form render the same schema and follow the #178 rules. - On compact widths an inline area may expand full width below the command bar. Opening it moves focus into it and announces it. Escape closes it and returns focus to the command. Complete - The Complete command's schema renders as a #178 form: a single choice of dispositions, and a wish picker shown by the `equals` rule for the dispositions that need a replacement. There is no special case for the command name. - The schema supplies the options, a short description for each, and the default (Done). The renderer does not hard-code the default. The current requirement is that Duplicate and Superseded both require "Replaced by which wish?". Each description is read with its option. The full choice is visible before submit. - How Cancel differs from Won't fix and Obsolete belongs to the Forge state machine. The renderer does not distinguish them. Confirmation - The contract declares each command's #182 severity: `none`, Simple, Review, Ceremony or Destructive. #182 is the authoritative list of severity values. A missing or invalid value is a contract error. It does not mean "no confirmation". - none: Submit runs the command. Simple: submitting the completed form is the confirmation, with no second dialog. A Simple command with no inputs runs with one click, with no second dialog. The CLI matches. Review, Ceremony and Destructive: the inputs are collected and validated first, then the #182 step is shown. The #182 step is not part of the #178 input schema. Cancelling it sends nothing and does not enter Pending. - Contract validation: a missing or invalid severity, more than one primary command, and a Destructive command declared primary are contract errors. Validation rejects them. The renderer does not silently demote or repair them. Change summary - A command whose contract asks for a change summary shows it after the form is submitted. The summary lists only fields whose submitted value differs from the starting value, with the label and Before and After. Multi-line text may use the #187 line-diff language. The wish is never shown as a fake file. A changed secret shows only "Changed". Fields hidden by conditions do not appear. There is no per-field accept. To change an edit, the person goes back to the form. - With no effective change, Save is unavailable and the UI says there is nothing to save. The summary does not rely on colour alone, and it announces the number of changed fields. - The summary does not set the severity. If the command declares Review, the summary is the Review step. For another severity it comes before that severity's #182 behaviour. States after sending - Pending starts only after all input and confirmation steps are done and the command is sent. There is no optimistic update. The wish keeps its state until the authoritative result arrives. The sent command is disabled so it cannot be sent twice, and focus is not lost. No timeout or "still waiting" behaviour is defined here. Pending → Failed belongs to the command lifecycle contract. The API defines the timeout. The renderer does not invent one. - Applied: a brief non-blocking confirmation. The wish updates from the authoritative result. That is the lasting evidence. A successful wish command that changes the wish writes a lineage entry, and Applied shows the resulting lineage proof line. - No change or duplicate: a successful non-error outcome ("Already done", "No change"), with server wording where available. - Rejected: the server's reason and errors stay inline near the command area until resolved, dismissed or replaced. Errors tied to a field, where the contract links them, show at that field. The entered values are kept. A generic 409 is a rejection. The renderer does not guess that it is a concurrent-edit conflict. If the API gives a distinct conflict result, #181 may show a dedicated conflict state. - Failed: persistent, with a recovery action where one exists. A retry after a transport or session failure reuses the same command ID. A rejected command is corrected and sent as a new attempt. - Needs a higher tier: nothing was sent. It is not Pending, Rejected or Failed. Explain the requirement and give the path to consent, using the existing tier language. - Results are announced through a live region. No modal result dialog. Expansion, the pending indicator and transitions respect reduced motion. Commands that are not available - If a command is not valid for the wish's current state, the contract does not list it, and nothing is shown. - If a command is relevant but unavailable to this person (permission, tier, consent or another declared restriction), the contract may list it as locked, with a reason and a next step where one exists. - A locked command is quieter and clearly unavailable. Its reason is text, not tooltip-only. A tier or capability restriction uses the established permission language and lock meaning. It keeps its place in the same order. It cannot be the primary or default action and cannot be activated. It stays readable by keyboard and assistive technology, with its reason. If the person can resolve the restriction, show the next step. If the primary command is locked, no other command becomes primary. - Below Tier 2 there is no authoritative command list. The renderer does not invent locked commands. It shows the existing capability invitation (commands require Remote allowed). CLI - One flat numbered list in the same order. Executable commands are numbered. A locked command stays in its position without a number, with its unavailable reason. No command is labelled primary. There is no default. An empty answer or Return selects nothing and exits the chooser. Ctrl-C cancels and sends nothing. An invalid or unavailable choice explains the problem and asks again. - After the choice: inputs follow the #178 CLI rules, the summary follows the change summary above, confirmation follows the declared #182 severity, and results print as lines. Rejected and failed outcomes include the reason and errors and exit as errors. Applied, no change and duplicate are not failures. - Non-interactive invocation is outside this decision. Any future one must keep the #182 rule that a confirmation is never silently bypassed. Accessibility (Grammar) - All controls work with the keyboard and a screen reader. Targets are at least 44 px. The CLI uses one item per line and does not rely on colour. @peterswimm
Author
Owner

Accepted. This answers the three acceptance criteria, and "the contract declares, the renderer places" is the right division — it is held consistently all the way through, including the refusal to special-case Complete by command name. The Tier 2 floor for an authoritative command list also matches the client rule that nothing contacts the Forge below Tier 2.

Engineering consequences filed separately, no design input needed on either: toilville/toilville_rituals#577 (per-command rank and importance needs a schema move — intent and importance are block-level today, so a command cannot carry its own) and toilville/toilville_rituals#578 (min_tier is decoded in Flutter and never gated on, while macOS implements it — a pre-existing parity defect that your locked-command rules now govern).

Four things block implementation

Each wants a sentence, not a redesign.

1. Nothing exits Pending. The sent command is disabled, there is no optimistic update, and no timeout is defined. If the result never arrives, that command stays disabled indefinitely with no path to Failed — and Failed is the only state carrying a recovery action. #183 already requires a visible Cancel on every waiting state. Does the command lifecycle contract own the Pending → Failed transition, or should #181 define a way out?

2. Simple severity with no inputs has no confirmation. "A command with no inputs runs from its command action, under its declared severity", but "Simple: submitting the completed form is the confirmation, with no second dialog" — with no inputs there is no form to submit. #182's CLI section says Simple is [y/N]. So the same severity behaves differently in the two surfaces for the same command. Is Simple-with-no-inputs one click, or a lightweight confirmation?

3. Rank has no contract-error rule. A missing or invalid severity is a contract error. Two commands declared primary, and a Destructive command declared primary, are not covered — and the second is the one case where the renderer must override a declared rank, against the opening rule that it never derives placement. Are both contract errors, or does the renderer silently demote?

4. none extends #182's enum. #182 ratified four levels. #181 requires a fifth member, none, on the same setting, and makes a missing value an error rather than a synonym for it — which is the right call, but it means the authoritative member list now lives in two issues. Amending #182 would give it one home.

Three edits to sibling issues

Not new design. Say the word and I will make them.

5. "There is no per-field accept" contradicts #187's acceptance criteria, which require accept all, accept some, or nothing, per change. Defensible — a field patch is not a file diff — but then the change summary borrows #187's visuals without being the diff-then-apply pattern, and #187 should say so in both directions, so nobody builds partial-accept machinery for wish patches or assumes reuse the other way.

6. #187's "conflicts with a newer version" state becomes unreachable for wish commands under "a generic 409 is a rejection, the renderer does not guess that it is a concurrent-edit conflict". Agreed on the rule, but that state now depends on a distinct conflict result the API does not return, so it is an engineering dependency rather than a design deliverable.

7. Applied does not mention lineage. "The wish updates from the authoritative result. That is the lasting evidence." #177 seals the lineage proof line, #187 says every apply writes a lineage entry, and the client rule is that every action produces an auditable record. Does a wish command write a lineage entry, and does Applied show the proof line?

Smaller, non-blocking

  • "A retry reuses the same command ID" only works if the client mints the ID. If the server mints it, a transport failure means it was never learned. Worth confirming against the command contract in rituals-api-spec.
  • The CLI section does not say which CLI. #178 distinguished the Dart ritual_cli from the ritual binary. Wishes exist only in the latter, which already has named verbs — ritual wish start / complete / cancel / reopen / depend.
  • ritual wish browse is not covered. It is a full-screen triage console and the place wish commands actually get used in bulk. It is neither the graphical UI nor a one-shot CLI prompt.
  • Numbered-only selection is unstable. Locked commands hold position without a number, so a command's number shifts when availability changes and does not match its line. Letting the chooser also accept the command key costs nothing and removes both problems, and matches the named verbs that already exist.

One scope question

This is prose, and #177 defines a view's deliverable as a block_specs entry. Is the presentation spec in the first acceptance criterion closed by this comment, or does it still want a spec entry and a rendered comparison? Asking because of toilville/toilville_rituals#301 — the token comparison artifact there went a long time without a link anyone could open.

Accepted. This answers the three acceptance criteria, and "the contract declares, the renderer places" is the right division — it is held consistently all the way through, including the refusal to special-case Complete by command name. The Tier 2 floor for an authoritative command list also matches the client rule that nothing contacts the Forge below Tier 2. Engineering consequences filed separately, no design input needed on either: toilville/toilville_rituals#577 (per-command rank and importance needs a schema move — `intent` and `importance` are block-level today, so a command cannot carry its own) and toilville/toilville_rituals#578 (`min_tier` is decoded in Flutter and never gated on, while macOS implements it — a pre-existing parity defect that your locked-command rules now govern). ## Four things block implementation Each wants a sentence, not a redesign. **1. Nothing exits Pending.** The sent command is disabled, there is no optimistic update, and no timeout is defined. If the result never arrives, that command stays disabled indefinitely with no path to Failed — and Failed is the only state carrying a recovery action. #183 already requires a visible Cancel on every waiting state. Does the command lifecycle contract own the Pending → Failed transition, or should #181 define a way out? **2. Simple severity with no inputs has no confirmation.** "A command with no inputs runs from its command action, under its declared severity", but "Simple: submitting the completed form is the confirmation, with no second dialog" — with no inputs there is no form to submit. #182's CLI section says Simple is `[y/N]`. So the same severity behaves differently in the two surfaces for the same command. Is Simple-with-no-inputs one click, or a lightweight confirmation? **3. Rank has no contract-error rule.** A missing or invalid severity is a contract error. Two commands declared primary, and a Destructive command declared primary, are not covered — and the second is the one case where the renderer must override a declared rank, against the opening rule that it never derives placement. Are both contract errors, or does the renderer silently demote? **4. `none` extends #182's enum.** #182 ratified four levels. #181 requires a fifth member, `none`, on the same setting, and makes a missing value an error rather than a synonym for it — which is the right call, but it means the authoritative member list now lives in two issues. Amending #182 would give it one home. ## Three edits to sibling issues Not new design. Say the word and I will make them. **5. "There is no per-field accept" contradicts #187's acceptance criteria**, which require accept all, accept some, or nothing, per change. Defensible — a field patch is not a file diff — but then the change summary borrows #187's *visuals* without being the diff-then-apply pattern, and #187 should say so in both directions, so nobody builds partial-accept machinery for wish patches or assumes reuse the other way. **6. #187's "conflicts with a newer version" state becomes unreachable** for wish commands under "a generic 409 is a rejection, the renderer does not guess that it is a concurrent-edit conflict". Agreed on the rule, but that state now depends on a distinct conflict result the API does not return, so it is an engineering dependency rather than a design deliverable. **7. Applied does not mention lineage.** "The wish updates from the authoritative result. That is the lasting evidence." #177 seals the lineage proof line, #187 says every apply writes a lineage entry, and the client rule is that every action produces an auditable record. Does a wish command write a lineage entry, and does Applied show the proof line? ## Smaller, non-blocking - **"A retry reuses the same command ID"** only works if the client mints the ID. If the server mints it, a transport failure means it was never learned. Worth confirming against the command contract in rituals-api-spec. - **The CLI section does not say which CLI.** #178 distinguished the Dart `ritual_cli` from the `ritual` binary. Wishes exist only in the latter, which already has named verbs — `ritual wish start / complete / cancel / reopen / depend`. - **`ritual wish browse` is not covered.** It is a full-screen triage console and the place wish commands actually get used in bulk. It is neither the graphical UI nor a one-shot CLI prompt. - **Numbered-only selection is unstable.** Locked commands hold position without a number, so a command's number shifts when availability changes and does not match its line. Letting the chooser also accept the command key costs nothing and removes both problems, and matches the named verbs that already exist. ## One scope question This is prose, and #177 defines a view's deliverable as a `block_specs` entry. Is the presentation spec in the first acceptance criterion closed by this comment, or does it still want a spec entry and a rendered comparison? Asking because of toilville/toilville_rituals#301 — the token comparison artifact there went a long time without a link anyone could open.
Author
Owner

My answers to the four blocking points (2026-10-20):

  1. Pending → Failed belongs to the command lifecycle contract, with a timeout defined by the API. The renderer doesn't invent one.
  2. Simple with no inputs is one click with no second dialog, and the CLI matches. Please amend #182's Simple rule for commands with no inputs.
  3. A missing or invalid severity, two primary commands, or a Destructive primary are contract errors. Validation rejects them; nothing is silently demoted.
  4. #182 holds the full enum, including none, so there's one authoritative list.

Scope: accept the three sibling edits as written. The presentation spec in your comment counts as the spec, and a rendered comparison can be a separate request. @ville, over to you to update #181 and #182 and proceed.

**My answers to the four blocking points (2026-10-20):** 1. **Pending → Failed** belongs to the command lifecycle contract, with a timeout defined by the API. The renderer doesn't invent one. 2. **Simple with no inputs** is one click with no second dialog, and the CLI matches. Please amend #182's Simple rule for commands with no inputs. 3. **A missing or invalid severity, two primary commands, or a Destructive primary** are contract errors. Validation rejects them; nothing is silently demoted. 4. **#182 holds the full enum**, including `none`, so there's one authoritative list. **Scope:** accept the three sibling edits as written. The presentation spec in your comment counts as the spec, and a rendered comparison can be a separate request. @ville, over to you to update #181 and #182 and proceed.
Member

All requested follow-up edits are now in #181 and #182. I also added the two accepted clarifications to #187. The lineage point is now resolved in #181 as well. Ready for close/review.

All requested follow-up edits are now in #181 and #182. I also added the two accepted clarifications to #187. The lineage point is now resolved in #181 as well. Ready for close/review.
Author
Owner

Closing — decided. All four blocking points are folded in, and the engineering dependency is filed.

I re-read your decision comment and confirmed each of the four answers landed:

  1. Pending → Failed — "belongs to the command lifecycle contract. The API defines the timeout. The renderer does not invent one." ✔
  2. Simple with no inputs — amended in #182: "With no inputs, one click executes the command, with no second dialog", and the CLI matches. ✔
  3. Rank contract errors — "a missing or invalid severity, more than one primary command, and a Destructive command declared primary are contract errors. Validation rejects them. The renderer does not silently demote or repair them." ✔
  4. The full enum — #182 is now the authoritative list of all five values including none. ✔

Plus the lineage point: "A successful wish command that changes the wish writes a lineage entry, and Applied shows the resulting lineage proof line." ✔ And the three sibling edits are in #187.

What blocks implementation is now one issue, not a design question

Toilville/rituals-api-spec#22. Your opening rule — the contract declares, the renderer places — is the right division, and it turns out no surface declares any of it. I measured all three:

core.wish_available_commands ritual-core::AvailableCommand openapi.yaml
command, label, to_status, required_inputs, input_schema, form the same six the same six

No rank, no importance, no severity, no locked/reason/next-step, no timeout, no lineage reference. Outcome is applied | noop | rejected | duplicate with no conflict, and WishCommandResult carries reason but no errors.

Worth stressing that this is not spec drift — the three agree with each other. The vocabulary simply does not exist yet, so #22 sequences it spel → ritual-engine → spec, which is the order that repo requires.

One thing already satisfied, recorded so nobody re-opens it: your "a retry reuses the same command ID" works. WishCommandRequest.command_id is documented as "Idempotency key … Generated when absent", and ritual-core exposes with_command_id. The client has to mint it rather than let the server generate one, or a transport failure means the key was never learned — but that is a client rule, not a contract gap.

The four smaller points, for the record

Not blocking, and not design asks:

  • Which CLI — wishes exist only in the ritual binary, which already has named verbs (ritual wish start / complete / cancel / reopen / depend). The Dart ritual_cli has no wish surface.
  • ritual wish browse — the full-screen triage console is where wish commands get used in bulk. Neither the graphical UI nor a one-shot prompt. Treating it as a third surface of this decision rather than a gap in it.
  • Numbered-only selection — taking your flat numbered list, and letting the chooser also accept the command key, since a locked command holding position without a number makes the numbers shift. Costs nothing and matches the named verbs that already exist.
  • Scope — your comment is the presentation spec, as agreed. A rendered comparison is a separate request if one is wanted.

Engineering issues that carry this forward: rituals-api-spec#22, and Toilville/toilville_rituals#577 (rank/importance attach to a command, not a block), #578 (min_tier), #581 (confirmations as a composition), #583 (screen migrations).

**Closing — decided. All four blocking points are folded in, and the engineering dependency is filed.** I re-read your decision comment and confirmed each of the four answers landed: 1. **Pending → Failed** — "belongs to the command lifecycle contract. The API defines the timeout. The renderer does not invent one." ✔ 2. **Simple with no inputs** — amended in #182: "With no inputs, one click executes the command, with no second dialog", and the CLI matches. ✔ 3. **Rank contract errors** — "a missing or invalid severity, more than one primary command, and a Destructive command declared primary are contract errors. Validation rejects them. The renderer does not silently demote or repair them." ✔ 4. **The full enum** — #182 is now the authoritative list of all five values including `none`. ✔ Plus the lineage point: "A successful wish command that changes the wish writes a lineage entry, and Applied shows the resulting lineage proof line." ✔ And the three sibling edits are in #187. ## What blocks implementation is now one issue, not a design question **Toilville/rituals-api-spec#22.** Your opening rule — the contract declares, the renderer places — is the right division, and it turns out **no surface declares any of it**. I measured all three: | | `core.wish_available_commands` | `ritual-core::AvailableCommand` | `openapi.yaml` | |---|---|---|---| | | `command`, `label`, `to_status`, `required_inputs`, `input_schema`, `form` | the same six | the same six | No `rank`, no `importance`, no `severity`, no `locked`/reason/next-step, no timeout, no lineage reference. `Outcome` is `applied | noop | rejected | duplicate` with no `conflict`, and `WishCommandResult` carries `reason` but no `errors`. Worth stressing that this is **not spec drift** — the three agree with each other. The vocabulary simply does not exist yet, so #22 sequences it spel → ritual-engine → spec, which is the order that repo requires. One thing already satisfied, recorded so nobody re-opens it: **your "a retry reuses the same command ID" works.** `WishCommandRequest.command_id` is documented as "Idempotency key … Generated when absent", and `ritual-core` exposes `with_command_id`. The client has to mint it rather than let the server generate one, or a transport failure means the key was never learned — but that is a client rule, not a contract gap. ## The four smaller points, for the record Not blocking, and not design asks: - **Which CLI** — wishes exist only in the `ritual` binary, which already has named verbs (`ritual wish start / complete / cancel / reopen / depend`). The Dart `ritual_cli` has no wish surface. - **`ritual wish browse`** — the full-screen triage console is where wish commands get used in bulk. Neither the graphical UI nor a one-shot prompt. Treating it as a third surface of this decision rather than a gap in it. - **Numbered-only selection** — taking your flat numbered list, and letting the chooser also accept the command key, since a locked command holding position without a number makes the numbers shift. Costs nothing and matches the named verbs that already exist. - **Scope** — your comment is the presentation spec, as agreed. A rendered comparison is a separate request if one is wanted. Engineering issues that carry this forward: rituals-api-spec#22, and Toilville/toilville_rituals#577 (rank/importance attach to a command, not a block), #578 (`min_tier`), #581 (confirmations as a composition), #583 (screen migrations).
Sign in to join this conversation.
No description provided.