Skip to content

v1.15.0

Choose a tag to compare

@github-actions github-actions released this 22 Sep 07:40
· 79 commits to main since this release
d3bfb09

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.