opencode-workflow-guard 1.16.0 → 1.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -22
- package/docs/accountability-evolution-plan.md +9 -9
- package/docs/installation.md +47 -27
- package/docs/managed-deployment.md +3 -3
- package/docs/policies.md +98 -77
- package/docs/policy-posture.md +1 -1
- package/docs/testing.md +3 -3
- package/docs/troubleshooting.md +20 -19
- package/package.json +3 -3
- package/src/lib/custom-tools.ts +1 -0
- package/src/lib/guard-dispatcher.ts +14 -0
- package/src/lib/state.ts +45 -0
- package/src/lib/types.ts +15 -0
- package/src/lib/v2-plugin.ts +54 -1
- package/src/policies/live-docs.ts +157 -0
- package/src/policies/stale-write.ts +10 -0
- package/src/policies/sweeping-claims.ts +71 -0
- package/src/workflow-guard.ts +80 -1
- package/test/test.mts +302 -0
package/README.md
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
[](test/test.mts)
|
|
6
6
|
|
|
7
|
-
Deterministic policy and enforcement layer for OpenCode. Workflow Guard enforces
|
|
7
|
+
Deterministic policy and enforcement layer for OpenCode. Workflow Guard enforces engineering discipline through **hard plugin hooks**, not prompt instructions that an LLM can ignore. The plugin also exposes a proactive workflow-tool surface for planning, review, worktree isolation, technical debt, and policy simulation.
|
|
8
8
|
|
|
9
|
-
Workflow Guard is deliberately **not an agent harness
|
|
9
|
+
Workflow Guard is deliberately **not an agent harness**. It can constrain actions, record and explain decisions, and supply bounded context. It does not plan, prioritize, delegate, or autonomously sequence the agent's work. Bounded continuation may resume an existing session with unfinished owned todos. It never chooses the next task or creates new work.
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
@@ -22,11 +22,11 @@ opencode plugin add "opencode-workflow-guard@$VERSION" # OpenCode 2.x
|
|
|
22
22
|
opencode plugin "opencode-workflow-guard@$VERSION" --global --force # OpenCode 1.x
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
*Requires OpenCode >= 1.18.* OpenCode detects the package's server and TUI targets and updates both global configs.
|
|
25
|
+
*Requires OpenCode >= 1.18.* OpenCode detects the package's server and TUI targets and updates both global configs. Run the same command after a release to upgrade. The explicit version gives OpenCode a fresh package-cache key. Restart OpenCode after installation. See [docs/installation.md](docs/installation.md) for details and links to the OpenCode plugin documentation.
|
|
26
26
|
|
|
27
|
-
The plugin supports both generations from one package
|
|
27
|
+
The plugin supports both generations from one package. **OpenCode 1.x** loads the `server()` entrypoint, and **OpenCode 2.x** loads the `setup()` entrypoint (`@opencode/plugin`). Policies, tools, and configuration behave the same on both. V2 load tests run against a real OpenCode 2 binary in `npm run test:install`.
|
|
28
28
|
|
|
29
|
-
For the optional TUI badge, configure `"opencode-workflow-guard"` in `tui.json`. OpenCode resolves the package's exported `./tui` entrypoint automatically for TUI plugins. The companion keeps a static `Workflow Guard` shield in the prompt bar
|
|
29
|
+
For the optional TUI badge, configure `"opencode-workflow-guard"` in `tui.json`. OpenCode resolves the package's exported `./tui` entrypoint automatically for TUI plugins. The companion keeps a static `Workflow Guard` shield in the prompt bar. Warning toasts provide block details without changing agent behavior. Do not place the TUI module under the server `plugins/` directory.
|
|
30
30
|
|
|
31
31
|

