Skip to main content
Use this when one supervisor should not decide alone: a section supervisor owns a stage, and a more senior supervisor (a policy engine, an AI agent) reviews what the section cannot settle, before a person is bothered. Prerequisites: Assign a supervisor. Add -H "X-API-Key: $YUKTA_API_KEY" if your server has keys. A supervisor lists parents (zero or more). A parent is another supervisor; a supervisor cannot be its own ancestor (saving refuses a cycle) and a chain has at most four levels.

Steps

1

Create the parents

A parent decides like any supervisor, with whatever engine suits it. This one is a rules supervisor that refuses a restart of the slow workflow. Save as policy.json:
policy.json
Create more parents the same way: a second rules supervisor, or an http supervisor run by an AI agent (Write an agent supervisor).
2

Let the parents watch, without owning

A parent that is not running is silent, and a supervisor runs only when it has an assignment. The one-owner rule means a parent cannot also own what the child owns. Give each parent a global assignment: it watches every run and owns nothing, so it never conflicts.
A global supervisor reviews and advises but sends no command of its own: what it sends is refused as not-owned.
3

Point the child at its parents

Add parents (in the order they are asked) and, optionally, a delegation per kind. release-guard from Assign a supervisor, with a rule that holds a restart for approval:
The delegation kinds are APPROVAL (one of the supervisor’s own commands needs approval), INPUT (it escalates an input request) and CONSULT (its engine asks a question). The modes:timeoutSeconds (default 30) is how long one parent has to answer. A refusal from any parent asked ends the command. A parent with no opinion, a silent one, or a HIGH risk need (which only a person approves) leaves the decision to a person.
4

Route input requests through the parents

For input requests the route decides who is asked and in which order; the tier selector supervisor:parent sends an escalated request to the parents of the supervisor that handed it up, with the tier’s own mode, quorum and timeout. This is server configuration (application.yml, or the matching environment variables):
A parent is asked only if it watches the request’s node. A tier with nobody to ask is skipped at once. A supervisor approves another’s parked command or request only from above it in the hierarchy, never a HIGH risk request, and never one of a run it started. More on routes: Route input requests.
A parent attached with global owns nothing, and the server refuses any command a supervisor sends for a unit it does not own. Try a request in a test session and read GET /api/supervisors/decisions: an answer from a parent that shows not-owned or out-of-scope means that parent can advise and review, but the owner or a person must answer for those nodes.

Verify it worked

Check the wiring, then send the held command as a person and read the outcome:
policy is asked first because release-guard parked the restart for approval, and its rule refuses. The restart is dropped and the person who sent it gets a 403, with the parent named in the reason:
The log records CONTROL_REJECTED, and GET /api/supervisors/decisions?supervisor=release-guard shows the outcome. Now change the rule’s if to match nothing (for example workflowId == 'prod-deploy') and send the restart again. No parent refuses, so the outcome depends on what the parents say. One that allows lets the command through, with its original sender, and the owner reviews it again (a denial still applies). When no parent decides (none running, all silent, or a HIGH risk need) the command is parked for a person: the server answers 202 Accepted:
The request appears in GET /api/sessions/steer/inputs?state=OPEN with the chain it went through, the parents asked (allow, deny, no-opinion or silent) before it reached a person, and a person approves it in the Inputs panel.

Variations

  • Limits. Parked commands count against the open request caps (5 per run, 50 per session) and live in memory: a restart cancels them. They wait yukta.control.approval.ttl (default 10m, never above yukta.input.max-ttl).
  • A stop sent by a person is never parked. It runs with a warning, so a person can always stop a run. A stop proposed by a supervisor can be parked.
  • Votes. In a QUORUM or UNANIMOUS input tier supervisors vote once each; a split vote hands the request up, and a person’s answer always decides at once.
  • A supervisor that flaps. A supervisor that turns a target around (pause then resume) more than yukta.supervisor.turn-around.limit (4) times in period (60 s) is blocked for cooldown (5 m), shown as flapping-blocked.