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

Reference / Workflow

Tracks and Initial (the field a workflow drives)

Tracks = <Entity>.<Property>; Initial = <State>;

Names the enum field a workflow owns and the state a run starts in. No application code may write that field, and when the workflow autostarts it also supplies the field's starting value, so the entity declares no default.

stable2 examples compiled by CIworkflowauthoring

Summary#

A workflow drives one field on one entity: the field that says where a row has got to. Tracks names that field and Initial names the state a fresh run begins in.

Together they hand the field over. From that point it is the workflow's, not the application's — it moves only by transition, and reading it anywhere tells you the truth about the run. When the workflow starts with the row, it supplies the field's opening value too.

Signature#

workflow <Name> {
  Tracks  = <Entity>.<Property>;
  Initial = <State>;
  …
}

The property is an enum-typed member of the tracked entity, and every state and terminal in the workflow must be a member of that enum — so the field's type is the workflow's vocabulary of states.

Description#

When the workflow starts with the row, the field needs no default#

An Autostart = true workflow begins the moment its row exists, so Initial is the field's value from the start and the entity does not restate it:

enum ApprovalStage { Pending, Approved, Rejected }

entity Invoice {
  [Required] [MaxLength(120)] string Description;
  [Required] decimal Amount;
  ApprovalStage Stage;
  security { allow create, read, update when IsAuthenticated || IsAnonymous; }
}

workflow ExpenseApproval {
  Tracks    = Invoice.Stage;
  Autostart = true;
  Initial   = Pending;

  event Decide(bool approved);

  state Pending {
    subscribe Decide(bool approved);
    on Decide(bool approved) {
      when (approved) { goto Approved; }
      default { goto Rejected; }
    }
  }

  terminal success Approved { }
  terminal error   Rejected { Message = "rejected"; }
}

A new Invoice reads back Pending with nothing in the source saying so twice.

This matters more than it looks. Elsewhere in the language a bare enum member is required — the platform will not invent a value for it, because the first member of an enum is a position, not a decision, and reordering the members would silently change what every new row means. A tracked field is the one case where a real value is genuinely available: the workflow declared it.

Restating it is refused#

Writing the default anyway is a compile error, not a redundancy the compiler tolerates:

ApprovalStage Stage = ApprovalStage.Pending;   // 'Stage' on 'Invoice' is owned by workflow 'ExpenseApproval' …

The reason is drift. Two copies of one fact stay in step only while someone keeps them there, and the copy a reader believes is the one in the entity — so the day Initial changes and the declaration does not, the source says something false and nothing reports it. One statement of the fact cannot disagree with itself.

The field is read-only to application code#

No function, action, or object initializer may assign a tracked field; a workflow's own handlers may. This is the same ownership seen from the other side — if application code could set the field, the state would no longer mean "where this run has got to", it would mean "whatever was written last".

invoice.Stage = ApprovalStage.Approved;   // refused — the workflow drives it

To move a run, send it an event and let a route transition it.

When the run starts later, the field's earlier value is yours to state#

Initial is where the run begins, not where the row begins. Those are the same moment only when the workflow autostarts. If it does not — Autostart = false, or a condition that is not yet true — the row exists for a while with no run attached, and what the field reads in that window is a fact only you know:

enum ReportStage { Draft, InApproval, Approved, Rejected }

entity Report {
  [Required] [MaxLength(120)] string Title;
  DateTime? SubmittedAt;
  ReportStage Stage = ReportStage.Draft;
  security { allow create, read, update when IsAuthenticated || IsAnonymous; }
}

workflow ReportApproval {
  Tracks    = Report.Stage;
  Autostart = this.Item.SubmittedAt != null;   // starts on submit, not on create
  Initial   = InApproval;

  event Decide(bool approved);

  state InApproval {
    subscribe Decide(bool approved);
    on Decide(bool approved) {
      when (approved) { goto Approved; }
      default { goto Rejected; }
    }
  }

  terminal success Approved { }
  terminal error   Rejected { Message = "rejected"; }
}

Draft is a member of the tracked enum but not a state the workflow ever occupies — it is where a report sits before the workflow has anything to do. Here the default is required, and it is not refused: the workflow is not claiming to supply it.

So the question "does my tracked field need a default?" has one answer: does the workflow start when the row is created? If yes, the workflow supplies the value and you must not restate it. If no, it is yours.

Examples#

An Initial that is decided per row rather than fixed — the field then starts wherever the expression lands when the run begins, and a declared default is legal for the same reason as above:

workflow ExpenseApproval {
  Tracks  = Invoice.Stage;
  Initial = this.Item.Amount > 500 ? ApprovalStage.Pending : ApprovalStage.Approved;
  …
}

Notes#

Every state must be a member of the tracked enum. A state the enum does not name is a compile error — the two declarations are one vocabulary, so they cannot drift apart either.

Autostart decides when a run begins — and therefore whether the field's opening value is the workflow's to supply or yours to declare. That is the one thing to carry away from this page.

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…

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…

Workflow.Run (start a workflow)

Start the workflow bound to an entity's type, on that entity. Bare — `Workflow.Run(order)` — is fire-and-forget: start…