You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This commit was created on GitHub.com and signed with GitHub’s verified signature.
Minor Changes
4797e62: The loaded guard version is now visible: guard_status reports it as pluginVersion, and the startup app log states Workflow Guard v<version> plugin initialized. This closes the silent-stale-installation trap where OpenCode resolves a configured bare plugin name through Node's parent-directory lookup from its configuration location — a copy in a parent node_modules (for example a pnpm-managed home directory) shadows both OpenCode's plugin cache and a global npm install -g, so updates to the wrong surface leave an old version running. docs/installation.md and docs/troubleshooting.md now document the shadowing behavior, how to verify which copy actually loaded, and how to update it (opencode service restart reloads the background service).
3ec7161: record_review supports an opt-in strict recorder mode: setting requireSubagentReview: true in .opencode/workflow-guard.json (or WORKFLOW_GUARD_REQUIRE_SUBAGENT_REVIEW=1) rejects approvals recorded by the root session itself, so a passing verdict must be recorded from a subagent session with a parent. This closes the root-session self-approval path that content binding intentionally leaves open for relay flows with read-only reviewer toolsets; changes-requested findings remain relayable, the default behavior is unchanged, the audit entry records reason subagent_recorder_required, and WORKFLOW_GUARD_REQUIRE_SUBAGENT_REVIEW=0 disables the requirement even when project config enables it. guard_status echoes the effective requirement in its projectConfig summary.
aae4ca6: guard_status now includes a read-only gitHygiene snapshot: local branches already merged into the mainline (capped list plus true count, excluding the current and unmerged branches), prunable worktree admin entries (clearable with guard_worktree_cleanup / cleanupGitWorktree or git worktree prune), and the current branch's base-behind distance when a remote base exists. Hygiene itself stays manual — the guard reports accumulated git state, it never prunes, fetches, or deletes branches on its own.
Patch Changes
99dd23f: Workspace-boundary blocks now attribute the unresolvable-variable cause: a mutation, redirect, or patch target containing an unresolvable $VARIABLE reference (for example mkdir -p $D/sub or > $CACHE/out.txt) is indeterminate without executing shell semantics and is treated as outside the workspace, but the block message now says so — "contains an unresolvable variable reference … use a literal workspace-relative path" — instead of the generic escape message, across the edit/write path, apply_patch, and shell mutations. Deterministically expandable forms (~, $HOME) and literal paths inside the workspace keep the generic messages; the fail-closed behavior itself is unchanged. docs/policies.md and docs/troubleshooting.md document the rule and the remedy.
e1065bf: guard_review_rubric diff invocations keep a trailing -- after the revision so a configured base ref can never parse as a pathspec (defense-in-depth on top of the revision-before--- fix), and the default-bases fallback loop (origin/main → origin/master → main → master, then HEAD~1) is directly asserted: a repository with no origin remote embeds the real branch diff from the local main branch.
634d748: Scratch-directory cause attribution: /tmp/opencode is the OpenCode environment's designated agent scratch space, but the workspace boundary is workspace-absolute and does not cover it, so writes there stay blocked. Block messages for targets under it (edit/write, apply_patch, and shell mutations) now name the directory and point to the remedy — write scratch as an untracked file inside the workspace instead of outside it — instead of the generic escape message that read as a guard bug (six of the device audit's boundary blocks were this class). The enforcement decision is unchanged.
2cb9fd8: Stale file-claim takeover for a missed idle release: a direct-edit claim created before its owner's last recorded idle timestamp cannot belong to an in-flight tool call, so when another session hits that conflict every provably stale claim of that owner is released (each re-checked after the owner lookup) with an audited stale_claim_takeover entry instead of blocking every session until the owner is deleted or the service restarts. Owners still mid-session (no idle timestamp), clients without a session lookup, and failed lookups keep the block (fail-closed), and guard_why remains read-only. Claims now record their creation timestamp, and a same-session re-claim refreshes the stored claim so its release matches the in-flight call.