Skip to content

Set Up Scheduled Verification

Scheduled verification is where Orcho becomes specific to your repository. You declare the commands the project already trusts; Orcho decides which ones apply to a run, gives them a lifecycle identity, executes the eligible gates, and records immutable proof.

inspect the project
→ declare environments and commands
→ classify cost from evidence
→ group, select, and schedule
→ choose policy and failure action
→ inspect the effective contract
→ read receipts

Do not start by copying a Python, JavaScript, or PHP example. Start with the repository’s own instructions, manifests, package scripts, CI workflows, and developer documentation.

If those checks already run in CI, that is evidence for their command and importance, not a reason to skip the contract. Keep CI as the independent repository gate. Declare the same project-native proof in Orcho when you need it selected, scheduled, repaired, handed off, and recorded before delivery. The contract adds lifecycle ownership and receipts; it should not fork the project’s quality system.

Create the project plugin at:

your-project/.orcho/multiagent/plugin.py

Then declare a contract using commands that are real for that project:

PLUGIN = {
"name": "Checkout API",
"language": "Python 3.12",
"verification_envs": {
"project": {"python": "python"},
},
"work_mode": "pro",
"verification": {
"default_env": "project",
"commands": {
"lint": {
"run": ["python", "-m", "ruff", "check", "."],
"cost": "fast",
},
"unit": {
"run": ["python", "-m", "pytest", "-q", "tests/unit"],
"cost": "moderate",
},
},
"gate_sets": {
"hygiene": {
"commands": ["lint"],
"default_policy": "require",
},
"tests": {
"commands": ["unit"],
"default_policy": "require",
},
},
"selection": [
{"always": ["hygiene"]},
{
"paths": ["app/**", "tests/**"],
"include": ["tests"],
},
],
"schedule": [
{
"after_phase": "implement",
"gate_sets": ["hygiene"],
"action": "repair_loop",
},
{
"after_phase": "implement",
"gate_sets": ["tests"],
"action": "repair_loop",
},
],
},
}

The example is intentionally asymmetric. lint is proven, fast, required, repairable, and universal, so it is selected always. The unit suite is moderate, required, and repairable, but only selected when application or test paths change. Do not default everything to warn: use require for a proven load-bearing check, warn for a genuinely advisory diagnostic, and operator selection for checks whose environment or consequence is still uncertain.

Cost describes expected execution effort. It does not decide whether a gate is selected, who executes it, or whether failure blocks delivery.

CostUse it when
fastThe check is bounded, deterministic, local, and cheap enough for the normal implementation loop.
moderateThe check needs materially more setup or time than immediate feedback, but remains routine during ordinary development.
slowThe proof is broad, expensive, service-heavy, serial, or long-running enough to shape run latency.
unknownThere is not enough reliable evidence, or behavior is too variable to predict.

Classify from observation, not from the command’s name. A focused test can be fast while a repository-wide formatter can be slow. If several commands in a gate set share a cost, declare default_cost on the gate set; a command-level cost overrides it.

Quality gate strategy owns the practical p90 bands and full-boundary measurement method.

The useful unit of cost is the smallest independently selectable and schedulable command receipt. Do not describe a mixed bundle such as make qa as moderate when it hides a 5-second linter, a 90-second unit suite, and a 12-minute integration suite. Declare those commands separately, then place them in gate sets that share the same semantic selection and lifecycle:

"commands": {
"lint": {
"run": ["make", "lint"],
"cost": "fast",
},
"unit": {
"run": ["make", "test-unit"],
"cost": "moderate",
},
"integration": {
"run": ["make", "test-integration"],
"cost": "slow",
},
},
"gate_sets": {
"hygiene": {
"commands": ["lint"],
"default_policy": "require",
},
"application-tests": {
"commands": ["unit"],
"default_policy": "require",
},
"integration-proof": {
"commands": ["integration"],
"default_policy": "require",
},
},

This is what cost granularity buys: lint can run after every implementation, unit can be path-selected and repairable, and integration can run only for relevant changes at the delivery boundary. Each result gets its own immutable receipt and can be reused as fresh evidence without rerunning unrelated work.

Keep one native command when it is genuinely atomic. Split a wrapper only when its components are stable public commands with meaningful independent outcomes; do not reproduce private setup logic in plugin.py.

Keep these axes independent:

cost ≠ selection ≠ schedule ≠ executor ≠ policy ≠ action

Changing slow to moderate must not silently make a check required, move it to another hook, or transfer ownership to an implementation agent.

Cost does not execute policy, but it is not decorative. The contract author uses measured cost together with four other questions:

  1. Is this proof universal, path-specific, task-specific, or rare?
  2. Is failure normally repairable in source code?
  3. Must the proof exist before delivery, or is independent post-push CI enough?
  4. Does the command need services, credentials, destructive setup, or serial infrastructure?

Answer those questions first, then choose selection, schedule, policy, and action explicitly. Do not build a hidden rule such as “slow means warn” or “fast means always.” A slow security proof may be release-blocking; a fast diagnostic may still be merely advisory.

Observed costGood default designEscalate whenAvoid
fastSelect always only for universal checks; otherwise use paths. Schedule repairable checks at after_phase(implement). A proven load-bearing check can be require + repair_loop.Use before_delivery when the check is meaningful only at the final boundary or failure is not safely repairable.Repeating the same command at multiple hooks without a distinct proof requirement.
moderatePrefer paths or an explicit task_kind. Use after_phase(implement) when routine failure should enter repair.Use before_delivery + handoff for integration proof that must block but should not start a repair loop.Selecting every moderate suite always merely because CI already runs it.
slowNarrow with paths, task_kind, or explicit operator opt-in. Put final release proof at before_delivery; use manual_only when it is rare or operational.A slow after_phase(implement) + repair_loop is valid when the proof is load-bearing, reliably repairable, and its retry budget is worth the time.Automatic broad execution on every task, or using warn only to hide the cost.
unknownKeep it manual_only or behind operator selection while measuring exact argv, cwd, services, credentials, duration, and failure shape.Promote to fast, moderate, or slow only after observed evidence supports predictable execution.Auto-running it to “finish configuration,” or assigning a guessed cost from the tool name.

These are authoring defaults, not runtime transformations. Orcho will execute the contract you declare; it will not move a gate because its cost changes.

SelectionUse it for
alwaysA universal repository invariant whose proof is relevant to every change.
pathsSubsystem tests, generated-code checks, schema validation, or builds relevant only when matching files changed.
task_kindA check tied to an explicitly declared work class such as migration or release work. Task kind is not guessed automatically when the run has no declared value.
operatorRare, expensive, credentialed, destructive, or deliberately requested proof. With no opt-in, the set is not selected.

Selection reduces irrelevant work. It must express semantic relevance, not only runtime duration: do not use paths to suppress a slow check that is actually a universal release invariant.

Choose the hook from the desired transition

Section titled “Choose the hook from the desired transition”

The authorable hooks are before_phase, after_phase, before_delivery, on_resume, and manual_only.

HookBest useFailure design
before_phase(<phase>)A prerequisite that must hold before entering that phase.Prefer handoff or explicit abort when repair has not started yet.
after_phase(implement)Code checks that should give early, actionable feedback.repair_loop is a real implement → repair transition here when the profile has repair_changes.
before_deliveryFinal integration, packaging, compatibility, or release proof that must be current at the delivery boundary.A required failure normally hands off for an operator decision; use explicit abort only for a deliberate terminal policy.
on_resumeReassert assumptions that may have changed while a run was paused.Treat failure as a resume decision, not an ordinary code-repair loop.
manual_onlyCredentialed, destructive, rare, or still-unproven commands.manual or suggest; the engine does not auto-run it.

