Skip to content

Forms on submit and binding command responses in continuations #53

Closed

Description

@woksin

Part of the Screenplay command production model: Cratis/Screenplay#309.

Why

A command can return a value to its caller, such as the id of the project it just created (Cratis/Screenplay#300, Cratis/Screenplay#303, decision 0021). A screen should be able to use it: submit the form, then navigate to the new item. Scene can't express that today:

  • Form has no OnSubmit (Source/DotNET/Model/Forms/Form.cs:15), so the form's continuation is lost.
  • Nothing binds the response. CommandOutcome.response?: unknown exists (Source/JavaScript/engine/ActionDispatcher.ts:12-17), but the engine binds nothing from it.

Example

Screenplay input:

command RegisterProject
  projectId ProjectId   generated identifier
  slug      ProjectSlug generated
  name      ProjectName
  produces event ProjectRegistered
    name ProjectName = name
  returns
    projectId = projectId
    slug      = slug

form RegisterProjectForm for RegisterProject
  field name
  on submit navigate to ProjectDetails by projectId

on submit means "execute the form's command, and on success run these actions". projectId is a returned name.

Scene contract, sketch of the change (the JSON shape and the binding path prefix are not decided; see open decisions):

{
  "name": "RegisterProjectForm",
  "forCommand": "RegisterProject",
  "fields": [{ "name": "name" }],
  "onSubmit": [                                   // new: same actions as an interaction's continuation
    {
      "kind": "navigate",
      "screen": "ProjectDetails",
      "arguments": [
        { "name": "projectId", "value": { "path": "commandResponse.projectId" } }   // new binding source
      ]
    }
  ]
}

C# model sketch:

public record Form(string Name, string ForCommand, PopulateSource? PopulateSource, IReadOnlyList<FormField> Fields)
{
    public IReadOnlyList<Behavior> Behaviors { get; init; } = [];
    public IReadOnlyList<InteractionAction> OnSubmit { get; init; } = [];   // new
}

Engine sketch: after executeCommand returns a CommandOutcome with isSuccess: true, resolve a commandResponse.<field> argument from outcome.response and hand the action to the dispatcher.

Scope / acceptance criteria

  • Forms: add OnSubmit to Form with the same continuation actions as interactions, such as navigate and open. Serialization and the TypeScript model follow.
  • Response binding: add a commandResponse binding source, so a continuation can use the whole response, or a field of it, as an argument.
    • Applies to on submit and to ExecuteCommandAction.OnSuccess.
    • The engine reads it from CommandOutcome.response.
  • Failure rule: a failed command has no response. Continuations under on failure can't bind it. Model validation rejects it; the engine never resolves it at runtime.
  • A missing field in the response (a command with no returns) is a model error, not a runtime undefined.
  • Specs: form submit navigation; response binding to a single field; whole-response binding; rejection of a binding under failure.

Open decisions

  1. Binding representation. BindingExpression is a resolved path string. Does the response binding become a path prefix (commandResponse.projectId), or a new binding record that says which command's response it reads?
  2. Response shape. A single returned value (Generated command values and returning a value to the caller Screenplay#300) is a scalar, not an object. Does commandResponse bind the scalar directly, and fields only for the returns block (Idea: returning several values from a command (returns block) Screenplay#303)?
  3. Meaning of on submit. Is it success-only, or does it also cover failure paths? The current proposal is success-only, with on failure unchanged.
  4. Typing. Does Scene carry a type for the response so a renderer can check the binding, or does it trust the producer (Stage translating Screenplay), as it does for other resolved paths?

Related

Status

State: Done — Shipped in Scene v4.84.0 (PR #273, merge fa5936d).
Branch: studio-856-auto-merge (#273)
Scope: Form onSubmit continuations bound to the command response (accepted L32 design).
Plan: Additive Form.OnSubmit and a commandResponse binding source. A producer-supplied response contract drives recursive fail-closed validation with C#/TS parity. The engine runs the continuation once, only after native Arc success, with a lexical response scope.
Delivered: Nothing yet.
Remaining: To be confirmed.
Follow-ups: None.

Activity

  1. self-assigned this
    on Sep 29, 2026
  2. github-actions commented on Oct 26, 2026

    @github-actions
    Contributor

    Closed by release v4.84.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions