An OpenCode plugin for running resumable, multi-agent workflows with durable run state.
@mcrescenzo/opencode-workflows adds a workflow_* tool family to OpenCode that
lets you (or an agent) fan work out across multiple agent lanes, preview and
approve an exact plan by default, inspect persisted status, resume after an
interruption, and apply edits to your repo only through a reviewed, hash-gated
boundary.
It is the engine and harness, with one flagship workflow included; you bring the rest.
OpenCode chats are great for one-off tasks, but some jobs are bigger or riskier than a single unstructured prompt: a whole-repo review, a multi-step research fan-out, a staged edit plan, a long-running background sweep. This plugin gives those jobs a structure:
- Plan, then approve by default. The default/manual path previews authority,
models, budgets, lanes, and a source hash before a matching hashed approval.
A plugin owner may instead configure
options.autoApprovefor eligible first-call launches within a fixed authority ceiling. - Fan out safely. Work runs in isolated lanes (including git worktrees for edits), inside a deterministic QuickJS sandbox, with deny-by-default permissions per lane.
- Recover after interruptions. Run state and completed lane results persist; pause, cancel, reconcile, and resume re-run only what's needed. Live execution still stops with the owning OpenCode process.
- Extend a stopped wave deliberately.
maxAgentsis cumulative approved logical-call credit, not an attempt counter. An agent-capacity stop persists a single-use request that an approval-boundgrantAgentsresume can fund additively while completed lanes replay. - Apply, don't assume. Normal edit workflows reach your real tree only
through
workflow_apply, after source/base/diff/domain hashes and a clean git base are checked. The explicit exception is a trusted autonomous-local drain: its launch approval can bind verified in-run apply and domain finalization.
- A
workflow_*tool family:workflow_run,workflow_status,workflow_events,workflow_apply,workflow_save,workflow_list,workflow_templates, plus lifecycle tools (workflow_cancel,workflow_pause,workflow_kill,workflow_reconcile,workflow_cleanup,workflow_salvage) and references (workflow_roles,workflow_models,workflow_template_save). - Workflow primitives in the sandbox body:
agent,parallel,pipeline, nestedworkflow, phases, budgets, structured-output schemas, and background runs with completion notifications. - Authority profiles that scope what a run can do — from read-only review up to hash-gated primary-tree apply and (via a trusted extension) autonomous local drains.
- Starter templates (
first-run-slice,scoped-parallel,edit-review) to copy and adapt. - One bundled flagship workflow —
deep-research— plus its/deep-researchcommand: deep multi-source web research with adversarial fact-checking. It doubles as the living gold-standard example of every convention below. - Three bundled skills:
opencode-workflow-authoring,workflow-model-tiering, andworkflow-plan-review. - A trusted-extension seam so operators can contribute their own workflows, commands, skills, tools, and drain adapters.
- It is an engine, not a pack of automations. It ships exactly one bundled
workflow (
deep-research, with its/deep-researchcommand) as the flagship exemplar; everything else you write yourself (or install via a trusted extension) and run withworkflow_run. - It is not a daemon. Background runs live inside the OpenCode process and
stop if that process exits; use
workflow_reconcileto recover stale runs. - It does not silently write to your working tree. Normal edit runs stop at an approval boundary; you apply explicitly.
- Trusted extensions are not sandboxed — they run as normal Node code in your process. Only install extensions you'd trust as local code.
Requires OpenCode, Node ≥ 20.11, and git on your PATH.
bun add @mcrescenzo/opencode-workflows
# or
npm install @mcrescenzo/opencode-workflowsRegister it in opencode.json (the singular plugin array key):
{
"plugin": ["@mcrescenzo/opencode-workflows"]
}Restart OpenCode after changing plugin config.
Deep multi-source research with adversarial fact-checking: Scope → parallel web
search per angle → URL-dedup + source budget → indexed, target-bound evidence
extraction through WebSearch →
per-claim adversarial vote panels (3 votes at thorough; verifier infrastructure
errors are reported as unverified, never as refutations) → cited synthesis.
workflow_run({ name: "deep-research", args: "is fish oil effective for ADHD?", background: true })
Or with options: args: { question, depth: "quick" | "normal" | "thorough", maxSources, seedUrls }. Seed inputs must be bounded, already-public and indexed
HTTP(S) URLs; local files, non-public address literals, and credential-bearing URLs are
rejected and surfaced in the result. Do not submit confidential research questions or
confidential, unlisted, capability-bearing, or signed URLs. The research question,
generated queries, and seed URL targets are sent through OpenCode WebSearch to its
configured external search provider (Exa or Parallel on the supported runtime). Depth
thorough (default) is full 3-vote verification.
The run asks for network authority at its one-time approval, but every child lane
explicitly denies direct WebFetch. Search, extraction, and verification use remote
WebSearch only; scope and synthesize lanes deny network access. Consequently, seed URLs
prioritize public indexed targets but are not an offline or direct-fetch fallback when
the external search provider is unavailable. No shell, no MCP, no edits. The
/deep-research command wraps the full flow: clarify → model tiers → approval →
report persisted to .deep-research/runs/.
The default/manual workflow path is two phases: preview, then approve.
Configured options.autoApprove is the explicit exception described below.
- Save or write a workflow, then preview it (runs nothing, returns an
approvalHash):workflow_run({ name: "my-workflow", args: {...}, background: true }) - Review the preview (authority, models, budgets, lanes, hashes), then run it:
workflow_run({ name: "my-workflow", args: {...}, background: true, approve: true, approvalHash: "<hash>" }) - The background call returns a run id. Yield instead of polling; the best-effort
completion prompt normally resumes the invoking session. A successful
promptAsyncsubmission proves acceptance only, not observation or acknowledgement. Then read the terminal result exactly once:workflow_status({ runId, detail: "result" })
If launch warns that completion prompts are unavailable, polling is the fallback.
Use background: false only when you intentionally want the approval call to block
and return the result inline.
detail: "result" preserves the legacy status.result.output path and also
returns resultEnvelope version 1. The envelope states whether the persisted
shape is full, partial, projected, or absent, with explicit terminal,
resumable, partial, output (or null), and budgetRequest fields. Follow
the returned nextActions; do not infer output for an absent/projected result.
Approving an inline-source preview doesn't require re-transmitting the
source: omit source, preserve the same envelope inputs (including args and
background: true), and send approve: true + approvalHash; the previewed
bytes are reused from a bounded in-memory store (approve-by-reference). A
mismatched approve returns changedFields
naming which envelope fields re-keyed — null when the supplied hash no
longer matches a recorded preview.
New to workflows? Save a copy of the smallest safe shape and run it read-only:
workflow_template_save({ template: "first-run-slice" })
workflow_run({ name: "first-run-slice", background: true })
The bundled workflow-plan-review skill owns the full launch → approval →
background → completion-notification → result-readback contract; opencode-workflow-authoring
covers source shape, fan-out, and edit/apply boundaries; workflow-model-tiering
covers fast/deep tier mapping.
OPENCODE_WORKFLOWS_DIR— where global run state, roles, and templates live. Defaults to$XDG_CONFIG_HOME/opencode/workflows.OPENCODE_WORKFLOWS_DEBUG_CAPTURE=1— write per-lane debug artifacts (prompt/schema/transcript) under each private run directory. Off by default.OPENCODE_WORKFLOWS_TUI_ASCII=1— replace the native monitor's Unicode status glyphs with distinct one-cell ASCII markers.TERM=dumband explicit ASCII locales select the same fallback automatically.- Plugin
autoApproveoption — set to"readOnly","worktree", or"all"to let eligibleworkflow_runcalls launch on the first call when within that tier. A per-callautoApprovearg can narrow, never widen, the ceiling. Normal edit workflows still keepworkflow_applyas a separate hash gate; a trusted autonomous-local drain may instead carry approval-bound in-run apply. OPENCODE_WORKFLOWS_HARD_CONCURRENCY_LIMIT— raises/lowers the per-run concurrency ceiling (default64; effective default remains4).
- The default/manual launch path needs a one-time hashed approval before it
starts. Configured
options.autoApprovemay authorize an eligible first-call launch within its fixed ceiling. Normal edit workflows retainworkflow_applyas a separate reviewed hash gate; a trusted autonomous-local drain can bind verified primary-tree apply and domain finalization into its launch approval. Elevated authority additionally requires a compatible OpenCode server (≥1.17.13). - Lane permissions are deny-by-default and re-checked against the session at launch; edit-capable lanes run in isolated git worktrees.
- Raw run artifacts under
.opencode/workflows/runs/can contain sensitive local evidence. Preferworkflow_status({ detail: "result" })andworkflow_events(both redacted) over reading raw files. - Model-facing status, completion prompts, and newly emitted exact run references do not contain absolute run/result paths. Exact duplicate-run references bind to a one-way run-root fingerprint; copied legacy references remain readable for compatibility.
- Model-facing artifact receipts expose availability, bounded file names/logical references, counts, and one-way hashes; canonical artifact directories and file paths remain private controller state.
The deep contracts — full trust model, source-of-truth hierarchy, salvage/crash recovery, apply internals — live in the docs linked below, not here.
Agents install and use this plugin exactly like users: add
@mcrescenzo/opencode-workflows to the plugin array in opencode.json and
restart OpenCode. Then drive it through tools:
- Author a workflow body (
export const meta = {...}+ top-level statements ending inreturn; no imports) and save it withworkflow_save, or run it inline. The body runs in a QuickJS sandbox with injected globals:agent,parallel,pipeline,workflow,phase,log,budget,args,persistArtifacts,inventoryFiles,drain. - Launch with
workflow_run(preview →approve: true+approvalHash; inline-source approves may omitsource— approve-by-reference). Passbackground: trueby default and useprofile: "read-only-review"until a task truly needs more. - Yield after launch instead of polling. The completion prompt normally resumes the
invoking session; then read
workflow_status({ detail: "result" })exactly once. Poll only on the explicit no-notification fallback, for user-requested progress/control, or for recovery. Foreground results are already inline. - For edit runs, review the diff plan and apply with
workflow_applyplus the required hashes. - See the bundled
workflow-plan-review,opencode-workflow-authoring, andworkflow-model-tieringskills and the tool reference below for the full contract (launch/readback, sandbox limits, fan-out arity, schemas, model tiers, edit/apply boundaries).
Use workflow_list({ format: "json" }) as the machine-canonical discovery surface
for saved and bundled workflow names, args schemas, examples, authority profile,
model-tier hints, and safe readback steps. The docs below are operator guidance,
technical contracts, or historical context.
Packaged vs GitHub-only. The tarball includes the runtime plugin, its bundled
workflow and command, all bundled skills, root package/community documents, and
docs/workflow-plugin.md. Within the docs/ tree,
docs/workflow-plugin.md is the only packaged file: it is the canonical
workflow_* tool reference
(docs/workflow-plugin.md#workflow-tool-reference) that every extension,
skill, or agent invoking workflow_run/workflow_apply/workflow_status
depends on — independent of any bundled command beyond the one flagship workflow and command pair (deep-research).
Every other doc below lives in the GitHub repository only: read it from a
source checkout, or follow the GitHub links in the table.
| Category | Documents | Packaged? |
|---|---|---|
| Packaged reference surfaces | README.md, skills/*/SKILL.md, docs/workflow-plugin.md |
Yes |
| Active operator references (GitHub only) | docs/workflow-recipes.md, docs/plugin-system-tests.md, docs/run-audit-playbook.md, docs/goal-supervision-autonomous-drains.md | No |
| Active technical contracts (GitHub only) | docs/workflow-extensions.md | No |
| Roadmap / planning (GitHub only) | docs/claude-parity-roadmap.md |
No |
| Historical snapshots / audits / completed plans (GitHub only) | docs/release-gate-validation-2026-06-16.md, docs/dogfood-rollout-2026-06-16.md, docs/workflow-autonomous-harness-design.md, docs/workflow-autonomous-harness-plan.md, docs/review-2026-06-19-bug-robustness-remediation-plan.md, docs/general-purpose-harness-extraction-plan.md, docs/superpowers/specs/2026-06-23-session-aware-model-tiering-design.md, docs/superpowers/plans/2026-06-23-session-aware-model-tiering-plan.md, docs/superpowers/plans/2026-06-23-port-repo-bughunt-to-opencode.md, docs/superpowers/specs/2026-07-07-toast-status-cards-design.md, docs/superpowers/plans/2026-07-07-design-c-gate-simplification.md, docs/superpowers/plans/2026-07-08-agent-surface-docs-accuracy.md, docs/superpowers/specs/2026-07-08-deep-research-bundled-workflow-design.md, docs/superpowers/plans/2026-07-08-deep-research-bundled-workflow.md, docs/superpowers/plans/2026-07-08-inline-approval-rekey-hardening.md, docs/superpowers/specs/2026-07-08-pure-architecture-extraction-design.md, docs/superpowers/plans/2026-07-08-pure-architecture-extraction.md, docs/superpowers/plans/2026-07-09-deep-research-hardening.md |
No |
Drains and extensions. The kernel ships no domain drain workflow or domain
extension — drain workflows (harness: "drain") are contributed by configured
trusted extensions and invoked by name (see docs/workflow-extensions.md and
the "Workflow Boundaries" section of AGENTS.md).
docs/goal-supervision-autonomous-drains.md describes the /goal oversight
boundary for such extension-supplied drains; it does not itself ship or assume a
bundled drain workflow.
Canonical safety references — apply authority and primary-tree writes, the
raw-artifact source-of-truth hierarchy, lifecycle recovery and cleanup, and the
deterministic launch-time trust checks — live in docs/workflow-plugin.md; the
complete workflow_* tool table is in
docs/workflow-plugin.md#workflow-tool-reference.
The package ships a native OpenCode workflow monitor as a dedicated TUI
plugin, alongside the server plugin, from the same npm package. Open it in
OpenCode with the /workflows command or <leader>w.
Run artifacts stay in the existing project/worktree roots, but the monitor is
scoped to the exact OpenCode session currently being viewed. Its minimal
aggregate sidebar and full-host-content workspace include only runs that session
successfully launched or resumed. /workflows, <leader>w, and clicking the
sidebar all navigate to the same namespaced public TUI route; Escape backs out
drill levels and then restores the exact prior route. With no exact host session,
the sidebar renders nothing and the route shows no workflow data.
- Adaptive layout by available host width: three panes (Runs / Phases and lanes / Detail) on wide terminals, two on medium, single-pane drill-down on narrow.
- Detail tabs: Details, Activity, and Output, with contextual recovery banners and pane maximize. Keyboard and mouse.
- Shared runs and unread results: resuming a run adds that session to its durable invoker set without removing earlier invokers, so every session that launched or resumed it can see it. Completed results stay unread until their Output loads or they are acknowledged. Acknowledgement belongs to the root-qualified run/output identity, so it is shared across those sessions and remains durable across reopen, restart, and another process viewing the same session.
- Controls: Pause/Resume (one reversible slot), resumable Stop, permanent Cancel (distinct from Remove), exact single-run Remove, bulk Cleanup (which protects unread and non-terminal runs), selected-run Save, and exact selected-lane Stop/Restart.
- Attention: when the terminal is blurred, the monitor raises a bounded, name-free host notification for results/attention transitions. This is distinct from the unchanged durable completion-notification prompt.
The monitor reads the kernel's bounded, secret-masked observer projections and
durable request/receipt files — no raw transcripts and no new run-artifact
reader. Approval stays an OpenCode-only interaction; the monitor has no approval
action and never resumes, applies, reconciles, or starts an owner.
Live execution still dies with its owner: after owner death the monitor shows
stale state; recover with workflow_reconcile (optionally workflow_salvage),
then an approved workflow_run({ resumeRunId }).
Session scoping is a display predicate, not an authorization boundary or a new
storage root. Session IDs are persisted only as domain-separated digests and are
never rendered. Existing runs without valid invocation attribution are hidden
from the monitor by design; workflow_status remains unscoped and
project/worktree-wide, so use it to inspect or recover legacy runs. There is no
project-history toggle, and a forked session does not inherit monitor visibility
unless it independently launches or resumes the run. Approval hashes, authority,
durable completion-notification targeting, and run ownership are unchanged.
Register the TUI target. The monitor is a separate registration from the
server plugin — OpenCode discovers TUI plugins only through a tui.json file.
Keep the server plugin in opencode.json (above) and add a .opencode/tui.json:
{
"$schema": "https://opencode.ai/tui.json",
"plugin": ["@mcrescenzo/opencode-workflows"]
}Restart OpenCode after changing either file. The support floor is OpenCode
>= 1.17.13; the native monitor's interactive live-test target is exactly
1.18.9. Installing the package does not modify your PATH and does not by
itself register either target.
The standalone terminal companion, its diagnostic command-line interface, and ambient lifecycle progress notifications have been removed. Use the native monitor above for live observation and control.
Monitor detail is a bounded, secret-masked display boundary, not semantic filtering. Raw prompts, reasoning, tool input/output/error, transcripts, checkpoints, journals, and full result files are excluded. A successful lane may expose one allowlisted result preview only after secret and absolute-path masking, bounded to 600 Unicode code points; names, hostnames, and business data can still remain visible within that preview. Treat raw run artifacts as local-sensitive.
Linux is the verified release platform. Windows and macOS observation have not
been runtime-verified for this release. The workflow_save/workflow_template_save
write paths, descriptor-pinned edit/integration lane control, and the attempt
journal are unsupported on those platforms and fail closed when Linux
/proc/self/fd, O_NOFOLLOW, or O_DIRECTORY guarantees are unavailable. The
destination registry filesystem must also support same-directory hard links and
directory fsync; unsupported operations fail explicitly rather than falling back
to a weaker transaction.
Ordinary workflow journals, events, and ledgers remain available on other platforms through the prior private-file implementation. On verified Linux hosts, those JSONL files additionally use descriptor-pinned target checks, cross-process append locks, fail-closed interior validation, and a version-matched validation sidecar so appends do not rescan the full history. Exact run deletion similarly uses validated-inode quarantine rename before recursive removal on Linux. It fails closed when descriptor-backed quarantine is unavailable; there is no pathname-only recursive-deletion fallback.
The npm package ships the server plugin, the native TUI monitor target
(@mcrescenzo/opencode-workflows/tui), all bundled skills, exactly one workflow
and command (deep-research), root package/community documents, and
docs/workflow-plugin.md (see files in package.json for the exact list).
The other active operator and technical docs are GitHub-only. It does not ship the "Historical snapshots / audits" or
"Roadmap / planning" docs; completed
plans stay with those historical sources. It also does not ship this repository's tests/, scripts/, or reference
extension source. The npm run ... verification commands below
are for a source checkout or contributor clone, not for an installed package tarball.
Run the nested repo workflow regression wrapper from this directory:
npm run test:workflowsThis wrapper covers the core workflow_run / workflow_apply paths. Kernel
drain, extension-seam, and durable state coverage live in the focused scripts
below and in the catch-all npm test matrix.
Run focused kernel and extension coverage from this directory:
npm run test:workflow-kernel
npm run test:workflow-adapters
npm run test:extension-seamRun the full plugin test matrix (all workflow, adapter, runtime, durable-state, and extension integration tests) from this directory:
npm testnpm test recursively discovers every tests/**/*.test.mjs file, orders the
paths deterministically, and passes the complete list to Node's built-in test
runner. The broader npm run release:no-token gate also type-checks and builds
the native TUI, packs and exercises the installed artifact with Node and Bun,
and validates the final dry-run package manifest. Its installed-artifact step
requires a tar executable on PATH.
The public CI workflow in .github/workflows/ci.yml runs that gate on
ubuntu-latest with Node 20.11.0, 22, and 24, plus the repository-pinned
Bun 1.3.8 and Node-20.11-compatible npm 10.9.4. It installs dependencies
from bun.lock. The automated release runs that same npm pin in an unprivileged
gate job, then uses the exact pinned npm 11.18.0 packer to create one release
tarball and runs the installed-artifact Node/Bun import and TUI-build smoke
against those exact validated and hashed bytes. A
checkout-free repository-write job then reserves the exact version tag. A
separate source-checkout-free minimal job uses npm 11.18.0 on Node 22.23.1
with OIDC permission, downloads that tarball by immutable artifact ID,
revalidates its SHA-256 and package contents, and publishes those exact bytes
with lifecycle scripts disabled. A final checkout-free repository-write job
creates or repairs the GitHub Release.
Checkout credentials are not persisted, every third-party action is pinned to
a full commit SHA, and GitHub's native max-queue concurrency serializes the
whole four-job flow without consuming a hosted runner while waiting. The
platform retains up to 100 pending release runs; additional runs are canceled
and must be rerun after capacity is available. Each release push must introduce
exactly one strictly increasing, npm-compatible stable X.Y.Z transition.
Prerelease and build-metadata versions are intentionally rejected until they
have an explicit channel policy; npm normalizes build metadata and a prerelease
must not silently become latest. Public CI applies an effective-merge
at-most-one, monotonic transition guard to pull requests and the exact
first-parent guard to pushes; configure that check as required before merging.
Version-bump PRs must use squash or merge-commit integration, or contain only
one version-changing commit when rebased. Because queue wait order can differ
from dispatch order, the OIDC job refreshes the current main manifest
immediately before publishing. A run whose package version is no longer current
publishes under a commit-qualified npm tag instead of moving the default
latest tag backward. Reserving the exact version tag after the no-token gate
makes both failed/ambiguous publication and post-publication finalization
crashes recoverable by rerunning the same workflow.
Already-published versions require an existing remote version tag that points
exactly at the workflow commit (annotated tags are checked at their peeled
commit); the release refuses to invent an unproven tag. Tag provenance is
checked before publishing and again before finalization, and only an explicit
registry E404 is treated as an unpublished version.
The vX.Y.Z tag records gated immutable release intent; the published GitHub
Release records completion. A reserved tag can therefore remain without an npm
version or GitHub Release after a persistent publish failure. Repair the
registry/service issue and rerun that same current-definition workflow. Do not
rerun pre-migration historical release runs, because a rerun uses the workflow
definition from its original commit.
Configure npm trusted publishing for this exact workflow and treat it as the
package's sole publisher. In npm package settings, the trusted publisher must
name this repository and release.yml, and Allowed actions must include
npm publish. Set Publishing access to Require two-factor authentication
and disallow tokens, and remove other out-of-band publication paths;
otherwise another publisher can race the registry check, reservation, and OIDC
publish boundaries. Registry reads and publication are executed from a clean
runner directory with both the default and @mcrescenzo registries pinned to
https://registry.npmjs.org, so a repository .npmrc cannot redirect them.
The repository must also enforce a v* tag ruleset that permits the release
workflow to create a missing tag but blocks tag updates and deletions, with
bypass access minimized. The workflow rechecks the reserved direct/peeled ref
immediately before npm publication and twice before GitHub Release creation;
the ruleset supplies the immutability that a read-then-publish workflow cannot
create by itself.
The full matrix remains Linux-only because Linux is the verified release
platform; it intentionally does not run publishing, token-using live probes,
the private parent integration check, or the required live child system smoke.
For system-level plugin startup checks, use docs/plugin-system-tests.md (GitHub only, not packaged). Those checks start disposable child opencode servers and verify startup health, registries, and cleanup without restarting the parent TUI.
See CONTRIBUTING.md for contributor prerequisites, lockfile
policy, and release-readiness notes. Verification matrix: npm test;
workflow_run/workflow_apply wrapper: npm run test:workflows.
The current roadmap is docs/claude-parity-roadmap.md — proposed parity work
where every item is marked proposed or [shipped]. The earlier
docs/workflow-autonomous-harness-plan.md is a historical moonshot plan
retained for context (its beads domain and live-gate subsystem were implemented
and later removed; see CHANGELOG 0.2.0). Current autonomous-drain behavior comes
from configured trusted extensions (docs/workflow-extensions.md), not a
bundled domain workflow.