Skip to content

fix: plan-mode reminder ignored-flag is stale on cross-turn model switch (experimental flag) #890

@anandgupta42

Description

@anandgupta42

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).
  • Claude (turn 1) → GPT-5.x (turn 2): the turn-1 reminder persisted with ignored unset. 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/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.

Scope

packages/opencode/src/session/prompt.ts (insertReminders experimental branch), packages/opencode/src/session/message-v2.ts (toModelMessages ignored-gate).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions