MentorMind Varro eduKG
Actions & workflows

Module 03

Actions & workflows

Governed actions, ordered workflows, and reads.

Learning intent

By the end of this module you will be able to

  • Explain how actions, queries, and workflows add governed behaviour to a checked contract.
  • Separate read-only queries from mutating actions and ordered workflows.

Success criteria

  • Classify an operation as action, query, or workflow from its semantics.
  • Explain why workflow order is load-bearing.
  • Describe preview-by-default for a mutating action.

Retrieval warm-up

Before the new material

Recall

Does the `ask` verb ever mutate state? If not, which verbs do?

Answer

No. `ask` is read-only; it retrieves authoritative truth. Only `run` (execute an action) and `create` (make a new artifact) mutate, and both under preview-by-default.

Recall

Both `show` and `ask` are read-only. What distinguishes them?

Answer

`ask` retrieves a value into the conversation; `show` renders a selection for presentation (a view or table). Same read, different intent.

Recall

What happens in a workflow when a `check` step fails?

Answer

`check` validates an artifact against its contract and emits diagnostics; it never executes. On failure the workflow fails closed, so any following `create` or `run` does not proceed.

Learner readiness

Before this reference page

Assumed terms:

Terms introduced or strengthened here:

You are ready to continue when you can:

  • distinguish a mutating action from a read-only query
  • explain why workflow order is load-bearing
  • state why preview-by-default is safer than direct mutation

Concept · grounded · RICH E4

action

A governed, risk-typed operation with declared inputs.

Worked examples

action authorize-ignition {
  input authorization_lane: string required
  risk high
  lowers-to "helios://action/authorize-ignition"
}

An `action` declares a governed operation: its typed inputs, a risk class, and a `lowers-to` target. It is invoked by the `run` verb and is subject to preview-by-default.

action parse-paper {
  risk low
  binding required
  input source_paper_ref: uri required
  input parser_kg_ref: uri required
}

An `action` is a governed mutation contract with four parts: a `risk` class, a `binding` (whether a concrete lowering target is required), and typed `input`s. The action does not contain an implementation body — it declares the contract and `lowers-to` an executable target downstream. Evaluate the separation: the risk class and binding are reviewable governance metadata sitting in front of the code, so an approver reasons about blast radius from the contract alone, before any implementation exists.

Try it

Inside a complete refund-desk `system`, author three governed `action`s — request, approve and pay a refund — that demonstrate the action contract: a `risk` class, a `binding`, and declared `input`s. Make the read-ish request action `risk low` / `binding optional` and the money-moving actions `risk confirm_required` / `binding required`. The full system must pass `varro check vsl` with zero errors.

action approve-refund {
  risk confirm_required
  binding required
  input refund_id: string required
}

# request-refund (risk low) and pay-refund actions still TODO
Model answer (passes varro check vsl)
system mentormind.payments.refund_desk {
  mission "Specify a governed refund desk where each mutation is a risk-typed action with declared inputs and a lowering target, under research authority"
  authority lane "operator:research"
  domain education
  maturity draft

  context root = "helios://local/mentormind/payments/refund-desk?view=detail&tab=contract&bind=auto"

  enum RefundState {
    value requested
    value approved
    value paid
    value rejected
  }

  type Refund {
    field refund_id: string required
    field amount: number required
    field reason: markdown required
    field state: RefundState required
  }

  compile refund-runtime -> varro

  action request-refund {
    risk low
    binding optional
    input order_id: string required
    input amount: number required
  }

  action approve-refund {
    risk confirm_required
    binding required
    input refund_id: string required
  }

  action pay-refund {
    risk confirm_required
    binding required
    input refund_id: string required
  }

  query refunds.approved {
    output Refund[]
  }

  workflow settle-refund {
    inspect selection
    resolve refunds.approved
    check approve-refund
    check pay-refund
    render center.detail
  }

  runtime specification {
    host governed
    commit-mode host-only
  }
}

Try it

Author a complete governed room-booking VSL system that composes a system header, enum, type, compile target, action, query, workflow, and runtime block, then validate it with `varro check vsl`.

system mentormind.facilities.room_booking {
  mission "..."
  authority lane "operator:research"
  domain education
  maturity draft
  # full contract still TODO
}
Model answer (passes varro check vsl)
system mentormind.facilities.room_booking {
  mission "Specify a governed room-booking surface for a campus, exposing booking state as typed contracts under research authority"
  authority lane "operator:research"
  domain education
  maturity draft

  context root = "helios://local/mentormind/facilities/room-booking?view=detail&tab=contract&bind=auto"

  enum RoomState {
    value free
    value held
    value booked
  }

  type Room {
    field room_id: string required
    field capacity: integer required
    field state: RoomState required
  }

  compile booking-runtime -> varro

  action hold-room {
    risk low
    binding required
    input room_id: string required
  }

  query rooms.free {
    output Room[]
  }

  workflow reserve-room {
    inspect selection
    resolve rooms.free
    check hold-room
    render center.detail
  }

  runtime specification {
    host governed
    commit-mode host-only
  }
}

Common trap

❌ Learners assume an `action` just executes code with no risk gating.

Correction

✅ An `action` is risk-typed and governed: it declares inputs, a risk class, and a lowers-to target, and it runs under preview-by-default. There is no ungoverned action.

Check yourself

What does an `action` declaration carry beyond its inputs?

Answer

A risk class and a `lowers-to` target, and it runs under preview-by-default. There is no ungoverned action; it is invoked by the `run` verb.

Concept · grounded · RICH E4

workflow

An ordered sequence of steps composing actions/queries.

Worked examples

workflow classify-year10-evidence {
  ask imported-artefact
  show allowed-claims
  check evidence-class
  create evidence-class-mapping
}

A `workflow` composes the five verbs into an ordered behaviour. Read top to bottom: gather, present, validate, then create. Order matters because each step can depend on the last.

workflow parse-paper-pipeline {
  inspect selection
  resolve parser-knowledge
  resolve source-paper
  check validate-parser-spec
  render center.detail
  emit route gate.round_trip
}

A `workflow` is a staged behaviour sequence written as verb + target pairs (`inspect`, `resolve`, `check`, `render`, `emit`...). It composes the declared queries and actions into an ordered pipeline; the ordering is the contract. Note these stage verbs are workflow-internal and distinct from the five-verb command grammar (`ask/show/check/run/create`) at the conversation surface. Evaluate it as a declarative DAG-of-steps: the spec states the sequence and bindings, and lowering generates the executor, so reordering is a spec edit, not a code refactor.

Try it

Inside a complete publishing `system` that already declares `submit-review` and `publish-article` actions plus an `articles.in-review` query, author a `workflow` that sequences them: inspect the selection, resolve the query, check the submit action, propose then check the publish action, render a view and emit a route. Declare the route you emit. The full system must pass `varro check vsl` with zero errors.

workflow review-and-publish {
  inspect selection
  resolve articles.in-review
  check submit-review
  # propose + check publish-article, render, emit route still TODO
}
Model answer (passes varro check vsl)
system mentormind.publishing.review_pipeline {
  mission "Specify an article review pipeline as an ordered workflow composing inspect, resolve, check and render steps under research authority"
  authority lane "operator:research"
  domain education
  maturity draft

  context root = "helios://local/mentormind/publishing/review-pipeline?view=detail&tab=contract&bind=auto"

  enum Stage {
    value drafting
    value review
    value published
  }

  type Article {
    field article_id: string required
    field title: markdown required
    field stage: Stage required
    field word_count: integer required
  }

  compile pipeline-runtime -> varro

  action submit-review {
    risk low
    binding required
    input article_id: string required
  }

  action publish-article {
    risk confirm_required
    binding required
    input article_id: string required
  }

  query articles.in-review {
    output Article[]
  }

  workflow review-and-publish {
    inspect selection
    resolve articles.in-review
    check submit-review
    propose publish-article
    check publish-article
    render center.detail
    emit route pipeline.published
  }

  route pipeline.published = "helios://local/mentormind/publishing/review-pipeline/published?view=detail&tab=state&bind=auto"

  runtime specification {
    host governed
    commit-mode host-only
  }
}

Try it

Author a complete governed room-booking VSL system that composes a system header, enum, type, compile target, action, query, workflow, and runtime block, then validate it with `varro check vsl`.

system mentormind.facilities.room_booking {
  mission "..."
  authority lane "operator:research"
  domain education
  maturity draft
  # full contract still TODO
}
Model answer (passes varro check vsl)
system mentormind.facilities.room_booking {
  mission "Specify a governed room-booking surface for a campus, exposing booking state as typed contracts under research authority"
  authority lane "operator:research"
  domain education
  maturity draft

  context root = "helios://local/mentormind/facilities/room-booking?view=detail&tab=contract&bind=auto"

  enum RoomState {
    value free
    value held
    value booked
  }

  type Room {
    field room_id: string required
    field capacity: integer required
    field state: RoomState required
  }

  compile booking-runtime -> varro

  action hold-room {
    risk low
    binding required
    input room_id: string required
  }

  query rooms.free {
    output Room[]
  }

  workflow reserve-room {
    inspect selection
    resolve rooms.free
    check hold-room
    render center.detail
  }

  runtime specification {
    host governed
    commit-mode host-only
  }
}

Common trap

❌ Learners think workflow steps are an unordered set that may run in parallel.

Correction

✅ A `workflow` is an ordered sequence. Steps run top to bottom; a `check` placed before a `create` exists precisely so the create cannot run if the check fails.

Check yourself

Do the steps of a `workflow` run in order, and why does it matter?

Answer

Yes, top to bottom. Order matters because steps depend on earlier ones; a `check` before a `create` blocks the create when the check fails.

Concept · grounded · RICH E4

query

A read of state with declared output and authorities.

Worked examples

query parsed-papers {
  output ParsedPaper[]
  authority knowledge-graph
}

A `query` in VSL is a named read contract: it declares an `output` type (here a `ParsedPaper[]`) and the `authority` it reads from. It never mutates. The declared output is part of the contract surface, so an agent can statically know the shape of what a query returns without executing it. Contrast the `ask`/`show` verbs of the five-verb command grammar: those are interactive read commands at the conversation surface, whereas a `query` is a reusable declared contract inside a `system`.

query open-gates { output GateStatus[] authority knowledge-graph }
action run-round-trip-gate {
  risk none
  binding required
  input parsed_paper_ref: uri required
}

Place a `query` and an `action` side by side: the query has only an `output` and an `authority` and carries no `risk` because it cannot change state; the action carries a `risk` class and a `binding` precisely because it can. Evaluate the design: putting the mutate/read split into the grammar (queries simply cannot declare a risk class) means a reviewer never has to audit a query for side effects — the absence of a risk field is a proof obligation discharged by parsing, not by reading the body.

Try it

Inside a complete analytics `system` that declares `Cohort` and `Metric` types, author three read-only `query` contracts, each declaring a typed `output`: all cohorts, all metrics, and only the rising metrics. Use a collection output (`Type[]`) where appropriate. The full system must pass `varro check vsl` with zero errors.

query cohorts.all {
  output Cohort[]
}

# metrics.by-cohort and metrics.rising query contracts still TODO
Model answer (passes varro check vsl)
system mentormind.analytics.cohort_metrics {
  mission "Specify read-only cohort analytics as named query contracts each declaring a typed output, under research authority"
  authority lane "operator:research"
  domain education
  maturity draft

  context root = "helios://local/mentormind/analytics/cohort-metrics?view=detail&tab=contract&bind=auto"

  enum Trend {
    value rising
    value flat
    value falling
  }

  type Cohort {
    field cohort_id: string required
    field label: string required
    field learner_count: integer required
  }

  type Metric {
    field metric_id: string required
    field cohort_ref: string required
    field mastery: number required
    field trend: Trend required
  }

  compile analytics-runtime -> varro

  action recompute-metric {
    risk low
    binding optional
    input cohort_id: string required
  }

  query cohorts.all {
    output Cohort[]
  }

  query metrics.by-cohort {
    output Metric[]
  }

  query metrics.rising {
    output Metric[]
  }

  workflow surface-metrics {
    inspect selection
    resolve cohorts.all
    resolve metrics.rising
    check recompute-metric
    render center.table
  }

  runtime specification {
    host governed
    commit-mode host-only
  }
}

Try it

Author a complete governed room-booking VSL system that composes a system header, enum, type, compile target, action, query, workflow, and runtime block, then validate it with `varro check vsl`.

system mentormind.facilities.room_booking {
  mission "..."
  authority lane "operator:research"
  domain education
  maturity draft
  # full contract still TODO
}
Model answer (passes varro check vsl)
system mentormind.facilities.room_booking {
  mission "Specify a governed room-booking surface for a campus, exposing booking state as typed contracts under research authority"
  authority lane "operator:research"
  domain education
  maturity draft

  context root = "helios://local/mentormind/facilities/room-booking?view=detail&tab=contract&bind=auto"

  enum RoomState {
    value free
    value held
    value booked
  }

  type Room {
    field room_id: string required
    field capacity: integer required
    field state: RoomState required
  }

  compile booking-runtime -> varro

  action hold-room {
    risk low
    binding required
    input room_id: string required
  }

  query rooms.free {
    output Room[]
  }

  workflow reserve-room {
    inspect selection
    resolve rooms.free
    check hold-room
    render center.detail
  }

  runtime specification {
    host governed
    commit-mode host-only
  }
}

Common trap

❌ Because SQL `query` can call functions with side effects and a GraphQL field resolver can mutate, an engineer assumes a VSL `query` is just "any callable" and may write.

Correction

✅ In VSL the read/write split is in the grammar: a `query` declares only output + authority and is non-mutating; anything that changes state is an `action` with a declared risk class and binding. The category boundary is enforced by the parser, not by convention.

Check yourself

A `query` declares an output type and an authority. Can it also declare a risk class and mutate state? Why or why not?

Answer

No. A query is a read contract: it declares output + authority and is non-mutating by construction. Mutation is the province of `action` (risk-typed, bound). The grammar makes the read/write distinction structural, so a query cannot carry a risk class.

Module assessment · scored

Module assessment: behaviour constructs (workflows, actions, queries)

  1. A `workflow` lists steps A, B, C. A developer assumes the scheduler may run them in any order for parallelism. Why is that unsound?

  2. Coming from GraphQL/SQL, a dev writes a `query` that also updates a counter. Why does the behaviour model forbid this?

  3. An `action` is described as running 'without governance' by a teammate refactoring it for speed. Why is that a misconception about the construct?

  4. Why is a workflow composing actions and queries the right place for sequencing logic, rather than putting that logic on a type?