Osy#the first language built for agents
Agents firstAgentic appsWorkflowsDurable Execution — built inSecurityTestingThe editorThe UI modelOne program

Reference / Workflow

Requires — named preconditions, and the live checklist

Requires { <Name> { Must = <predicate over this.Item>; Message = "…"; } … } · <Wf>.For(item).Requirements

Named conditions that must hold before something may happen, declared on a state or on a slot — and readable as a live checklist so a screen can show what is left instead of refusing the button afterwards. Where you declare it decides what it gates: on a slot it gates the deposit into that wait; on a state it IS the state's completion condition.

stable1 example compiled by CIworkflowauthoring

Summary#

Requires names the conditions standing between an item and progress, and the platform both enforces them and reports them. The reporting half is the point: a rule the engine will refuse is a rule a screen should be able to show before anybody presses anything.

Signature#

Requires {
  <Name> {
    Must    = <predicate over this.Item>;
    Message = "what to tell a human when it does not hold";
  }
  …
}

Read the live checklist back:

<Wf>.For(item).Requirements            // List<RequirementStatus> — the state's AND its slots'
<Wf>.For(item).<Slot>.Requirements     // just that one wait's

Description#

Where you declare it decides what it gates#

This is the distinction to hold, and the two are not variations of one rule:

declared ongatesmeans
a slot (subscribe … { Requires { … } })the deposit into that wait"you may not resolve without a root cause"
a state (state X { Requires { … } })the state's completion"this state is done when all of these hold"

A slot's criteria are checked when somebody tries to fill it — an unmet one refuses the deposit and hands back which criteria failed, with their messages. A state's criteria replace the default "every armed slot is satisfied" completion test, which is how a quorum is expressed: three voters armed, done at two.

The checklist returns BOTH, and each row names its slot#

<Wf>.For(item).Requirements returns the state's own criteria first, then each of its slots' in declaration order. Every row carries Slot — the wait it gates, or null for a state criterion.

That member is not decoration. "Record a root cause before you can resolve" and "triage it before this state is done" are different sentences about different acts, and on one flat list a screen cannot group them or say which button each belongs to.

The unscoped read used to return the state's criteria ALONE, so a slot-scoped Requires — the commonest kind — came back empty. The gate still refused correctly, which made it worse than a plain omission: the app rendered a checklist saying there was nothing left to do and then refused the button. An empty checklist and a satisfied one are the same screen.

The rows are LIVE#

Every predicate is evaluated against this.Item at the moment of the read, and nothing is stored. Re-reading after a change gives the new answer — so a form can re-check as fields are filled.

Examples#

A ticket that must be triaged before the state is done, and cannot be resolved without a root cause:

enum Stage { Working, Done }

[Principal] entity Agent {
  [Required, MaxLength(80)] string Name;
  security { allow read when IsAuthenticated || IsAnonymous; }
}

entity Ticket {
  [Required, MaxLength(120)] string Subject;
  [MaxLength(200)] string? RootCause;
  bool Triaged;
  Stage State;
  security { allow read, create, update when IsAuthenticated || IsAnonymous; }
}

workflow TicketFlow {
  Tracks    = Ticket.State;
  Autostart = true;
  Initial   = Working;

  event Resolve();

  state Working {
    // About the STATE: it is not done until this holds.
    Requires {
      Triaged { Must = this.Item.Triaged; Message = "triage it first"; }
    }

    // About filling THIS WAIT: the deposit is refused until this holds.
    subscribe Resolve() as Resolver {
      Requires {
        RootCause { Must = this.Item.RootCause != null; Message = "record a root cause"; }
      }
    }

    on Resolver { goto Done; }
  }

  terminal success Done { }
}

// What is left to do — everything, grouped by what it blocks.
List<Osyrin.Workflow.RequirementStatus> Outstanding(Ticket ticket) {
  return TicketFlow.For(ticket).Requirements.Where(r => !r.Met).ToList();
}

// Just the wait's own gate — what to show beside the Resolve button.
List<Osyrin.Workflow.RequirementStatus> BeforeResolving(Ticket ticket) {
  return TicketFlow.For(ticket).Resolver.Requirements.ToList();
}

Notes#

Requires is not authorization. It gates the base fact, never the person: "is the work complete", not "may you do this". Who may fill a wait is the slot's Candidates, checked first — a refusal there is a WorkflowAuthorizationException, while an unmet criterion is a RequirementsNotMet carrying the failed criteria. A UI tells them apart deliberately: one greys a button with a checklist, the other should not have offered it.

A refused deposit is recorded. The refusal writes a Refused row to the trail, so "why did this never get resolved" has an answer.

complete when is the other way to say a state is done. complete when (<predicate>) goto <State> states one condition and where it goes; a state-level Requires states several NAMED ones with messages, and is what you want when a human needs to be told which is missing.

See also#

Related

subscribe

Declares that a workflow state waits on an event, and configures the wait — who may hold it, who may hand it on…

Transitions — where this item may go next

One row per move this instance can make right now, with each arm's guard evaluated against it. A board offers only the…

Acting on an inbox row (deposit, claim, release)

Answer a queued slot from the row itself. The event is named at the call site because a queue's rows are heterogeneous…

complete when (a state's own completion condition)

Declare, once on a state, the condition under which that state is finished and where the run goes next. It is…

Candidates (slot)

Declares WHO may hold or satisfy a `subscribe` slot. `Candidates` is one expression surface that dispatches on its…