Skip to content

fix: resolve the session agent so the plan guard cannot fail open - #67

Merged
willytop8 merged 1 commit into
mainfrom
wr/plan-guard-agent-resolution
Aug 29, 2026
Merged

fix: resolve the session agent so the plan guard cannot fail open#67
willytop8 merged 1 commit into
mainfrom
wr/plan-guard-agent-resolution

Conversation

@willytop8

Copy link
Copy Markdown
Owner

The bug this fixes

A live canary against a real OpenCode 1.18.25 TUI showed the planning-only restriction merged in #64 never firing in practice. With the Plan agent selected, /goal make index.js print goodbye produced:

stopped: False | stopReason: '' | executionContext: None
history: ['set', 'auto-continue', 'warning', 'auto-continue']

The goal was created live, ran the auto-continue loop, and edited the file. Reproduced twice, with and without an agent pin on the command config.

Root cause

The guard read only the cached execution context, populated from chat.message / chat.params / session.updated. OpenCode runs command.execute.before before any of those fire, so the context is empty for the first command in a session — exactly the case a freshly opened Plan-mode session hits. isRestrictedAgent(undefined) returns false, so the restriction failed open precisely where it mattered most.

The unit tests in #64 passed because they drove a chat.message or session.updated first. They encoded my assumption about the host rather than the host's actual behavior.

Fix

Fall back to the session record (Session.agent), which carries the selected agent from the moment the user picks it. Confirmed present in 1.18 and verified to read plan after switching.

  • The cached context is still preferred, so a known agent costs no extra session fetch.
  • A host that cannot report the agent still fails open rather than holding every goal.

Verification

Re-ran the same live canary that found the bug, using the documented install config. With the fix:

before after
turnCount (auto-continues) multiple 0
stopped false true
stopReason '' plan agent active
history set, auto-continue, warning, auto-continue set, paused

Zero auto-continues means no unattended work.

Also verified live in the same run: sessionTitleStatus renders correctly in the real TUI —
⏸ make index.js print goodbye instead of hi · 0/10 · 0s · 25k/200k, with for a running goal.

npm run release:check → exit 0: 414 tests (411 before; +3 new), mutation 68/68, benchmark 100, 0 vulnerabilities.

The new regression test reproduces the live failure exactly — agent visible only on the session record, no execution context — and was confirmed to fail against the pre-fix code.

Honest scope

The README now states precisely what the restriction does and does not prevent. It stops the goal loop (zero auto-continues). It cannot stop a model acting on the single routed command turn, because command.execute.before does not fully intercept command text — the already-documented limitation. In the canary the free test model did edit the file on that one turn despite being told not to; the plugin correctly held the goal and never continued it.

🤖 Generated with Claude Code

A live canary against OpenCode 1.18.25 showed the planning-only
restriction never firing in the real TUI: a goal set while the Plan
agent was selected was created live, auto-continued, and edited files.

Root cause: the guard read only the cached execution context, which the
host populates from `chat.message` / `chat.params` / `session.updated`.
OpenCode runs `command.execute.before` *before* any of those fire, so the
context is empty for the first command in a session — the exact case a
freshly opened Plan-mode session hits. `isRestrictedAgent(undefined)` is
false, so the restriction failed open precisely where it mattered most.

The unit tests passed because they drove a `chat.message` or
`session.updated` first, encoding my assumption rather than the host's
behavior.

Fall back to the session record, which carries the selected agent from
the moment the user picks it. The cached context is still preferred, so a
known agent costs no extra call, and a host that cannot report the agent
still fails open rather than holding every goal.

Verified live on the same canary that found the bug. With the documented
install config, a goal set under Plan now records:

    stopped: true, stopReason: "plan agent active", turnCount: 0
    history: [set, paused]

against the previous `stopped: false` with auto-continues. Zero
auto-continues means no unattended work.

README now states precisely what the restriction does and does not
prevent: it stops the goal loop, but cannot stop a model acting on the
single routed command turn, which is the already-documented
`command.execute.before` interception limitation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@willytop8
willytop8 merged commit 0b94ea2 into main Aug 29, 2026
10 checks passed
@willytop8
willytop8 deleted the wr/plan-guard-agent-resolution branch August 29, 2026 18:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant