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
Found during v0.8.3 release review (Chaos Gremlin / prompt-injection red-team persona). Deferred because it needs a design change to the J2 persisted-flag ownership contract and a cross-vendor test matrix (>30 min).
Context
Under OPENCODE_EXPERIMENTAL_PLAN_MODE=true, insertReminders (in packages/opencode/src/session/prompt.ts) stamps ignored: true onto persisted reminder parts at insertion time, computed from the inserting turn's model via isAnthropicLikeModel. The flag is then persisted via Session.updatePart.
Bug
If the user switches models with /model between turns, the persisted ignored flag reflects the old turn's model, not the current one:
GPT-5.x (turn 1) → Claude (turn 2): the turn-1 reminder persisted with ignored: true. On turn 2 it's suppressed from Claude's user role by toModelMessages (gates on !part.ignored) and it's not in turn-2's freshly-built trustedReminderParts, so it isn't hoisted either → Claude silently loses that plan-mode constraint for the turn (degradation).
Both directions are flag-gated (default path unaffected, since default-path reminders are in-memory only and rebuilt each turn) and the GPT→Claude case is degradation-only, so this is not a default-path regression. But the experimental path ships a latent turn-ordering bug.
Suggested fix
Compute the hoist/ignored decision at read time per turn from the current turn's model, rather than baking it into the persisted row at insert time; or re-evaluate persisted reminder parts each turn against the active model. Add tests across the switch matrix (GPT→Claude, Claude→GPT, Gemini→GPT) under the experimental flag.
Found during v0.8.3 release review (Chaos Gremlin / prompt-injection red-team persona). Deferred because it needs a design change to the J2 persisted-flag ownership contract and a cross-vendor test matrix (>30 min).
Context
Under
OPENCODE_EXPERIMENTAL_PLAN_MODE=true,insertReminders(inpackages/opencode/src/session/prompt.ts) stampsignored: trueonto persisted reminder parts at insertion time, computed from the inserting turn's model viaisAnthropicLikeModel. The flag is then persisted viaSession.updatePart.Bug
If the user switches models with
/modelbetween turns, the persistedignoredflag reflects the old turn's model, not the current one:ignored: true. On turn 2 it's suppressed from Claude's user role bytoModelMessages(gates on!part.ignored) and it's not in turn-2's freshly-builttrustedReminderParts, so it isn't hoisted either → Claude silently loses that plan-mode constraint for the turn (degradation).ignoredunset. On the GPT turn it slips back into the user role as a<system-reminder>block → re-triggers the original plan agent stops without tools on altimate-default (prompt-only plans + content-policy refusals) #887 refusal class.Both directions are flag-gated (default path unaffected, since default-path reminders are in-memory only and rebuilt each turn) and the GPT→Claude case is degradation-only, so this is not a default-path regression. But the experimental path ships a latent turn-ordering bug.
Suggested fix
Compute the hoist/
ignoreddecision at read time per turn from the current turn's model, rather than baking it into the persisted row at insert time; or re-evaluate persisted reminder parts each turn against the active model. Add tests across the switch matrix (GPT→Claude, Claude→GPT, Gemini→GPT) under the experimental flag.Scope
packages/opencode/src/session/prompt.ts(insertRemindersexperimental branch),packages/opencode/src/session/message-v2.ts(toModelMessagesignored-gate).