repair_loop is executable repair only at after_phase(implement). At other hooks it degrades visibly to handoff, because there is no valid implement → repair transition to drive.

pre-final is not a hook you write in plugin.py. It is the derived operator-facing stage used for eligible non-required gates when a profile has a final phase. Author the real hook and policy; inspect the effective when column with orcho quality-gates --profile <work-kind>.

Fast universal hygiene

"commands": {
"lint": {"run": ["make", "lint"], "cost": "fast"},
},
"gate_sets": {
"hygiene": {
"commands": ["lint"],
"default_policy": "require",
},
},
"selection": [{"always": ["hygiene"]}],
"schedule": [{
"after_phase": "implement",
"gate_sets": ["hygiene"],
"action": "repair_loop",
}],

Slow subsystem proof at delivery

"commands": {
"payments-integration": {
"run": ["make", "test-payments-integration"],
"cost": "slow",
},
},
"gate_sets": {
"payments": {
"commands": ["payments-integration"],
"default_policy": "require",
},
},
"selection": [{
"paths": ["src/payments/**", "tests/integration/payments/**"],
"include": ["payments"],
}],
"schedule": [{
"before_delivery": True,
"gate_sets": ["payments"],
"action": "handoff",
}],

Unknown external proof kept operator-owned

"commands": {
"staging-smoke": {
"run": ["./scripts/staging-smoke"],
"cost": "unknown",
},
},
"gate_sets": {
"external": {
"commands": ["staging-smoke"],
"default_policy": "suggest",
},
},
"selection": [{"operator": ["external"]}],
"schedule": [{
"manual_only": True,
"gate_sets": ["external"],
"policy": "suggest",
}],

require and warn scheduled identities are engine-owned. The engine invokes the native command, writes the authoritative receipt, and applies the declared consequence.

manual and suggest identities remain operator-owned. Use them when a check needs credentials, external services, destructive setup, or a deliberate human decision.

Implementation agents may still run focused tests, lint changed files, or use another bounded command while debugging. What they should not do is duplicate the broad scheduled proof as an implementation subtask. Official readiness comes from the engine receipt.

Resolve the project contract without executing its commands:

Terminal window
orcho quality-gates --project .

Review:

  • the selected gate sets;
  • the resolved schedule hook and phase;
  • executor and policy;
  • failure action;
  • command cost;
  • validation errors or unreachable gate sets.

Fix every contract validation error. Execute only bounded candidate commands needed to confirm their exact arguments and working directory. Do not launch a broad, destructive, or credential-dependent suite merely to finish setup.

Let project instructions teach future agents

Section titled “Let project instructions teach future agents”

orcho workspace init creates a language-neutral plugin template and adjacent agent guidance. When adopting the plugin in a repository:

  1. copy the template to .orcho/multiagent/plugin.py;
  2. merge the adjacent agent rules into the repository’s root AGENTS.md;
  3. keep a root CLAUDE.md shim pointing to AGENTS.md;
  4. preserve existing project instructions while adding verification ownership and task-authoring rules.

Those instructions matter whether work arrives through --task, a task file, an edited plan, or a follow-up. They teach agents to inspect the effective contract before putting broad commands into acceptance criteria.

For a faster first draft, run the read-only marker inspection:

Terminal window
orcho workspace fine-tune --dry-run

Then let a coding agent use the generated rules to inspect the actual repository and prepare the plugin. Treat both outputs as proposals. An engineer must approve the exact command, environment, selection, schedule, policy, and failure action before the gate becomes authoritative. Finish by reviewing orcho quality-gates; do not accept a contract merely because it validates syntactically.

The lifecycle is:

declaration
→ selection
→ scheduled identity
→ execution
→ immutable receipt
→ readiness consequence

A missing receipt is not a failed test. A stale receipt is not a current pass. The scheduled-gate ledger preserves why each identity ran, did not run, or needs attention. Final acceptance consumes that evidence rather than trusting the worker’s summary.

The canonical engineering docs live with the code: