Writing a workflow
Load this card when a task adds or changes a workflow: a request editor the
service opens with EditRequest or WorkflowRequest, its Next/Submit and
PrevRequest actions, its completion, or the service's handling of a changed
earlier page. Its rules are generated from the
workflow contract, which
owns them. The protocol itself follows the
Endpoint protocol card; master-detail placement
follows the pane contract,
and local draft recovery follows
CacheIdentity.
Order of work
- Choose how each editor is entered:
EditRequestwith a complete value orWorkflowRequestfrom a Response (workflow.edit-request.*,workflow.workflow-request.*,protocol.edit-request.*), and declare the replies each request admits (workflow.next-response.*). - Declare the previous actions and their construction (
workflow.prev.*,workflow.cookie.*,workflow.back.*). - Design completion and progress (
workflow.completion.*,workflow.progress.*). - Make the service reconcile a changed earlier page before it continues
(
workflow.saved-pages.*), then compile with Studio and fix every reported rule.
Rules to apply
Studio does not check these rules. Each is quoted from its owner.
Eligibility and field placement
Owner: annotations.md#eligibility-and-field-placement
draft.eligibility.small-requests:CacheIdentityis a Boolean field attribute. A workflow page is cacheable only when at least one root-record field has effectiveCacheIdentity=true. Use it for large workflow requests where losing completed field edits would require substantial re-entry. Small requests must omit CacheIdentity; clients do not guess eligibility from payload size or field count.
Identity, revisions, and application responsibility
Owner: annotations.md#identity-revisions-and-application-responsibility
draft.revisions.application-owned: Protocol changes are allowed. Application data revisions are independent of protocol/schema versions; tasks need not detect protocol changes. Management tools guard schema compatibility, and clients reject incompatible cached data at restoration. Compatible protocol changes alone do not invalidate drafts.
Protocol roles
Owner: protocols.md#protocol-roles
protocol.edit-request.request-variant:EditRequestis a correlated server reply carrying a completeRequestvalue: its variant must carryRequest, and a Request-only variant suffices. EditRequest is an operation, not an additional schema role, and receiving it does not invoke the request.
Workflowrequest semi role
Owner: protocols.md#workflowrequest-semi-role
workflow.workflow-request.opens-editor(judgment): WorkflowRequest semi-role.WorkflowRequestspecializesResponsethrough a schema attribute.NextRequestidentifies available next request actions;WorkflowRequestrequires the client to construct and open its named request immediately when the variant is received as a correlated Response.
Protocol roles
Owner: protocols.md#protocol-roles
workflow.workflow-request.awaits-submission(judgment): The client uses the received response under the reference context and request construction rule above. The response may contain arbitrary valid cookie data or be empty; it is not shown as an intermediate screen. The resulting editor may be incomplete and awaits explicit user submission, even for Nil, already complete, or entirely Fixed/ReadOnly payloads. Normal request visibility and access rules apply; an unavailable target must not be silently replaced with another request.workflow.workflow-request.dual-role(judgment): For example, a variant carrying bothResponseandSubscriptionDatacan useWorkflowRequestto open its named editor on correlated Response delivery. Subscription delivery of that same variant updates subscribed data without opening the editor, under the shared role rule above.
Workflow interactions
Owner: protocols.md#workflow-interactions
workflow.mix-state-machine(judgment): This is the unreleased workflow interaction contract. A protocol Union may mix state-machine interactions, whose stable screen is a response or subscription report, and workflows, whose stable screen is a request editor. The service selects every resulting transition. There is no separate machine-type marker.
Valid data and editor state
Owner: protocols.md#valid-data-and-editor-state
workflow.valid-data(judgment): Every transmitted value must be structurally valid for its Avro schema. An editor can also contain an undefined field: it has neither a valid value nor an encoded null. A required undefined field prevents submission. Validation expressions may observe undefined as null, but this does not change the wire schema or make the field nullable.workflow.no-partial-requests(judgment): No partially filled request is transmitted for initialization or backend draft persistence. The CacheIdentity contract defines opt-in local recovery of committed field values on large workflow pages, independently of backend persistence. Uncommitted control text is discarded on exit. Without CacheIdentity, all unfinished page edits are discarded and the application resumes from its own saved, valid data.
Entering and continuing a workflow
Owner: protocols.md#entering-and-continuing-a-workflow
workflow.leading-page-row(judgment): A resolved editing Request can identify its leading-page row through an enum/protocol association, including direct entry; see master-detail panes.workflow.edit-request.complete-value: EditRequest: a server-to-client opcode carrying one complete, structurally valid Request variant from the Endpoint protocol Union. It is a correlated reply to the pending request that owns the next transition, for example Start, Submit, Prev, Resume, an action, or a link, and completes that operation under the shared role rule. The client uses the supplied value directly, without running CopyFrom or default initialization over it.workflow.edit-request.awaits-submission(judgment): EditRequest also awaits explicit user submission, including Nil, already complete, or entirely Fixed/ReadOnly values; it must not automatically resubmit them. Its reference context follows the current reference context rule.workflow.edit-request.replaces-editor(judgment): EditRequest replaces the active editor even when the returned variant is the same variant as the previous step; variant identity is not step identity.workflow.delivered-editor-access(judgment): When a delivered editor, whether the target of a WorkflowRequest or the variant of an EditRequest, is beyond the user's AccessPrivilege, the client refuses the reply by the error procedure and keeps the originating surface, its reference context, and its draft.ExcludeUIandLinkOnlygovern what a client offers, not what the service delivers: a delivered editor that carries either is installed.workflow.next-response.reply-membership(judgment): When NextResponse is declared, a successful transition reply must be one of its named variants. Error responses and standardized failure statuses use the shared error/retry path and do not need to appear in this navigation list. These declarations constrain the possible replies; the client never selects the service's reply for it.
Submit, previous actions, and completion
Owner: protocols.md#submit-previous-actions-and-completion
workflow.submit.button(judgment): The workflow's Next/Submit button validates and sends the current request. It is not a chooser for the state machine's NextRequest actions.workflow.submit.captions(judgment): These are control roles: a page's Display Template may supply localized button captions without changing their behavior, eligibility, or request names inside a chooser.workflow.prev.order(judgment): Their declared order is presentation order. With one eligible target, Back selects it directly; with several, Back opens a chooser using their ordinary localized names. This is the workflow's in-app Go Back action, distinct from screen-navigation and system Back. With none, there is no workflow Back action.workflow.prev.master-detail-suppressed(judgment): While the editor is embedded in a master-detail trailing pane, clients suppress all workflow Back controls, including the chooser and shortcuts, under the pane contract. The pane offers Close and Next; the leading pane owns navigation for the two-panel arrangement, and the App must make that navigation complete.workflow.prev.composer-none(judgment): A transient request composer likewise never offers a workflow Back control: it was opened above a displayed page by the person's own choice, and its Cancel already closes it while idle. The declaration remains available for ordinary full-page workflow presentation. Suppression depends on the editor's actual placement, never on inferred equivalence between requests.workflow.go-to-composer(judgment): A request whose purpose is to choose where to go, such as a section chooser offered as a "Go to" action, belongs in a composer above the page it leaves. There is no reason to deliver it as a workflow editor through WorkflowRequest or EditRequest: such a page would exist only to be left, and the service already opens the chosen destination as the reply to the composed request.workflow.prev.not-history(judgment):PrevRequestnames this action declaration, not a stored previous request or reference context. Its declared target list is not dynamically filtered byConstrainedBy.workflow.prev.construction(judgment): Each selected Back action constructs an ordinary request using the shared CopyFrom rules. Its reference object is the current editor's selected payload, independently of the unfinished editor's validity.workflow.cookie.app-owned(judgment): A hidden application-defined cookie can identify any of several concurrent workflows. Cookie design and historical selection belong to the application. Accepting a historical edit must follow the saved-page consistency rule. Studio validates declarations and statically decidable mapping rules; it does not prove that cookie fields are initialized on every path.workflow.prev.complete-or-not-sent(judgment): The client refuses to encode or send an incomplete or invalid previous action. Only the newly constructed action request is encoded; the current draft is not.workflow.back.abandonment(judgment): Back expresses abandonment of the current edit. The backend decides what to return, including a complete historical request through EditRequest. The client does not navigate through cached historical requests. Construct and validate the action before sending it; a rejected selection or failed attempt follows the ordinary error/retry procedure. There is no server-side undo implied by discarding the current edit.workflow.completion.terminal(judgment): For a workflow Submit or Prev operation, a non-error response without WorkflowRequest ends the editor and is presented as the resulting stable response. NextRequest, if present, offers its declared state-machine actions. With no NextRequest, the response is a valid terminal screen, for example a FormattedText congratulations message. The state-machine acknowledgement rule for ordinary requests outside a workflow remains unchanged.workflow.native-back.no-prev(judgment): Only the explicit workflow Prev and Next/Submit buttons have workflow semantics. Native Back, dismissal, or leaving the interaction sends no previous action. Local draft retention follows CacheIdentity; Control endpoint navigation follows the landing rule. Closing a field picker or nested editor returns to the same parent editor. Cancelling an action chooser changes neither data nor workflow state.
Saved-page consistency after historical edits
Owner: protocols.md#saved-page-consistency-after-historical-edits
workflow.saved-pages.compare(judgment): Compare the submitted page answers with the saved answers before changing any stored page. Ignore transport cookies, navigation metadata, and presentation-only values in this comparison. An unchanged Next is review/navigation: preserve all saved pages and completion state. Opening, canceling, or rejecting an edit also preserves saved history.workflow.saved-pages.reconcile(judgment): When the backend accepts a changed page, it must, for each stored page that the changed answers affect, do one of the following: recompute and revalidate that page's data against the revised answers and save the consistent result; erase that page's data; or keep the page's answers and mark the page unanswered, so that it is asked again, with those answers, before the workflow can complete. This is a mandatory application rule, not an optional client navigation policy.workflow.saved-pages.atomic(judgment): Do not erase before validation or leave a partially updated state visible. No page remains answered for resume, calculation, or completion merely because it was valid before the change; derived results, cookies, allowed-choice sets, and saved progress must agree with the revised answers as well.workflow.saved-pages.before-continuing(judgment): Complete this reconciliation before continuing or resuming the workflow. Resume and EditRequest use only the consistent stored state; retries, concurrent editors, and late submissions must not restore data from before the change. The backend owns persistence and consistency; the client and Studio cannot infer application dependencies or repair saved pages.workflow.saved-pages.opening-changes-nothing(judgment): Opening a historical editor does not itself change anything. An abandoned or rejected draft changes no saved history; the rule applies when the backend accepts and saves the change. For example, changing a saved filing status from joint to single erases the spouse data that no page of a single filer owns, and asks again each affected page whose stored answers no longer validate, before the interview can complete.
Validation context and progress
Owner: protocols.md#validation-context-and-progress
workflow.validation-context(judgment): Editable requests use the shared path contract. Supported references use the current reference context, including on inherited types and hidden fields. CopyFrom remains separate initialization metadata with its defined invocation source. EditRequest context follows that same owner; it does not retain an older response in its place.workflow.progress.formatted-text(judgment): Progress is an ordinary FormattedText field following the static read-only placement and request-value contract. For example, the service can supply it in EditRequest or response data copied into a WorkflowRequest editor. There is no progress opcode, percentage schema, fixed step count, or client inference from previous-action lists or history.
Rules Studio enforces
Studio rejects a source that breaks one of these rules. The owner holds the full rule.
Workflowrequest semi role
Owner: protocols.md#workflowrequest-semi-role
workflow.workflow-request.declaration: WorkflowRequest is on a Response, names exactly one Request variant of the protocol, and excludes NextRequest and Error
Entering and continuing a workflow
Owner: protocols.md#entering-and-continuing-a-workflow
workflow.next-response.kinds: NextResponse on a Request or SubscriptionRequest lists Response, SubscriptionData or Request variants
Submit, previous actions, and completion
Owner: protocols.md#submit-previous-actions-and-completion
workflow.prev.declaration: PrevRequest goes on a Request variant and names distinct Request variants of the same protocol