|
|
32
32
|
|
|
@@ -43,12 +43,12 @@ WORKFLOW_GUARD_LIVE_E2E=1 npm run test:install # Also run policy probes with Ope
|
|
|
43
43
|
|
|
44
44
|
## Core Guardrails Overview
|
|
45
45
|
|
|
46
|
-
The plugin enforces **
|
|
46
|
+
The plugin enforces **26 deterministic policy rules** across four main pillars:
|
|
47
47
|
|
|
48
|
-
* **Branch & History Protection:** Enforces feature-branch workflows
|
|
49
|
-
* **Task & Verification Discipline:** Gates file mutations on active `todowrite` tasks
|
|
50
|
-
* **Secrets & Workspace Confinement:** Confines edit tools and recognized shell/git mutations
|
|
51
|
-
* **Destructive CLI & Environment Safety:** Intercepts destructive cloud
|
|
48
|
+
* **Branch & History Protection:** Enforces feature-branch workflows and blocks edits and commits on `main`/`master`. Direct and forced pushes to protected branches are hard-blocked. The guard checks mergeability before PR creation, requires secondary review approval bound to the reviewed worktree content, and requires a PR changelog or changeset.
|
|
49
|
+
* **Task & Verification Discipline:** Gates file mutations on active `todowrite` tasks, which subagents inherit. A task can be marked completed only after its own recorded work. Review and investigation tasks use an audited `[read-only]` marker instead. The guard also blocks concurrent direct edits to the same canonical file across sessions. It requires a same-session fresh read before `edit`/`write` can replace an existing file. Silent task deletion is blocked, and task completion requires fresh test verification evidence. Completion claims and sweeping repository claims are journaled against their evidence. Repeated failures force a live-documentation lookup instead of a guess from training data.
|
|
50
|
+
* **Secrets & Workspace Confinement:** Confines edit tools and recognized shell/git mutations to the workspace root, including symlink and `../` escape checks. Agent subshells have sensitive environment variables scrubbed, and `.env` reads return a safe schema mask. Arbitrary executables are not OS-sandboxed. Use containers or filesystem isolation when hard confinement is required.
|
|
51
|
+
* **Destructive CLI & Environment Safety:** Intercepts destructive cloud, database, and infrastructure commands. Stops script laundering and interpreter evasion, blocks TTY hangs such as `vim`, `nano`, and `sudo`, and guards against dangerous package manager flags.
|
|
52
52
|
|
|
53
53
|
For complete policy specifications and override rules, see [docs/policies.md](docs/policies.md).
|
|
54
54
|
|
|
@@ -56,17 +56,17 @@ For complete policy specifications and override rules, see [docs/policies.md](do
|
|
|
56
56
|
|
|
57
57
|
## Proactive Workflow Tools
|
|
58
58
|
|
|
59
|
-
Enforcement is only half the surface. The plugin also registers first-class agent tools
|
|
59
|
+
Enforcement is only half the surface. The plugin also registers first-class agent tools under the same names on OpenCode 1.x and 2.x, so disciplined workflow is the path of least resistance:
|
|
60
60
|
|
|
61
|
-
* **Task Planning:** `guard_next_tasks` surfaces durable repository planning context
|
|
62
|
-
* **Review & Verification:** before PR creation, a secondary reviewer subagent evaluates the diff against `guard_review_rubric
|
|
63
|
-
* **Worktree Isolation:** `guard_worktree_create` and `guard_worktree_cleanup` give parallel subagents isolated linked worktrees so concurrent mutations never collide.
|
|
64
|
-
* **Technical Debt:** `guard_review_followups` lists open P2/P3 durable review follow-ups
|
|
65
|
-
* **Policy Simulation:** `guard_why` returns the structured policy decision for a tool call or command without performing it
|
|
66
|
-
* **Project Memory** (on by default): `project_memory_search`, `project_memory_record`, `project_memory_export`, and `project_memory_import` maintain durable facts, decisions, constraints, and lessons
|
|
61
|
+
* **Task Planning:** `guard_next_tasks` surfaces durable repository planning context at session start or during planning. It reads `TODO.md`, `ROADMAP.md`, `PLAN.md`, `TASKS.md`, `BACKLOG.md`, their `docs/` counterparts, and plans under `docs/plans/`. `guard_status` reports active guardrails, branch protection, mutation counts, outstanding verification and review gates, a read-only git-hygiene snapshot, and the loaded `pluginVersion`.
|
|
62
|
+
* **Review & Verification:** before PR creation, a secondary reviewer subagent evaluates the diff against `guard_review_rubric`. The rubric covers five axes: test integrity, task completeness, cleanliness, security, and platform. The reviewer records its verdict with `record_review`, and approvals bind to the reviewed worktree content.
|
|
63
|
+
* **Worktree Isolation:** `guard_worktree_create` and `guard_worktree_cleanup` give parallel subagents isolated linked worktrees, so concurrent mutations never collide.
|
|
64
|
+
* **Technical Debt:** `guard_review_followups` lists open P2/P3 durable review follow-ups. `guard_review_followup_resolve` closes one after the underlying issue is fixed and verified.
|
|
65
|
+
* **Policy Simulation:** `guard_why` returns the structured policy decision for a tool call or command without performing it. `guard_audit` shows recent audited policy entries.
|
|
66
|
+
* **Project Memory** (on by default): `project_memory_search`, `project_memory_record`, `project_memory_export`, and `project_memory_import` maintain durable facts, decisions, constraints, and lessons. Secret content is rejected. Export promotes records to the repo-local `.opencode/memory/project-memory.jsonl`.
|
|
67
67
|
* **Socratic Learning** (off by default): `learning_checkpoint`, `learning_profile`, and `learning_record` maintain a global evidence-based learner profile with a per-session intervention budget.
|
|
68
68
|
|
|
69
|
-
The context modules
|
|
69
|
+
The context modules are project memory, Socratic learning, and recovery checkpoints (`guard_recovery_restore`). Each is independently gated, so any of them can be disabled without weakening policy enforcement. See [docs/installation.md](docs/installation.md). Full tool specifications live in [docs/policies.md](docs/policies.md#custom-tools).
|
|
70
70
|
|
|
71
71
|
---
|
|
72
72
|
|
|
@@ -74,11 +74,11 @@ The context modules — project memory, Socratic learning, and recovery checkpoi
|
|
|
74
74
|
|
|
75
75
|
| Guide | Description |
|
|
76
76
|
|---|---|
|
|
77
|
-
| [**Policy Reference**](docs/policies.md) |
|
|
78
|
-
| [**Installation & Configuration**](docs/installation.md) | Setup options, global
|
|
79
|
-
| [**Static Policy Posture**](docs/policy-posture.md) |
|
|
77
|
+
| [**Policy Reference**](docs/policies.md) | Specification of all 26 enforced policies, invariants, and the proactive tool surface (`guard_*`, `project_memory_*`, `learning_*`) |
|
|
78
|
+
| [**Installation & Configuration**](docs/installation.md) | Setup options, global and local install, worktree isolation, and project configuration |
|
|
79
|
+
| [**Static Policy Posture**](docs/policy-posture.md) | How the guard emits host-level policies for invariants a repository cannot lift. Covers both host envelopes (`experimental.policies` on 2.0.x, top-level `policies` on 2.1.x), precedence, and residual risk |
|
|
80
80
|
| [**Managed Deployment**](docs/managed-deployment.md) | Administrator-managed OpenCode policy, platform locations, and startup diagnostics |
|
|
81
|
-
| [**Troubleshooting**](docs/troubleshooting.md) | Diagnosing policy blocks, common false positives, and
|
|
81
|
+
| [**Troubleshooting**](docs/troubleshooting.md) | Diagnosing policy blocks, common false positives, and the live-system override |
|
|
82
82
|
| [**Testing Architecture**](docs/testing.md) | Test harness design, adversarial regression matrix, and CI verification |
|
|
83
83
|
| [**Contributing Guide**](CONTRIBUTING.md) | Development workflow, coding conventions, test requirements, and changeset PR rules |
|
|
84
84
|
| [**Security Policy**](SECURITY.md) | Vulnerability disclosure policy and threat model |
|
|
@@ -20,18 +20,18 @@ Policy decisions and operational evidence are separate concepts.
|
|
|
20
20
|
|
|
21
21
|
`PolicyDecision` is the result of evaluating whether an operation may proceed. It should provide a stable machine-readable status and code, an optional policy identifier, a human-readable message, and bounded structured details. Human block text, audit entries, `guard_why`, and controller-facing output should derive from this result rather than independently interpreting policy state.
|
|
22
22
|
|
|
23
|
-
`EvidenceRecord` represents operational proof or observation
|
|
23
|
+
`EvidenceRecord` represents operational proof or observation. Examples are verification, secondary review, tool outcome, policy evaluation, and lifecycle state. Evidence records should carry common project, session, and worktree provenance where applicable. Existing purpose-built stores remain authoritative. This work should introduce a common representation rather than another database.
|
|
24
24
|
|
|
25
25
|
Evidence must be bound to the subject it actually establishes. Where applicable, that means identifying the workspace/worktree, commit or worktree fingerprint, session/call, actor or reviewer, policy/version, and observation time. A historical fact such as "verification passed" is not sufficient to establish that the current worktree is verified unless its subject still matches current state.
|
|
26
26
|
|
|
27
27
|
Operational evidence should distinguish confidence classes rather than treating all machine-readable claims as equivalent:
|
|
28
28
|
|
|
29
29
|
- Deterministic observation: repository state, changed files, tool execution/outcome, test exit status, or another directly observed fact.
|
|
30
|
-
- Attestation: a reviewer, model, or human records a judgment such as approval. The fact that the attestation occurred is observable
|
|
30
|
+
- Attestation: a reviewer, model, or human records a judgment such as approval. The fact that the attestation occurred is observable. The judgment is not converted into deterministic truth.
|
|
31
31
|
- Derived state: a reproducible conclusion such as "verification is fresh" computed from subject-bound observations and current state.
|
|
32
32
|
- Agent assertion: natural-language claims such as "tests pass" or "review is complete". These may be useful diagnostics but are never sufficient evidence for a safety or finalization gate by themselves.
|
|
33
33
|
|
|
34
|
-
Prefer outcome and state predicates over prescribed trajectories. Workflow Guard may require demonstrable end state or consequential checkpoints
|
|
34
|
+
Prefer outcome and state predicates over prescribed trajectories. Workflow Guard may require demonstrable end state or consequential checkpoints. It should not require one exact sequence of otherwise-valid agent actions when the desired state can be established directly.
|
|
35
35
|
|
|
36
36
|
Project memory and learning evidence remain separate from `EvidenceRecord`. Project memory represents durable repository knowledge. Learning evidence represents observations from real Socratic interactions. Neither should be forced into an allow/block or operational-proof model.
|
|
37
37
|
|
|
@@ -46,7 +46,7 @@ Project memory and learning evidence remain separate from `EvidenceRecord`. Proj
|
|
|
46
46
|
7. Expand adversarial tests for stable decision codes, provenance invalidation, option/profile precedence, and machine-readable status. Add headless/server coverage proving core enforcement does not depend on the TUI or interactive approval.
|
|
47
47
|
8. Update policy, installation, and managed-deployment documentation as behavior becomes user-visible.
|
|
48
48
|
|
|
49
|
-
The accountability contracts now support an opt-in bounded autonomous-continuation add-on ("Ralph mode"). Its initial implementation only continues an agent while explicitly owned native todos remain unfinished
|
|
49
|
+
The accountability contracts now support an opt-in bounded autonomous-continuation add-on ("Ralph mode"). Its initial implementation only continues an agent while explicitly owned native todos remain unfinished. It does not interpret configured documents or invent completion criteria. It is disabled by default, including under the `autonomous` profile. It is user-interruptible, iteration-bounded, and preserves all core guardrails. It reports observable outcomes such as `completed`, `budget_exhausted`, and `user_stopped`. `blocked` is reserved for a demonstrable guard state rather than inferred from repeated idle events. Ralph remains an optional orchestration consumer rather than part of the accountability kernel.
|
|
50
50
|
|
|
51
51
|
## Configuration Boundary
|
|
52
52
|
|
|
@@ -54,13 +54,13 @@ Safety invariants stay core and are not weakened by operation profiles or option
|
|
|
54
54
|
|
|
55
55
|
Capabilities that express user workflow preference, add continuity/context, or approach orchestration must be configurable. Examples include project memory, Socratic learning, recovery checkpoints, review/documentation requirements, bounded continuation behavior, and future autonomous-operation conveniences. Optional capabilities must fail independently where practical so disabling or failing an add-on does not disable core enforcement.
|
|
56
56
|
|
|
57
|
-
Workflow Guard must not become responsible for planning work, choosing the next task, delegating tasks
|
|
57
|
+
Workflow Guard must not become responsible for planning work, choosing the next task, or delegating tasks. It also must not own autonomous retry loops, agent sequencing, or the decision that an external job should terminate. Those are orchestration duties. External SDK or server clients and dedicated add-ons own them. The bounded `session.idle` continuation may enforce already-owned unfinished work. It must never grow into an orchestration loop.
|
|
58
58
|
|
|
59
59
|
The same boundary applies to review. Workflow Guard can prove that a review occurred against a particular subject, preserve the reviewer's disposition and findings, and enforce a configured review requirement. It must not infer that an approval proves correctness, choose reviewers, launch review loops, negotiate findings, or decide what work should happen next.
|
|
60
60
|
|
|
61
61
|
## External Research Rationale
|
|
62
62
|
|
|
63
|
-
Recent OpenAI and Anthropic engineering
|
|
63
|
+
Recent OpenAI and Anthropic engineering and research material independently supports this architecture. These sources are design inputs rather than normative dependencies. Workflow Guard should continue to rely on supported OpenCode APIs and its own tested contracts.
|
|
64
64
|
|
|
65
65
|
- OpenAI's February 2026 [Harness engineering: leveraging Codex in an agent-first world](https://openai.com/index/harness-engineering/) describes mechanically enforced architectural invariants around agent autonomy. The applicable inference for Workflow Guard is to enforce boundaries centrally while leaving implementation strategy and task execution to the agent.
|
|
66
66
|
- OpenAI's March 2025 [Detecting misbehavior in frontier reasoning models](https://openai.com/index/chain-of-thought-monitoring/) reports coding agents exploiting evaluation loopholes, including weakening or altering verification. The applicable inference is to retain independent provenance about changed artifacts and verification subjects rather than trusting reported success or an undifferentiated test-pass signal.
|
|
@@ -71,11 +71,11 @@ Recent OpenAI and Anthropic engineering/research material independently supports
|
|
|
71
71
|
- Anthropic's September 2025 [Effective context engineering for AI agents](https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents) recommends the smallest high-signal context and just-in-time retrieval. The applicable inference is that `guard_status` should expose compact current state and stable evidence references rather than inject an ever-growing accountability transcript.
|
|
72
72
|
- Anthropic's December 2024 [Building effective agents](https://www.anthropic.com/engineering/building-effective-agents) separates deterministic workflows from model-directed agents, recommends simple composable mechanisms, and uses programmatic gates where checks are appropriate. The applicable inference is to keep Workflow Guard a guardrail/accountability layer, not an orchestrator-worker or evaluator-optimizer implementation.
|
|
73
73
|
|
|
74
|
-
The combined design principle is
|
|
74
|
+
The combined design principle is this. Workflow Guard answers one question: "May this operation happen, given what can actually be demonstrated?" It does not answer, "What should the agent do next?"
|
|
75
75
|
|
|
76
76
|
## OpenCode API Boundary
|
|
77
77
|
|
|
78
|
-
Use supported semantic OpenCode APIs wherever they provide enough information. `tool.execute.before` is the supported deterministic tool-blocking boundary. Current `session.idle` events and `experimental.text.complete`
|
|
78
|
+
Use supported semantic OpenCode APIs wherever they provide enough information. `tool.execute.before` is the supported deterministic tool-blocking boundary. Current `session.idle` events and `experimental.text.complete` expose no explicit supported completion-veto result. Workflow Guard must therefore not emulate a hard completion gate through undocumented exception behavior.
|
|
79
79
|
|
|
80
80
|
Machine-readable controller outcomes should therefore come from Workflow Guard's own structured contracts, tools, and audit trail until OpenCode exposes a stronger lifecycle decision hook.
|
|
81
81
|
|
|
@@ -84,7 +84,7 @@ Shell matching remains defense in depth rather than a sandbox. Prefer structured
|
|
|
84
84
|
## Compatibility And Non-Goals
|
|
85
85
|
|
|
86
86
|
- Preserve existing human-readable block messages at the hook boundary while internal consumers migrate to structured decisions.
|
|
87
|
-
- Preserve existing verification history, review state, project-memory databases, learning profiles, and review-follow-up storage
|
|
87
|
+
- Preserve existing verification history, review state, project-memory databases, learning profiles, and review-follow-up storage. Migrate representations before considering storage changes.
|
|
88
88
|
- Do not merge project memory, learner evidence, and operational evidence into a universal event store.
|
|
89
89
|
- Do not claim managed deployment proves plugin provenance when OpenCode cannot provide that guarantee.
|
|
90
90
|
- Do not add a hard completion gate until OpenCode exposes a supported block/continue lifecycle hook.
|
package/docs/installation.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
## Requirements
|
|
8
8
|
|
|
9
|
-
- **OpenCode >= 1.18** (V1: requires the `GET /session/:id/todo` endpoint and the V1 `PluginModule` loader) or **OpenCode 2.x
|
|
9
|
+
- **OpenCode >= 1.18** (V1: requires the `GET /session/:id/todo` endpoint and the V1 `PluginModule` loader) or **OpenCode 2.x**. V2 loads through the `@opencode/plugin` API. The current todo list is reconstructed from session message history, or read from the native `todowrite` on 2.1.x when `experimental.todo` is enabled.
|
|
10
10
|
- **Node.js >= 22.18** or **Bun** (for running test scripts directly).
|
|
11
11
|
|
|
12
12
|
### Version Qualification
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
The guard declares exact host dependency versions, so an unreviewed host API can
|
|
15
15
|
never float in. A host release is qualified only when it appears in this table
|
|
16
16
|
with the seats that were actually exercised against it. An unlisted version is
|
|
17
|
-
unqualified by definition
|
|
17
|
+
unqualified by definition. That does not mean it is broken. It means nobody has
|
|
18
18
|
verified it.
|
|
19
19
|
|
|
20
20
|
| Guard | OpenCode qualified | Hook seats exercised | Notes |
|
|
@@ -25,8 +25,8 @@ verified it.
|
|
|
25
25
|
|
|
26
26
|
The 2.1.3 pins require the 2.1.x package set. `@opencode/plugin` and
|
|
27
27
|
`@opencode/ai` are pinned to `2.1.3`, which depends on `effect@4.0.1` and
|
|
28
|
-
requires `@opentui/solid >=0.6.6
|
|
29
|
-
2.1.x host fails to load at all
|
|
28
|
+
requires `@opentui/solid >=0.6.6`. The guard pins `0.6.6`. A 2.0.x pin set on a
|
|
29
|
+
2.1.x host fails to load at all. The plugin imports an `effect` path the host no
|
|
30
30
|
longer provides, and the host reports `failed to load plugin` while every
|
|
31
31
|
policy stays inert.
|
|
32
32
|
|
|
@@ -66,11 +66,11 @@ opencode plugin add "opencode-workflow-guard@$VERSION" # Open
|
|
|
66
66
|
opencode plugin "opencode-workflow-guard@$VERSION" --global --force # OpenCode 1.x
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
-
OpenCode 2 documents `opencode plugin add <package>` as the command to install a plugin and add it to the global configuration
|
|
69
|
+
OpenCode 2 documents `opencode plugin add <package>` as the command to install a plugin and add it to the global configuration. OpenCode 1 documents `opencode plugin <module>` with `--global` and `--force`. Workflow Guard exposes both server and TUI targets, so OpenCode detects both and updates their global configuration files. OpenCode 2 uses one global `cli.json` for terminal-client settings. Restart OpenCode after installation, because configuration is loaded at startup. See the [OpenCode plugin CLI documentation](https://opencode.ai/docs/cli/#plugin).
|
|
70
70
|
|
|
71
|
-
Use the same two commands to upgrade after a Workflow Guard release. Keep the explicit version in OpenCode's configuration rather than relying on a bare package name or `@latest
|
|
71
|
+
Use the same two commands to upgrade after a Workflow Guard release. Keep the explicit version in OpenCode's configuration rather than relying on a bare package name or `@latest`. OpenCode caches npm plugins, and an already-populated `@latest` cache key may keep resolving to the version that originally populated it. An explicit new version creates a new package-cache key without manually deleting OpenCode's cache.
|
|
72
72
|
|
|
73
|
-
If you only want the server guard without the TUI companion, add the package to your project's `opencode.json
|
|
73
|
+
If you only want the server guard without the TUI companion, add the package to your project's `opencode.json`, or to the global `~/.config/opencode/opencode.json`. OpenCode 2 reads only the plural `plugins` key. OpenCode 1.x uses the singular `plugin` key. The OpenCode 2 config schema drops a singular key without an error. The guard then appears configured and never loads:
|
|
74
74
|
|
|
75
75
|
```json
|
|
76
76
|
{
|
|
@@ -104,18 +104,18 @@ config or a non-array plugin key.
|
|
|
104
104
|
`tui.json` is the exception to the key rule. Its schema reads singular `plugin`
|
|
105
105
|
on both majors, and OpenCode 2 migrates that key into `cli.json.plugins`. So
|
|
106
106
|
`--major 2` writes `plugins` to the server config and still writes `plugin` to
|
|
107
|
-
`tui.json
|
|
107
|
+
`tui.json`. A plural key there is silently ignored, and the TUI badge never
|
|
108
108
|
loads.
|
|
109
109
|
|
|
110
110
|
### Configure The Guard In One Location Only
|
|
111
111
|
|
|
112
|
-
Name the guard in exactly one place per OpenCode process: either the global configuration
|
|
112
|
+
Name the guard in exactly one place per OpenCode process: either the global configuration or a project configuration, never both. The global configuration is `~/.config/opencode/opencode.json[c]`, with `plugins` on 2.x or `plugin` on 1.x. OpenCode 2 merges plugin arrays instead of replacing them. Listing `opencode-workflow-guard` globally and again in a project's `.opencode/opencode.json` therefore loads the same entrypoint twice under two ids. Two instances register duplicate hooks and duplicate tool definitions. That evaluates and audits every tool call twice. One instance can overwrite the other's tool-description enrichment, and both prime the process-global workspace latch from two roots in the same tick.
|
|
113
113
|
|
|
114
|
-
The guard detects the duplicate instead of failing silently
|
|
114
|
+
The guard detects the duplicate instead of failing silently. The first instance for a given (module path, workspace root) pair owns enforcement, and a later instance registers no hooks or tools. The duplicate is recorded at warn level in the server log and as an audit record (`tool: "plugin.instance"`, `reason: "duplicate_plugin_load"`) in `workflow-guard.jsonl`. A server log with two `loading plugin.*workflow-guard` lines on a fresh start is therefore a configuration error, not a supported setup. As a second layer, the first instance to reach a given (session, call, tool) claims its policy decision. A guard loaded from two distinct paths, such as an npm cache copy plus a source checkout, still produces one decision per call.
|
|
115
115
|
|
|
116
116
|
A repository that ships its own `.opencode/opencode.json` with a `plugin`/`plugins` entry is the usual source of the second copy. Remove the entry there and let the global configuration carry the guard. Because OpenCode settings are tamper-protected from agent edits, that file must be changed by the operator.
|
|
117
117
|
|
|
118
|
-
A configured bare package name is resolved through Node's parent-directory lookup from OpenCode's configuration location
|
|
118
|
+
A configured bare package name is resolved through Node's parent-directory lookup from OpenCode's configuration location. Any `node_modules` above that location that contains a copy of the package is loaded instead. That includes a pnpm-managed home directory, a manual `npm install`, and a workspace dependency. This shadows both OpenCode's own plugin cache and a global `npm install -g`. Updating the wrong copy leaves an old version running while the update appears to succeed. OpenCode's own plugin-update check can also fail with only a server-log record (`NpmInstallFailedError`). Update the copy that actually loads. For a pnpm-managed home directory, run `pnpm add opencode-workflow-guard@<version>` in that directory, or remove the dependency so OpenCode manages its own copy. Then restart the background service with `opencode service restart`. The plugin loads there, so a TUI-only restart is not enough. Verify what loaded in three places. `guard_status` reports the loaded version as `pluginVersion`. The startup app log states `Workflow Guard v<version> plugin initialized`. The server log records the resolved module path in its `loading plugin` entry.
|
|
119
119
|
|
|
120
120
|
---
|
|
121
121
|
|
|
@@ -130,7 +130,7 @@ cp -r src/lib src/policies .opencode/workflow-guard-source/
|
|
|
130
130
|
printf '%s\n' 'import { WorkflowGuard } from "../workflow-guard-source/workflow-guard.ts";' 'export default { id: "workflow-guard-local", server: WorkflowGuard };' > .opencode/plugins/workflow-guard.ts
|
|
131
131
|
```
|
|
132
132
|
|
|
133
|
-
OpenCode automatically discovers the adapter from the project-level `.opencode/plugins/` directory at startup. For deterministic development and test fixtures, it can also be registered explicitly in `.opencode/opencode.json` as `"./plugins/workflow-guard.ts"
|
|
133
|
+
OpenCode automatically discovers the adapter from the project-level `.opencode/plugins/` directory at startup. For deterministic development and test fixtures, it can also be registered explicitly in `.opencode/opencode.json` as `"./plugins/workflow-guard.ts"`. OpenCode deduplicates the resulting file URL. npm installation is recommended for normal use. The local-file form is primarily useful when developing from a source checkout.
|
|
134
134
|
|
|
135
135
|
---
|
|
136
136
|
|
|
@@ -138,7 +138,7 @@ OpenCode automatically discovers the adapter from the project-level `.opencode/p
|
|
|
138
138
|
|
|
139
139
|
To enable the static `Workflow Guard 🛡️` prompt-bar badge, with block details reported separately through warning toasts:
|
|
140
140
|
|
|
141
|
-
The recommended `opencode plugin add "opencode-workflow-guard@$VERSION"` command configures this automatically
|
|
141
|
+
The recommended `opencode plugin add "opencode-workflow-guard@$VERSION"` command configures this automatically. OpenCode detects both targets and registers the server plugin in the global `opencode.json(c)`. On OpenCode 2 it also registers the TUI companion in the global `~/.config/opencode/cli.json`. For manual installation, use the same explicit package version in the TUI target of your OpenCode generation. On OpenCode 2 that target is the global `~/.config/opencode/cli.json` with the `plugins` array:
|
|
142
142
|
|
|
143
143
|
```json
|
|
144
144
|
{
|
|
@@ -160,7 +160,7 @@ or `~/.config/opencode/tui.json` with the `plugin` array on OpenCode 1.x:
|
|
|
160
160
|
}
|
|
161
161
|
```
|
|
162
162
|
|
|
163
|
-
The version above is illustrative
|
|
163
|
+
The version above is illustrative. Replace it with the version you intend to install. The package exposes separate server and TUI entrypoints. In the terminal-client config, configure the package spec. OpenCode resolves its exported `./tui` entrypoint automatically. Do not place `workflow-guard-ui.ts` under `plugins/`, because the server loader rejects TUI-only modules.
|
|
164
164
|
|
|
165
165
|
On OpenCode 2, `plugin check` and `plugin update` cover both server and TUI-only package plugins.
|
|
166
166
|
|
|
@@ -168,7 +168,7 @@ The TUI badge uses OpenCode's Solid slot API (`@opentui/solid`). It is a runtime
|
|
|
168
168
|
|
|
169
169
|
## Recovery Checkpoints
|
|
170
170
|
|
|
171
|
-
Workflow Guard supports two optional operation profiles in `.opencode/workflow-guard.json
|
|
171
|
+
Workflow Guard supports two optional operation profiles in `.opencode/workflow-guard.json`. JSONC is also supported. `interactive` is the default and preserves the standard behavior. `autonomous` enables durable recovery checkpoints by default for longer-running agent work. Profiles only supply defaults for optional workflow behavior. They never weaken secret protection, protected-path protection, workspace confinement, protected-branch safeguards, or other deterministic policy checks.
|
|
172
172
|
|
|
173
173
|
```json
|
|
174
174
|
{
|
|
@@ -188,15 +188,15 @@ Durable recovery checkpoints can also be enabled directly per project:
|
|
|
188
188
|
|
|
189
189
|
For each genuine user run in a root session, Workflow Guard captures the pre-run tracked and untracked workspace state in private Git objects. The objects are kept reachable under `refs/workflow-guard/checkpoints/` and do not add entries to the user's stash list. Subagent runs and Workflow Guard's synthetic continuation messages do not create or replace checkpoints.
|
|
190
190
|
|
|
191
|
-
When the root session reaches idle, the checkpoint records the resulting workspace fingerprint. The `guard_recovery_restore` tool can then restore a selected run
|
|
191
|
+
When the root session reaches idle, the checkpoint records the resulting workspace fingerprint. The `guard_recovery_restore` tool can then restore a selected run. It works only for that same root session, and only while the workspace still exactly matches the recorded idle boundary. Any intervening workspace change makes recovery refuse rather than overwrite newer work. Recovery checkpoints require an existing Git commit, and they fail open if a snapshot cannot be created.
|
|
192
192
|
|
|
193
|
-
With the optional TUI companion installed, open the command palette and choose **Workflow Guard: Project Options
|
|
193
|
+
With the optional TUI companion installed, open the command palette and choose **Workflow Guard: Project Options**, or run `/guard-options`. The command toggles recovery checkpoints, project memory, learner mode, Ralph mode, or the title-settle workaround for the current project. It writes the existing Workflow Guard project config location, which defaults to `.opencode/workflow-guard.json`. Restart OpenCode after changing a setting, so the server plugin can expose the matching tools.
|
|
194
194
|
|
|
195
|
-
The title-settle workaround is on by default because Workflow Guard's automatic continuation can race OpenCode's asynchronous native title generation. Workflow Guard briefly waits for native title generation before sending an automatic continuation for unfinished root-session work
|
|
195
|
+
The title-settle workaround is on by default, because Workflow Guard's automatic continuation can race OpenCode's asynchronous native title generation. Workflow Guard briefly waits for native title generation before sending an automatic continuation for unfinished root-session work. Set `titleSettleWorkaround: false` to opt out. The wait is bounded. It applies only when that session owns unfinished todos, and it never changes the title or provider and model parameters itself.
|
|
196
196
|
|
|
197
|
-
Ralph mode is also off by default, including under the `autonomous` profile. Enable it explicitly with `ralphMode: true
|
|
197
|
+
Ralph mode is also off by default, including under the `autonomous` profile. Enable it explicitly with `ralphMode: true`. `ralphMaxIterations` sets its positive integer continuation budget from 1 through 100. The default is 10, and invalid values use the default. Ralph only continues a root session while that session already owns unfinished native todos. It does not create, prioritize, select, sequence, or delegate work, and every resumed run remains subject to the normal guardrails. Genuine user input stops an active Ralph run instead of resetting its budget. Reaching the budget with unfinished work reports `budget_exhausted`. Completion is reported only when owned todos are actually finished. A `blocked` outcome is reserved for a demonstrable guard state and is never inferred merely from repeated idle events. `guard_status` exposes the effective Ralph configuration and current calling-session outcome.
|
|
198
198
|
|
|
199
|
-
OpenCode's model
|
|
199
|
+
OpenCode's model and provider request timeout is separate from Workflow Guard. In OpenCode 1.18+, configure it under the selected provider's `options.timeout` in project `opencode.json[c]`. The default is 300000 ms (5 minutes), and `false` disables the provider request timeout. Workflow Guard does not impose a whole-session wall-clock limit.
|
|
200
200
|
|
|
201
201
|
---
|
|
202
202
|
|
|
@@ -204,7 +204,7 @@ OpenCode's model/provider request timeout is separate from Workflow Guard. In Op
|
|
|
204
204
|
|
|
205
205
|
Workflow Guard enforces secondary review approval (`requireReview`) by default during PR creation (`gh pr create` and `az repos pr create`). Before a pull request can be opened, an agent must invoke a secondary review subagent and record passing review approval using `record_review` evaluated against `guard_review_rubric`.
|
|
206
206
|
|
|
207
|
-
The recorded verdict
|
|
207
|
+
The recorded verdict binds to the reviewed repository content: the commit hash and the tracked-content worktree fingerprint of the directory it was recorded against. The PR preflight accepts any approval whose binding matches the tree the PR would publish. A review recorded by a subagent rooted in a linked git worktree therefore satisfies a `pr create` run from that worktree. That holds even from a different session than the one that spawned the reviewer. Review freshness is scoped to tracked content. Deleting an untracked scratch file does not invalidate an approval, and any tracked edit or new commit does. Reviewer agents are frequently configured with read-only toolsets that exclude `record_review`. The tool therefore accepts recorders of any session type and binds the verdict to the recording session's directory. The audit trail records who recorded it and where it is bound.
|
|
208
208
|
|
|
209
209
|
To opt out of the PR secondary review gate for a project, set `requireReview: false` in `.opencode/workflow-guard.json`:
|
|
210
210
|
|
|
@@ -216,7 +216,27 @@ To opt out of the PR secondary review gate for a project, set `requireReview: fa
|
|
|
216
216
|
|
|
217
217
|
Or set `WORKFLOW_GUARD_REQUIRE_REVIEW=0` in your environment. Conversely, `WORKFLOW_GUARD_REQUIRE_REVIEW=1` enforces the review gate regardless of repository configuration.
|
|
218
218
|
|
|
219
|
-
For stricter environments, set `requireSubagentReview: true` in `.opencode/workflow-guard.json
|
|
219
|
+
For stricter environments, set `requireSubagentReview: true` in `.opencode/workflow-guard.json`. That setting rejects approvals recorded by the root session itself. A passing verdict must then be recorded from a subagent session, which is one with a parent session. The root session can never approve its own work. Changes-requested findings remain relayable from any session. `WORKFLOW_GUARD_REQUIRE_SUBAGENT_REVIEW=1` enables the same requirement for any project. `WORKFLOW_GUARD_REQUIRE_SUBAGENT_REVIEW=0` disables it even when project config enables it.
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Live-Documentation Research Floor
|
|
224
|
+
|
|
225
|
+
Policy 25 is enabled by default. A session that accumulates failed tool calls with no live-documentation lookup since gets its next mutating or shell call blocked. The block holds until the agent consults a current source. Only non-research failures count, so a failed `websearch` never deepens the block. Reads, greps, and research tools are never gated.
|
|
226
|
+
|
|
227
|
+
The builtin research names cover `websearch`, `webfetch`, and common aliases. MCP documentation servers are matched on whole server names. That means a guard inspection tool or a generic file reader cannot clear the debt by name accident. If your docs server uses an unrecognised name, declare it:
|
|
228
|
+
|
|
229
|
+
```json
|
|
230
|
+
{
|
|
231
|
+
"requireLiveDocs": true,
|
|
232
|
+
"liveDocFailureThreshold": 3,
|
|
233
|
+
"researchTools": ["acme-docs", "internal-wiki"]
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Or set `WORKFLOW_GUARD_REQUIRE_LIVE_DOCS=0` to disable the floor, `WORKFLOW_GUARD_LIVE_DOC_THRESHOLD` to retune the trigger, and `WORKFLOW_GUARD_RESEARCH_TOOLS` (comma-separated) to add research names. An unparseable, zero, or negative threshold falls back to the default of 3.
|
|
238
|
+
|
|
239
|
+
The acting agent must be permitted to call at least one research tool. An agent whose toolset denies `websearch`/`webfetch` and exposes no MCP docs server cannot satisfy the floor, so grant one or disable the policy.
|
|
220
240
|
|
|
221
241
|
---
|
|
222
242
|
|
|
@@ -259,7 +279,7 @@ Project memory and Socratic learning are context modules, not part of Workflow G
|
|
|
259
279
|
|
|
260
280
|
### Experimental Learning Mode
|
|
261
281
|
|
|
262
|
-
Socratic learning is disabled by default. Enable its agent-facing tools with the `learning: true` project option
|
|
282
|
+
Socratic learning is disabled by default. Enable its agent-facing tools with the `learning: true` project option, including through `/guard-options`. Alternatively, set `WORKFLOW_GUARD_LEARNING=1` in your user environment before OpenCode starts. That enables it regardless of the project setting.
|
|
263
283
|
|
|
264
284
|
To customize the per-session intervention budget, add:
|
|
265
285
|
|
|
@@ -269,7 +289,7 @@ To customize the per-session intervention budget, add:
|
|
|
269
289
|
}
|
|
270
290
|
```
|
|
271
291
|
|
|
272
|
-
to `.opencode/workflow-guard.json` (JSONC is also supported). The intervention budget limits high-value checkpoints per session
|
|
292
|
+
to `.opencode/workflow-guard.json` (JSONC is also supported). The intervention budget limits high-value checkpoints per session. Learning remains advisory and does not alter policy enforcement or direct the workflow.
|
|
273
293
|
|
|
274
294
|
The global learner profile is stored at `$XDG_DATA_HOME/opencode/workflow-guard/learner-profile.json`, falling back to `~/.local/share/opencode/workflow-guard/learner-profile.json`. The parent directory and file are created with user-only permissions where supported. The profile contains distilled learning evidence and project/session references, so it can be backed up or removed separately from any repository.
|
|
275
295
|
|
|
@@ -279,10 +299,10 @@ The global learner profile is stored at `$XDG_DATA_HOME/opencode/workflow-guard/
|
|
|
279
299
|
|
|
280
300
|
Project memory is optional supporting context, enabled by default, and requires no additional dependency or service. Set `projectMemory: false` (or toggle it through `/guard-options`) to disable its initialization and tools for a project without weakening policy enforcement. Its authoritative working index is a local SQLite database under `$XDG_DATA_HOME/opencode/workflow-guard/project-memory/`, falling back to `~/.local/share/opencode/workflow-guard/project-memory/`. Repositories use their Git common directory as the local identity where available, so linked worktrees share an index.
|
|
281
301
|
|
|
282
|
-
The plugin exposes `project_memory_search`, `project_memory_record`, `project_memory_export`, and `project_memory_import`. Recording is intended only for durable facts, decisions, constraints, and lessons
|
|
302
|
+
The plugin exposes `project_memory_search`, `project_memory_record`, `project_memory_export`, and `project_memory_import`. Recording is intended only for durable facts, decisions, constraints, and lessons. Recognized secret content is rejected. Routine tool, test, and verification output, transient errors and retries, successful operational telemetry, todos, and session progress belong in their existing operational stores. They do not belong in project memory.
|
|
283
303
|
|
|
284
|
-
Project-memory maintenance runs opportunistically when a project index is opened, at most once per day. Superseded memories and resolved review follow-ups are retained for 90 days and then pruned
|
|
304
|
+
Project-memory maintenance runs opportunistically when a project index is opened, at most once per day. Superseded memories and resolved review follow-ups are retained for 90 days and then pruned. Current memories are not expired just because they are old. Maintenance checkpoints and compacts the active SQLite database. It also reaps coordination markers (`*.sqlite.active.*`) left behind by processes that died without closing their store. Markers owned by live or unidentifiable processes are never removed. When the project-memory directory exceeds 100 MB, least-recently-used inactive project databases are evicted toward about 80 MB when eligible databases exist. The current project's database is never selected for global eviction, and maintenance failures do not disable core guard enforcement.
|
|
285
305
|
|
|
286
306
|
Portable project knowledge lives at the fixed path `.opencode/memory/project-memory.jsonl`. Importing that file bootstraps the local index, but exporting is always explicit and accepts selected record IDs rather than dumping private working memory. The plugin adds `.opencode/memory/` to `.git/info/exclude` for the local clone. To share promoted knowledge through Git, deliberately remove that local exclusion or force-add the JSONL file after reviewing its contents. Do not commit the SQLite database.
|
|
287
307
|
|
|
288
|
-
Compaction receives a bounded set of recent current local memories. Imported portable records are never injected automatically and require an explicit memory search. Path-backed local memories with a recorded Git commit are excluded when those paths have committed, staged, or unstaged changes since that commit
|
|
308
|
+
Compaction receives a bounded set of recent current local memories. Imported portable records are never injected automatically and require an explicit memory search. Path-backed local memories with a recorded Git commit are excluded when those paths have committed, staged, or unstaged changes since that commit. Repository contents remain authoritative over remembered context. Memory supplies context rather than instructions. It does not select, prioritize, or sequence work.
|
|
@@ -6,10 +6,10 @@ As documented by OpenCode, managed configuration has the highest configuration p
|
|
|
6
6
|
|
|
7
7
|
A typical managed configuration should restrict organization-level capabilities, for example with `enabled_providers`, `disabled_providers`, `experimental.policies` (2.0.x) or top-level `policies` (2.1.x), and `permission`. Install workflow-guard through your controlled OpenCode distribution or configuration alongside those settings, and pin/review the package version according to your normal software-supply-chain process.
|
|
8
8
|
|
|
9
|
-
Workflow Guard's project `profile` is a convenience for optional workflow defaults, not a security level. The `interactive` default and `autonomous` profile enforce the same core safety invariants
|
|
9
|
+
Workflow Guard's project `profile` is a convenience for optional workflow defaults, not a security level. The `interactive` default and the `autonomous` profile enforce the same core safety invariants. `autonomous` currently enables recovery checkpoints by default. Explicit project feature settings override profile defaults. Ralph mode is orchestration-adjacent and therefore remains disabled unless the project explicitly sets `ralphMode: true`. Selecting `autonomous` does not enable it.
|
|
10
10
|
|
|
11
|
-
For headless or centrally operated environments, use `guard_status` and `guard_why` as the machine-readable accountability surfaces
|
|
11
|
+
For headless or centrally operated environments, use `guard_status` and `guard_why` as the machine-readable accountability surfaces. Do not scrape log prose, and do not rely on the optional TUI. `guard_status` reports effective configuration, fresh verification and review references, bounded outstanding requirements, and Ralph configuration and outcome. `guard_why` evaluates the authoritative structured policy decision in simulation mode. It performs no verification, remote PR lookups, durable audit writes, or other mutation accounting. A simulated Git operation may pass local checks while its final result depends on GitHub or Azure PR state that was intentionally not queried. In that case `guard_why` reports `needs_approval` with code `remote_state_unchecked` instead of claiming the operation is allowed. Core server enforcement does not depend on the TUI.
|
|
12
12
|
|
|
13
|
-
At startup workflow-guard writes an informational app-log diagnostic
|
|
13
|
+
At startup workflow-guard writes an informational app-log diagnostic. It states whether a file-based managed configuration was detected at the platform's standard location. This is observational only. OpenCode's V1 plugin API does not provide a supported provenance or trust signal proving that workflow-guard itself came from an administrator-controlled source. Managed configuration precedence also does not prove that untrusted project plugins cannot run alongside it.
|
|
14
14
|
|
|
15
15
|
Reference: https://opencode.ai/docs/config/#managed-settings (retrieved August 25, 2026).
|