CLI Reference
Use this page when you need a quick map of the OAT CLI rather than the full command-by-command docs. It is intentionally shallow: each section points to the owning page that documents the detailed behavior.
The CLI is also a standalone value path. You can use oat init, oat sync, oat tools, docs commands, repo archive sync, and repo-analysis commands without adopting the full project workflow.
Contents
- CLI Bootstrap - Bootstrap a repo with
oat init, guided setup, and initial provider adoption. - Tool Packs - Install, update, inspect, migrate, and remove bundled OAT skills and agents at project or user scope.
- Config and Local State - Config, backlog, local paths, diagnostics, and related utility commands.
- Remote Project Management - Explicit remote bindings, policy, host handoffs, and recovery.
- Workflow Gates - Per-skill final commands, review gates, and cross-runtime prompt dispatch.
- Docs Tooling Commands - Markdown setup/adoption, framework scaffolding, manifests, and nav sync.
- Provider Sync - Sync behavior, provider capabilities, config, and drift management.
- Agentic Workflows - Tracked project execution, skills, ideas, and workflow routing.
- Workflow & Projects - Project lifecycle, artifacts, reviews, PR flow, and state-machine docs.
- Repository PR Comment Analysis - Detailed
oat repo pr-comments ...behavior.
Full CLI Reference Expansion Path
Keep this page as the command-family map. Fuller command coverage should live either in the owning section for a command family or in generated/semi-generated reference pages that link back here.
Each full command reference should include:
- exact arguments, flags, defaults, aliases, and mutually exclusive options
- output examples, including
--jsonshapes where the CLI supports JSON - exit behavior for success, validation errors, missing state, and non-interactive blockers
- side effects such as file writes, generated artifacts, config mutations, branch/commit behavior, and network calls
- non-interactive usage guidance, including required flags and scripting-safe forms
- source or test references for behavior that is easy to drift, marking unknown exit-code behavior as unknown instead of inventing a contract
The first practical expansion path is to keep improving the existing owners: Docs App Commands, Provider Interop Commands, Config and Local State, Tool Packs, Workflow & Projects, and Repository PR Comment Analysis.
Exact-path maintenance commits
oat internal commit-paths --identity <operation> --message <message> -- <files...>
commits an explicit list of owned files with enabled Git hooks. Include tracked
removals and both names of a rename. Directories, untracked missing paths,
unmerged entries and paths outside the repository are rejected; the command
does not discover ownership or expand directory/glob pathspecs. Unrelated staged
entries and worktree bytes remain preserved, including partially staged files.
On success, owned index/worktree bytes match the hook-final commit.
Choose a unique identity before the first attempt and retain the identity,
message and literal path list for retries. With global --json, outcomes are
committed, already-matching, nothing, blocked or failed.
Success exits 0, blocked exits 1 and failed exits 2. A blocked result can
name a verified commit whose owned-index publication still needs repair; do not
claim settlement until a matching retry succeeds. Ignored receipts under the
Git directory verify the exact identity, commit, tree and paths before reuse.
Lock inspection/retry is bounded and never deletes another writer's lock.
If the command is unavailable, stop and update the CLI; there is no broad Git
commit fallback. Synced project artifacts still use oat project push after
successful scope resolution. During development, run the current source entry:
pnpm --silent run cli -- internal commit-paths --identity "$COMMIT_IDENTITY" --message "$COMMIT_MESSAGE" -- "${OWNED_FILES[@]}"A dist invocation requires a fresh build after the latest CLI source changes.
For oat backlog archive --json, use the complete returned affectedPaths
array as the archive-owned list, converting absolute paths to literal
repository-relative files against the same repository root. Union per-item
results, append an owned
handoff's filesystem deletion, and format only existing supported text with
the repository command before the helper commit. Include both old/new item
paths and every ledger/index/reference output; never stage only the old
missing name. A settled noop may return no paths and needs no commit. Pending
Git retries include prior outputs only with HEAD ownership evidence, while
no-Git retries report current-pass mutations. See Backlog
Lifecycle.
Command Groups
| Command group | What it covers | Go deeper |
|---|---|---|
oat init | Bootstrap canonical OAT directories, sync config, optional hooks, and guided setup. | CLI Bootstrap |
oat tools ... | Install, inspect, update, remove, and migrate bundled OAT tool packs and assets across project and user scope. | Tool Packs |
oat pjm ... | Adopt PJM for a repository and record it (init), run read-only adoption and reference diagnostics (doctor), and migrate legacy layouts to pjm/ + reference/ (migrate). Adoption is separate from pack placement. | Install vs. initialize |
oat pjm remote ... | Intake, publish, refresh, reconcile, close out, inspect discussion, resolve anomalies, diagnose, migrate, and continue durable host actions for explicit remote bindings. | Remote Project Management |
oat decision ... | Create, index, and migrate file-per-record repo decisions under reference/decisions/ (init, new, regenerate-index, migrate). | Config and Local State |
oat backlog ... / oat local ... | File-backed backlog helpers, local path sync, and local-only operational support. | Config and Local State |
oat config ... / oat instructions ... | Config discovery, source-aware config dumps, supported mutations, and instruction-integrity helpers. | Config and Local State |
oat gate ... | Per-skill final gate config, review-specific gate execution, exec-target registry writes, and cross-runtime prompt dispatch. | Workflow Gates |
oat state ... / oat index ... / internal | Repo dashboard refresh, repo indexing, validation helpers, and diagnostics. | Config and Local State |
oat docs ... | Markdown bootstrap/adoption, framework setup, external manifests, nav sync, and docs workflow entrypoints. | Docs Tooling Commands |
oat status / oat sync / oat providers ... | Provider sync, drift inspection, provider configuration, and adoption behavior. | Provider Sync |
oat project ... / oat cleanup ... | Project scaffolding, active-project status inspection, tracked-project listing, plan validation, archive creation, and project/artifact cleanup commands. | Workflow & Projects |
oat template resolve | Resolve a lifecycle template in repository, user, then bundle order and optionally copy it into place. | File Locations |
oat review ... | Review artifact discovery helpers, including latest-review resolution for project and ad-hoc review flows. | Reviews |
oat repo ... | Repository-level workflows such as archive sync and PR-comment analysis. | Repository Analysis |
Notable commands introduced in the current CLI surface:
oat project new <slug> [--mode spec-driven|quick|import|lite|brief|goal] --scope shared|local|synced [--force]- create a project in an explicit scope.--modeselects the workflow mode and therefore which artifacts are scaffolded; it defaults tospec-driven. Usequickfor a bounded feature with clear requirements,importto normalize an externally authored plan,litefor a single-sitting change that scaffolds onlyplan.md,state.md, andimplementation.md, andbrieffor one delivery planned as a Project Brief, which scaffolds theplan-briefandimplementation-brieftemplates plusreferences/research/, andgoalfor an outcome delivered in milestones, which scaffolds the same files withoat_plan_source: goaland the canonical| Milestone | Phases |table header. Start brief and goal projects withoat-project-brieforoat-project-goal, which run this command themselves. See Lifecycle for the per-lane flow. Without--scope, creation usesprojects.defaultScope, which defaults tosynced. Project slugs are unique across all scopes by default, including recorded or remotely published synced projects.--forcedeliberately permits a duplicate; use an explicit project path withproject openorproject pausewhenever a slug is ambiguous across scopes.oat project scope [project] --format value|json- resolve a project's storage scope without inspecting its path manually.oat project push [project] [--message <message>]- commit pending synced artifacts, reconcile with the exact project ref onorigin, publish it, and refresh an open PR's artifact-links block. Use--no-refresh-prto skip the PR edit. With--json, success returnsstatus(pushedorup-to-date), the full project-refsha, the retainedref, and optionalprRefresh; conflict and rejected outcomes return their correspondingstatusand exit nonzero.oat project pull [project] [--no-commit]- fetch and materialize or update a synced checkout. Resolve a rebase conflict and runpull --continue, or usepull --abortto return to the pre-pull state. Coordination children are pulled by default;--no-childrenlimits the operation to the selected project. When pull adopts a remote project with no local discovery record,--no-commitleaves the new record uncommitted for the caller to persist. With--json, the receipt includesstatus,sha, andref, plusconflictswhen applicable and per-child results when coordination children were requested. A project whose authoritative ref isrefs/oat/completed/<project>is terminal and cannot be pulled or opened. A same-SHArefs/oat/projects/<project>ref is only an inert alias; differing active and completed SHAs are reported as a terminal mismatch that must be repaired before lifecycle work continues.oat project links [project] --format markdown|json- render SHA-pinned links for the linkable artifacts on the project ref. On GitHub origins the artifacts become full-SHA blob links; other hosts degrade to the retained ref and short SHA without guessing a web URL.--durable-summary <path>accepts only a path contained in the repository, normalizes it repository-relative, and renders it as a code span rather than a guessed remote link. Completed projects resolve throughrefs/oat/completed/<project>while the rendered artifact URLs remain pinned to the verified full source SHA.oat project pause [project]- persist the pause state and clear the active project pointer. If synced publication is declined or fails after the pause commit is created, that clean commit is retained; address the publication failure and rerunpauseto publish it.oat project prune [project] [--force] [--no-commit]- remove every checkout, the local and remote project refs, and the tracked record. When the project is omitted, the active project is used. This is the explicit destructive operation;--forcemay discard dirty or unpushed artifacts, while--no-commitleaves the parent-record deletion for a library caller to commit. If remote deletion succeeds but local checkout removal fails, the local ref, record, and checkouts are retained. Resolve the reported local obstruction and retryprune --force; do not runproject push, which would republish the deleted remote ref. For a completed project, explicit prune deletes the completed ref and any same-SHA active alias but preserves durable local and S3 archive snapshots. A differing-SHA terminal mismatch blocks prune and retains both refs.oat project migrate <path> --to synced [--no-commit]- migrate an existing shared project to synced storage while preserving its artifacts and retargeting the active project pointer.--no-commitleaves the parent record and source-tree transition uncommitted for a library caller.oat project list --scope shared|local|synced- filter tracked projects by scope. Add--remoteto discover project refs that do not yet have a local record or checkout, and--include-coordinationto include coordination parents. A malformed discovery record with no materialized checkout appears as arecorded-invalidrow. When the checkout is materialized, its existing row instead carries the restore hint andrecordErrorparse diagnostic in--jsonoutput. Fully retired completed refs and same-SHA active aliases are omitted from active discovery. A differing-SHA active/completed pair appears as aterminal-invalidrow with a recovery diagnosis. When reconciling a local synced record or checkout, only a verified missingrefs/oat/completed/<project>result counts as absence. Transport, authentication, and other lookup failures fail the command closed; they are never converted into arecorded-absentrow or pull guidance.oat tools guidance- print the managedOAT toolsAGENTS.mdblock for the currently installed packs without installing, upgrading, or writing anything;--jsonreturnsstatus,sectionKey,target,packs,otherProjectSkills, andmanagedBlock. See Tool Packs.oat tools migrate --pack <pack> --from <scope> --to <scope>- move one installed pack between project and user scope. Always previews first, installs and re-inventories the destination before touching the source, and offers source removal only after the destination is verified complete. Declining or running non-interactively leaves the pack installed at both scopes rather than failing.--dry-runstops after the preview; there is no force flag. See Tool Packs.oat pjm doctor --json- read-only repository PJM diagnostics whose result carries an additiveadoptionobject (stateofdeclared|inferred-legacy|partial-initialization|none,repoRoot, andrecovery). This, notoat tools has project-management, is the check that answers whether this repository adopted PJM.oat pjm remote ... --json- operate one explicit provider-neutral binding. The family includesintake,publish,refresh,reconcile,closeout,discussion,resolve,doctor,migrate,storage, andoperation continue. Anokenvelope exits 0, a systemfailedenvelope exits 2, and every safely persisted nonterminal or blocked status exits 1. Apendingenvelope withexternalActionis a durable host handoff, not remote success.oat config dump --json- merged config with source attributionoat project status --json- full parsed state for the active tracked project. Stable contract for skills: the JSON output is a typed read interface for OAT skills; the fields that OAT skills read from it are pinned by a contract test in the OAT source repository. Removing or renaming any ofproject.{name, path, phase, phaseStatus, workflowMode, docsUpdated, lastCommit, prStatus, prUrl}is a breaking change and fails that test.oat project status --field <path>- print one arbitrary dot-path field from the same status payload, e.g.project.workflowModeorproject.timestamps.stateUpdated. Missing/null fields printnull; object and array fields print compact JSON.oat project status --project-path <path>- read from a repo-relative or absolute project path instead of.oat/config.local.json's active project pointer. Combine it with--fieldor--shellwhen a skill has already resolved the target project path.oat project status --shell NAME=path ...- print shell-safe assignments for one or more fields from one status read, e.g.WORKFLOW_MODE='quick'. This is the preferred multi-field read API for skills. See Writing Skills → Reading project state for examples and thenpx-backedoatshim contract.oat project promote <project-path> --to quick [--json]- promote an authored lite, brief, or settled goal project to quick mode. For a lite project, the command derivesdiscovery.mdfrom the lite plan's Summary, Decisions, Assumptions, Out of Scope, and Validation Criteria; carries Product Behavior into Success Criteria and Technical Design into a carried-forward discovery section when those adaptive sections are present; preserves the original asreferences/lite-plan.md; and scaffolds a fresh quickplan.md. For a brief project, it migrates each phase by state: accepted phases keep their completed execution records as quick tasks or get one synthesizedAccepted via phase evidencetask against the phase's**Head:**, active phases keep their completed records, and pending or provisional phases stay taskless and are listed inoat_promotion_pending_phases(quick plan readiness stays false until that list is empty); it renders the brief's sections intodiscovery.md, archives the original asreferences/brief-archive.md, and setsoat_promoted_fromto the source mode (brief, orgoalfor a goal project). There is no reverse direction. A goal project promotes like a brief, but only at a settled delivery boundary: the active milestone isdelivered, everyoat_deliveriesentry isat_endpointorsuperseded, and nooat_goal_recoveryis pending; otherwise it refuses withgoal-milestone-active,goal-recovery-pending, orgoal-delivery-unsettled(with adeliveriesarray naming the unsettled entries; an empty registry is also unsettled), and its archive appends the delivery history after the original brief. It refuses unsupported targets, projects that are neither lite, brief, nor goal, unreadable projects, an existing lite-plan reference, invalid authored sections, an existing brief archive (brief-archive-exists), an extracted journal phase (extracted-phases), a brief with no parseable phases (invalid-brief-plan), an accepted brief phase with no completed record and no**Head:**anchor (missing-phase-head), unresolved scope, unavailable templates, failed writes, or failed persistence. JSON output is{ status: "promoted" | "refused", reason, files }, plus an optionalphasesarray naming the refused phases whenreasonismissing-phase-head;reasonis the stable categorical contract and refused results exit non-zero.oat template resolve <name> [--output <path>] [--json]- resolve a template in repository (<repo>/.oat/templates/), user (~/.oat/templates/), then bundled order and reportname,found,tier(repository|user|bundle), andpath.pathis set only for the repository and user tiers; it isnullfor the bundled tier, so callers never depend on a package-manager path.<name>acceptsplanorplan.mdand rejects path separators and...--output <path>copies the resolved content to that file, replacing an existing file, and does not create missing parent directories; JSON output adds the absoluteoutputpath. A template missing from every tier exits 1 withfound: false. Lifecycle skills use--outputto copy their artifact templates, so a user-scope-only install works without a repository.oat/templates/directory.oat review latest --json- find the newest review artifact byoat_generated_at, scanning the active or specified project'sreviews/andreviews/archived/directories plus ad-hoc review locations. Same-time candidates use target priority, then lifecycle recency (final> higher phase/task > lower phase/task). The JSON contract returnspath,scope,generatedAt,kind(projectoradhoc),archived, andactionable, withnullvalues when no review exists. Archived project reviews remain discoverable as history but returnactionable: false.oat project list --json- summary state for tracked projects under the configured projects rootoat project closeout-check <project-path> [--autonomous] [--json]- read-only check of the closeout invariant: a closeout that must snapshot its post-implementation sequence cannot reach terminal completion until itsoat_post_implement_sequencesnapshot exists, every stored step is recorded complete, approval is recorded, and the snapshot reachedstatus: complete. A snapshot is required when the effective layeredworkflow.postImplementSequenceis configured, when the closeout is autonomous (--autonomous, orOAT_AUTONOMOUS=1in the environment even without the flag), or whenoat_workflow_modeislite. Once a snapshot exists its recorded state is authoritative and current configuration is not consulted. JSON output reportsstatus(complete,not_required, orincomplete), the missinginvariant(snapshot_missing,snapshot_malformed,sequence_failed,pre_approval_step_pending,approval_pending,post_approval_step_pending, orsequence_not_complete),route(oat-project-implementwhen incomplete),nextOwner(the next stored step and its skill, or the approval), and the evaluatedinputs. An incomplete or malformed result exits 1. For a goal project (oat_workflow_mode: goal) the goal boundary is checked first and read-only: it requiresoat_goal_status: declared_met, a valid exhaustiveoat_goal_outcome_audit, resolved extractions, a successfulgh pr viewobservation of every terminal delivery member, and delivery coverage of every accepted phase (each in a terminal delivery's effective scope with a passed, fresh integrated review and its persistedexit_gate); refusals reportgoal_inputs_unreadable,goal_not_declared,goal_audit_invalid,goal_extraction_unresolved,goal_observation_failed, orgoal_coverage_incomplete, andnextOwneris{ kind: "goal", skill, reasons }, where each reason is{ code, message, subject? }andskillis the owner of the first reason: for examplenot-declaredandaudit-invalidroute tooat-project-goal,review-not-freshtooat-project-review-provide,exit-carrier-not-freshtooat-project-implement, delivery and endpoint reasons such asendpoint-unmettooat-project-pr-progress, andphase-uncoveredtooat-project-pr-final. A goal snapshot may record itsprstep as satisfied by delivery coverage (satisfied_by: { pr: delivery-coverage }) only oncepris in the completed prefix.oat project delivery refresh --project <path> [--json]- goal only. Observes every registered delivery member withgh pr view <url> --json state,headRefOid,baseRefOidand persists, instate.md, only the observations (observed_head,observed_base, memberstatus), the deliverystatus(openorat_endpoint), the first observedendpoint_attainment, and derived staleness (integrated_review.disposition: stale, the milestone exit carrier'sfreshness: stalewithfreshness_reason). It never rewrites a reviewed revision, a member base, or the first endpoint attainment, never writesoat_milestone_statusoroat_goal_recovery, and never manufactures a passed review or a fresh carrier.oat-project-pr-progressandoat-project-goalrun it before registration and re-alignment.oat project delivery check --project <path> --subject <mNN|dNN> [--json]- goal only, read-only. Reports whether a milestone (before registration, against the branch head) or a registered delivery (against its observed member revisions) is deliverable: a passed integrated review that is still fresh under the bookkeeping-successor rule and an allowed, fresh milestone exit carrier (reported asexit.source: milestone-carrier; the closeout boundary judges each delivery's persistedexit_gateinstead). Evidence reviewed at R covers a later head only when every intervening commit changes permitted bookkeeping in this project's tracking files (state.md,plan.md,implementation.md,project-log.md,summary.md) and review artifacts (including those of deliveries sharing the same member pull request); any change tosummary.mdis bookkeeping because it is derived memory, never the authority for the brief's content; code changes, edits to approved brief content, and a substantive change followed by its revert fail. JSON output carriesstatus(okorundeliverable), the subject, its orderedscope,publishedHeads,review,exit, anddeliverable; an undeliverable subject exits 1 with the full report. A stalerevieworexitresult carries areasonthat starts with a code, such asreview-missing,review-scope-narrow,member-unobserved,base-changed,bound-revision-mismatch(a bound revision differs from its member's set-oncereviewed_head),subject-changed,configuration-changed, or a bookkeeping-successor failure:missing-revision,missing-object,not-ancestor,inspection-failed,disallowed-path, orsubstantive-change.oat project complete-state <project-path> [--archived] [--autonomous]- apply the canonical completed-state mutation to a project'sstate.md; used byoat-project-completeduring lifecycle closeout. It first evaluates the same closeout invariant asoat project closeout-checkand refuses, with the same message and without writing, a configured, autonomous, or lite closeout whose snapshot is absent, malformed, or incomplete. A project whose implementation finished before closeout snapshots existed recovers by runningoat-project-implement, which persists the snapshot (completion-and-closeout.mdStep 15) and completes its stored steps; there is no override flag. An unconfigured, interactive, non-lite closeout without a snapshot completes as before. A goal project is also refused, without writing, by the goal boundary thatcloseout-checkdescribes. When the project has animplementation.md,complete-statethen runs the final journal evaluator (oat project journal check --at completion) on the same bytes immediately before its write, and refuses with the evaluator's reasons, writing nothing, when the journal does not hold its terminal shape; an override line covers only its eligible gap and never the closeout invariant. A project with aplan.mdbut noimplementation.mdis refused withjournal-missing; only a plan-less project completes on the closeout invariant alone.oat project archive [project-path] [--project-recap-run <project-relative-path>] [--no-commit] [--new-generation <reason>]- archive a tracked project through the same local move, summary export, and configured S3 upload path used by completion. When omitted, the project path falls back to the active project. The optional recap path must identify aproject-recaprun inside the project'sexplainers/directory. Archive verifies the complete selected package and every immutable file byte, then atomically exports one self-contained.oat/repo/reference/project-recaps/<YYYYMMDD-project-slug>.htmlpage before deleting the active project. The full original package remains in the archived project. JSONprojectRecapExportidentifiesrunId,page.sourceRelativePath,page.originalSha256,page.exportedSha256,verifiedArtifactCount, and the flatexportRoot. No tracked sidecar is created. Matching dated pages are adopted on retry; conflicting destinations, incomplete inventory coverage, stale bytes, path escapes, and recipe mismatches fail without removing the active project. Summary and PR links point to the tracked page. For synced projects, a configured S3 upload must succeed before terminal cleanup. Successful closeout makesrefs/oat/completed/<project>authoritative, deletes the tracked JSON record and nested checkout, and leaves archive metadata sufficient for an idempotent recordless retry. With--json, the terminal receipt reportscompletedRef,verifiedSourceSha, whetheractiveAliasDispositionwasretainedorremoved, whetherrecordRetiredcompleted, and the parentlifecycleCommit.--no-commitis a manual/library-only option: for a synced project it still removes the checkout but deliberately leaveslifecycleCommitnull. Never pass it throughoat-project-complete, which requires that receipt. Retry identity: one read-only evaluator, shared by apply and--dry-run, resolves the archive before any write. An identical retry (the project's retained-artifact manifest matches exactly one same-scope archive) reuses that snapshot's local path, dated export identity (summary export file and S3 key) and receipt on any later date, and a source-absent retry resumes the single same-scope archive; changed content, several matching archives, or an unverifiable non-empty directory at the archive path refuse before export, copy, overwrite or S3 upload, with follow-up-project guidance. The dry-run reports the identity the apply would use..oat-archive-source.jsonrecordsmanifestHashandgenerationbeside the project name, scope, snapshot and synced source SHA. The exported summary rewritesoat-project:<project>#<entry-id>locators tooat-archive:<snapshot>#<entry-id>and turns relativeproject-log.mdoroat-execution-learnings.mdlinks into plain text; see Archive identity.--new-generation <reason>archives reopened work of a shared or local project as the next generation instead of refusing it: it requires a non-empty single-line reason and exactly one latest generation of the same project and scope as predecessor (otherwise it refuses), never overwrites that predecessor, and persists the new generation (<project>-gNlocally,<YYYYMMDD>-<project>-gNas export identity; when the same project's archive from another scope already holds that path or identity, both carry the scope:<project>-gN-<scope>and<YYYYMMDD>-<project>-gN-<scope>) with its predecessor, reason andgenerationRequestid before any export, copy or S3 side effect, so a retry reuses it. Replaying the same predecessor and content resolves to the same generation; synced projects refuse the option. JSON addsgenerationandpredecessorSnapshot, and--dry-runreports the proposed generation without writing it.oat repo archive sync [project-name]- hydrate archived project snapshots from the configured repo-scoped S3 archive into.oat/projects/archived/. A name resolves as an exact snapshot, then a full generation name such asdemo-g2, then the project (or<project>-<scope>family), restoring its latest generation and then its latest date; see Project Artifacts for the<project>[-gN][-<scope>]naming contract. The oldoat project archive syncpath remains as a deprecated shim.oat project dispatch record --event-file <json-file-or-dash>- validate one generic dispatch record plus its namespaced OAT evidence event and print the result withstatus: validated-only. The command writes nothing; launches are recorded in the calling workflow's run record. Managed, effort-pinned Claude launches run it before launch: the input carriesclaudeLaunchwith the real resolver result, generated definition, and proposed payload, plusrecordBaseandevent. The command validates their agreement and derives the configured invocation fields; callers cannot copy those fields intorecordBase. One validation run reports every violation acrossclaudeLaunch,recordBase, andeventin a single error message, onestage path: messageline each; therecordBasecross-field rules run even when a required field is missing, checks that need a value that failed to parse are skipped and named on a final line, secret-shaped values and absolute paths are scrubbed from the message, and the exit code stays 1. A validated input per role is published at.agents/skills/oat-dispatch-subagents/references/managed-claude-example.json. The command validates evidence only — it never launches a provider. Aruntime-observationevent may carry either a finishedobservationor the providermetadataenvelope (provider,observedAt,entries) to normalize, but not both.entriesmay be raw provider output: they are projected through the owning parser's allowlist, are never echoed in the output, and both forms have theirmatchderived from the record's configured invocation rather than trusted from the caller, with the observation'sproviderbound to the record's. With--json, the result addsruntimeIdentitywith separateconfiguredandobservedobjects plus amatchofmatching,mismatching,not-comparable, ornull; observation is corroboration only and never overrides configured invocation. Error output is redacted on a best-effort basis; absolute paths in identity fields are rejected outright, while some prose forms (colon-prefixed, URL routes) can survive. See Evidence Layers.oat project dispatch record --event-file <json-file-or-dash> --format line [--dispatch-stamp <line>]- print the one-line journal run record for the same validated, redacted record and event (projectionrun/v1), and write nothing; standard input still works. The line isrun/v1followed by named fields in a fixed order, joined by a space-padded·:request,scope,action,role, configuredtarget,modelandeffort,stamp(the formalDispatch:stamp passed verbatim through--dispatch-stamp; a stamp without the canonical prefix is refused),launch(planned,accepted, orblocked-before-start), the raw childoutcome,terminal(yesorno) and itsclass(completed,terminal-stop,blocked-before-start,live, orunrecognized),continuationreferences, and the optionalobservedidentity with itsmatch, which never replaces the configured fields. Inside a value,\\is a literal backslash and\·,\~,\n, and\rescape the delimiter, a tilde, a newline, and a carriage return; a bare~is null. Terminality comes from one producer-to-outcome mapping: a successful completion (completed,DONE,DONE_WITH_CONCERNS), a terminal stop (BLOCKED,failed,cancelled,INVALID_RUN_ABORT) andblocked-before-startare terminal;planned,running,pending, andNEEDS_CONTEXTwith its handle still open are not, and an outcome outside the mapping isunrecognized, never success. The owning skill writes one line per request id under## Orchestration Runsand updates it in place.--jsonkeepsstatus: validated-only, the record andruntimeIdentity, and addsrunLine(version,line,fields,classification,terminal).oat project dispatch canonical-role --role <name> --request-id <id> --skill-dir <dir> [--json]- resolve a canonical role through the loaded, user ($HOME/.agents), and project (<repo>/.agents) tiers and print thecanonical-role-resolutionevent thatoat project dispatch recordaccepts unchanged: redactedcanonicalPathandselectedPathin the form<loaded|user|project>/agents/<name>.md, the role'scontentDigest, andcandidateMisses.--skill-diris required because the loaded tier is derived from the loaded skill's directory; a missing or invalid directory exits 1 with a redacted error. An unknown role printsmissingevidence with its recovery commands. Read-only; the dependency is theworkflowspack.oat project validate-plan --project-path <path>- validatesoat_plan_parallel_groupsmetadata inplan.md; exits non-zero on invalid. Lite plans must contain exactly one phase, declare no parallel groups, and give every Validation Criteria bullet either a backticked command or amanual:instruction. Brief plans (oat_workflow_mode: brief) are validated with the brief profile: phases need non-empty**Intent:**and**Exit evidence:**, a**Provisional:**value ofyesorno, unique IDs, and a concrete first phase;### Taskheadings in a brief are reported as a warning, and taskless parallel groups are valid. Goal plans add the goal profile:oat_plan_source: goal, a valid## Milestonestable (goal-milestones-missing,goal-milestones-malformed,goal-duplicate-milestone,goal-milestone-empty,goal-milestone-unknown-phase,goal-phase-in-multiple-milestones,goal-phase-without-milestone), no parallel group spanning milestones (goal-group-crosses-milestone), canonical persisted phase IDs (goal-noncanonical-phase-id), and no change to a shipped milestone or a registered delivery's frozen scope (goal-shipped-milestone-changed,goal-delivered-scope-changed); phases the journal marksextractedneed no milestone. See Implementation Execution.oat project journal check --at <implement-preflight|review|completion> [--project <path>] [--scope pNN[,pNN]] [--json]- read-only journal evaluator: it readsplan.md,implementation.md, andstate.mdand writes nothing.implement-preflightis implement's call before its Step 16: it validates the accepted task and phase evidence while the terminal headers and pointers Step 16 owns may still be unwritten.reviewrequires an explicit--scopeof phases the plan declares and ignores future phases; a live run line warns there instead of refusing.completion(oat-project-complete, before roll-up, seal, or any lifecycle write) also requires the terminal journal shape: journaloat_status: completeand a clearedoat_current_task_id,state.mdoat_phase_statuscompleteorpr_openand a clearedoat_current_task. Task modes read journal records through the shared heading-only reader (fenced and indented code excluded, a block ends at the next heading of equal or higher level): every plan task heading is unique and sits under its own phase (plan-task-duplicate,plan-task-misplaced, routed to the plan owner and never override-eligible), and every plan task has exactly one journal block in its phase with exactly one owned**Status:** completedand one owned**Commit:**SHA; tables and checklists are never completion evidence, and overview totals must match. Brief and goal read phase units: an accepted phase carries its**Head:**, execution records close ascompletedwith a commit or asfailed,cancelled, orno-changewithout one, an extracted goal phase and a phase the goal outcome audit defers or retires stay valid and incomplete. Everywhere:## Referencesis last, review receipts sit in their phase or## Reviews and Gates, deferrals in## Deferred Findings, dispatch payloads are refused unless markedquoted-evidence, every run line parses asrun/v1under## Orchestration Runs, carries a validrequestidentifier, and is terminal at implement-preflight and completion, and no template placeholder survives, including one left in an inline-code slot such as- `{path}` - …unless the line is markedquoted-evidence. An override is one line under## Deviations from Plan / Design:**Override:** <scope-disposition-approved|evidence-lost|operator-accepted-gap> · **Authorized by:** <decision id> · **Scope:** <pNN-tNN|pNN> · <reason>; it covers only a missing task block, an incomplete task, missing commit evidence, or (forscope-disposition-approved) an unaccepted phase within its scope, and a malformed or misplaced override is refused. Output is{ status: ok | refused, reasons: [{ code, path, line, owner, message }], warnings, overrides }; a refusal exits 1. Delivery coverage and the post-implementation sequence stay withcloseout-check.oat project log append|check|synthesize|rollup- manage the optional append-only project observation log: append validated judgment or structural entries, inspect grammar and synthesis status, complete end-of-run synthesis, and roll observations intosummary.mdplus the configured repository ledger. See Project Log.oat project log append --category <category> --source <id> [--origin <producer>]- record a learning on a judgment entry. The category is one ofgotcha,efficiency,documentation-gap,candidate-skill-content,decision, orenvironment-limited; any other value,--categorywithout--source, a--sourcecontaining whitespace, and these flags on a--structuralentry are refused. The flags write a**Category:**,**Source:**,**Origin:**,**Recorded:**body head above the body; the heading grammar is unchanged and an entry without them is stored as before. Passing the source id as--idempotency-keymakes a retry reportalready-appendedand write nothing.oat project log check- JSON addslearnings:legacywhen the project still hasoat-execution-learnings.md, which summary and retro read after the log, andnoneotherwise, in every status.oat project log rollup [--json]- writes a bounded Workflow Observations digest, not a copy: judgment entries only (structural entries are counted, never rendered), newest first withdecisionandgotchaahead, capped byworkflow.projectLogDigest.maxEntriesand.maxLineswith an omitted-count trailer, one concise item per entry carrying the non-link locatoroat-project:<slug>#<entry-id>, and live project paths labelled.summarySectioniswritten,updated, oromitted(no judgment entry: an existing section is removed and none is created).entriesRolledUpcounts every entry read. JSON addsdigestwith the bounds,rendered,omitted(overBound,structural,malformed), the over-boundomittedEntriestokens andbyProducercounts. Ledger graduation still reads the complete judgment set.oat project set-mode— deprecated no-op. Execution mode is no longer user-selectable; emits a deprecation warning and preserves the--jsoncontract.oat gate review <prompt...>- run a stateful, headless OAT review through the target registry, parse the produced review artifact, and exit nonzero for configured blocking findings.--timeout-ms <milliseconds>overrides target,workflow.gateTimeouts, environment, and scope defaults; accepted values are 1,000–14,400,000. Final/phase/range code reviews and artifact reviews default to 30 minutes, while task code reviews default to 15 minutes. With--json, the result envelope on exit is the canonical completion signal:statusisok|blocked|review_failed|artifact_missing|artifact_validation_failed|targeting_correlation_failed, alongsiderunId,generatedAt, andartifactPathwhen available.artifact_missingmeans an accepted child exited cleanly without an artifact; fix synchronous review/artifact completion and start a new run, without review-receive or same-run remediation. Fortargeting_correlation_failed, correct the project declaration or artifact routing and start a new run; do not run review-receive even if an artifact path is present. Forartifact_validation_failed, correct the artifact and rerun the gate until it revalidates asokorblocked. Invokeoat-project-review-receiveonly when all three conditions hold:statusisokorblocked,receiveEligibleistrue, andhandoffis non-null.review_failedmay include structuredrefusal,noOutputProduced, and metadata-onlyactivityEvidence; these diagnostic fields never make a run receive-eligible. After a timeout, a validated run-correlated artifact returns the ordinaryokorblockedenvelope with additivelateCompletion: true. When post-selection work fails after the reviewer already committed a run-correlated artifact, the gate re-validates that selected snapshot through the same eligibility path rather than re-parsing whatever the path now holds, never re-dispatching, and returns the ordinaryokorblockedenvelope with additivepostSelectionRecovery: true; the artifact must still be present and unchanged for re-validation to succeed. When nothing recovers,review_failednames the failing sub-step inpostSelection.stepand its cause inpostSelection.code. Duplicate run-ID matches or a changed artifact with a mismatched run ID retaintargeting_correlation_failedandreceiveEligible: false. Orchestrators should read the structured result rather than poll the filesystem. See Workflow Gates.oat gate route --expect-runtime <runtime> --expect-model <model> --can-await <true|false> --json- return the headless child route (inline,delegate-sync, orrefuse) from provider-marker and model evidence. Ambiguous or contradictory evidence never routes inline.oat gate exec <prompt...>/oat gate cross-provider-exec <prompt...>- run a generic target prompt.--timeout-msuses the same validated CLI override; untyped runs otherwise retain the legacy default path.oat gate target set <id> --invocation-model <model|provider-default> --invocation-reasoning-effort <effort|provider-default>- persist optional configured invocation metadata alongside an exec target without inferring it from the target command.oat gate target set <id> --timeout-ms <milliseconds>- persist a validated per-target gate budget.oat gate target list --json- inspect resolved gate targets without selecting or executing a reviewer. Each entry reports its config origin, whether it is explicitly configured and enabled, current availability, and normalized configured invocation values (unknownwhen omitted).oat gate cross-provider-exec <prompt...>- choose an available exec target while avoiding the current runtime by default, then run the prompt with the chosen target's configured base command and exit with the child status.oat instructions sync [--dry-run] [--strategy none|pointer|symlink|copy]- apply the effectiveCLAUDE.mdstrategy (--strategy, elseinstructions.claude.shims, elsenone). Under the defaultnoneit removes theCLAUDE.mdshims OAT created — none at all while anyCLAUDE.mdhas content of its own — keeps and reports every otherCLAUDE.md, and warns about anyCLAUDE.md,.claude/CLAUDE.md, orCLAUDE.local.mdthat would make Claude Code ignoreAGENTS.md.oat instructions validatechecks the same strategy read-only. See Instruction Sync.
oat config surface flags
oat config set and oat config unset support the same mutually exclusive surface flags, which control the config file the write or removal lands in:
--shared— write to or remove from.oat/config.json(committed team repo settings)--local— write to or remove from.oat/config.local.json(per-developer repo state, gitignored)--user— write to or remove from~/.oat/config.json(user-level fallback, applies across all repos)
When no flag is passed, the CLI picks a sensible default per key type: structural keys (projects.root, documentation.*, etc.) go to shared, state keys (activeProject, etc.) go to local, workflow preferences (workflow.*) go to local. Pass at most one flag — the command rejects multiple surface flags.
Per-key restrictions apply identically to set and unset: structural keys can only be written at shared scope, most state keys can only be written at local scope (activeIdea is the exception — it accepts both local and user), and workflow preference keys accept any non-auto surface. Legacy autoReviewAtCheckpoints remains shared-only; prefer workflow.autoReviewAtHillCheckpoints.
oat config unset <key> removes the key from the selected surface and prunes any parent object it empties, so the resolved value falls back through the remaining surfaces to the built-in default. Removing a key the surface does not hold is not an error: it reports an already-unset outcome and exits 0. With --json the envelope adds a removed boolean, which is the machine-readable way to tell a real removal from an already-unset no-op, since both exit 0. A stored value that fails validation is still removed, matching oat config set, which repairs the same file.
Remote settings under pjm.remote.* remain shared-only. Removing a mutable leaf preserves sibling settings and the required empty policy object, if necessary, so the remaining remote configuration stays readable. Explicit defaults are removed from disk rather than silently written back by normalization; effective values still use fail-closed defaults. Remote policy must pass strict validation before removal.
unset refuses six classes of key, each with exit code 1:
- Unknown keys, including keys OAT reads but does not expose in the
oat configcatalog (documentation.index). These are not removable by anyoat configcommand. - Lifecycle state (
activeProject,lastPausedProject) — cleared withoat config set <key> ''instead. - Pack intent (
tools.*) — removed withoat tools remove --pack <pack> --scope project, which also removes the installed pack files. Clearing the intent alone would leave them behind. - Aggregate read views (
workflow.dispatchCeiling,workflow.dispatchCeiling.providers) — these are assembled forgetfrom the leaf keys and are not stored as such. Unsetworkflow.dispatchCeiling.presetorworkflow.dispatchCeiling.providers.<provider>instead. - Read-only remote-policy structure (
pjm.remote,pjm.remote.policy,pjm.remote.schemaVersion) — unset a documented mutable child key instead; removing the complete policy or its schema discriminator is not supported. - Environment-shadowed keys with nothing stored on the target surface — when
OAT_PROJECTS_ROOT,OAT_PROJECTS_DEFAULT_SCOPE, orOAT_WORKTREES_ROOTsupplies the effective value and the surface holds nothing to remove, clear the environment variable in your shell. When the surface does hold a stored value,unsetremoves it (assetwould rewrite it) and warns that the override still supplies the effective value.
workflow.* preference keys
The workflow.* namespace holds user-facing workflow preferences that let you answer repetitive confirmation prompts once and have OAT skills respect the answer automatically. Common keys:
workflow.hillCheckpointDefault(every|final) — default HiLL checkpoint behavior inoat-project-implementworkflow.archiveOnComplete(boolean) — skip the archive prompt inoat-project-completeworkflow.createPrOnComplete(boolean) — skip the "Open a PR?" prompt inoat-project-completeworkflow.autonomousComplete(boolean, defaultfalse) — standing opt-in for the non-interactiveoat-project-complete-autocompanion; whenfalseit stops with "interactive completion required"workflow.postImplementSequence(legacywait|summary|pr|docs-pr, or structured{preApproval, postApproval}arrays) — approval-aware post-implementation chainingworkflow.reviewExecutionModel(subagent|inline|fresh-session) — default final-review execution modelworkflow.autoReviewAtHillCheckpoints(boolean) — auto-run the extra lifecycle review at HiLL checkpointsworkflow.autoNarrowReReviewScope(boolean, defaulttrue) — automatically narrow re-reviews to the guarded range after the prior matching review's recorded head; setfalseto use the nominal full scopeworkflow.autoArtifactReview.plan(boolean, defaulttrue) — auto-run the boundedplan.mdartifact-review loop before implementation handoffworkflow.autoArtifactReview.analysis(boolean, defaulttrue) — auto-run the bounded accuracy-review loop for generated analysis artifacts before apply workflows consume themworkflow.gateTimeouts.code/workflow.gateTimeouts.artifact(integer milliseconds from 1,000–14,400,000) — review-type budget defaults below CLI and target overrides
These workflow keys resolve through config files and defaults (local > shared > user > default). Some config keys have explicit environment aliases, but workflow.autoArtifactReview.plan and workflow.autoArtifactReview.analysis do not. See Workflow preferences in the Configuration guide for full descriptions, surface guidance, and cross-repo foot-gun examples.