Skip to content

Historical forks replay instruction updates that contradict their inherited baseline #61550

Open

Description

@yyytttwww888-beep

Description

A historical fork inherits the parent's latest instruction values, but also replays older instruction updates from the copied history. Those system messages can contradict the new baseline. API context entries explicitly say the old value supersedes the previous one.

OpenCode version

dev at fe046564650854594c4c5aa22270f46d9de6cace (2.2.5).

Steps to reproduce

  1. Run a session with an instruction source whose value changes from A to B to C across requests.
  2. Fork at a historical point that includes the B update but precedes C.
  3. Continue the fork and inspect the next model request.

The initial instructions contain C, while a later system message still applies B. The inherited instruction state already equals C, so no corrective update is generated.

Expected: the fork uses its inherited current baseline without replaying superseded updates. New instruction changes made after the fork should still be delivered.

Reproduced with the real SessionRunner and database using a local test LLM. The existing seeds a fork with the parent's newest instruction values test currently asserts that the stale update remains; changing that expectation exposes the conflict. No live model behavior is claimed.

Regression test and proposed fix.

Activity

  1. opencodejr commented on Nov 4, 2026

    @opencodejr
    Contributor

    Thanks for the clear report. I was able to reproduce this on the latest dev (fe0465646), which is the commit you reported, using the session runner tests with a fake model and a real database.

    The steps (the script below runs them):

    1. Prompt three times while the instruction value changes from A to B ("Changed context") to C ("Latest context").
    2. Fork before the second prompt, so B's update is in the copied history and C's is not.
    3. Prompt the fork and look at the next request.

    That request has C in its system prompt, then replays the copied system update for B, and nothing restores C afterwards. The fork's stored instruction state is already C, so no corrective update is sent. For API context entries the replayed text says the value "supersedes the previous value", so the model ends up treating B as current.

    The existing test seeds a fork with the parent's newest instruction values currently expects this stale update to stay. However, the fork is meant to start from the parent's newest values, so I've passed this to the maintainers as a bug, with your proposed fix linked for reference.

    Reproduction script (repro.sh)
    #!/usr/bin/env bash
    # Reproduces #61550 in the real SessionRunner test harness (fake LLM, real SQLite DB).
    # Run from an opencode checkout (dev): bash repro.sh /path/to/opencode
    set -euo pipefail
    REPO=${1:-/workspace/repo}
    cd "$REPO/packages/core"
    F=test/session-runner.test.ts
    cp $F /tmp/opencode/session-runner.test.ts.bak
    trap 'cp /tmp/opencode/session-runner.test.ts.bak '"$F" EXIT
    python3 - "$F" <<'PY'
    import sys
    p=sys.argv[1]; s=open(p).read()
    anchor='  scenario("keeps nested forks self-contained"'
    new='''  scenario("REPRO 61550 historical fork replays superseded instruction update", function* (s) {
        yield* s.runPrompt("First")
        s.systemBaseline = "Changed context"
        // The "Changed context" update is written just before the "Second" user message,
        // so forking before "Second" keeps update B in the copy but not update C.
        const second = yield* s.runPrompt("Second")
        s.systemBaseline = "Latest context"
        yield* s.runPrompt("Third")
        const forked = yield* s.session.fork({ sessionID, before: second.id })
        yield* s.session.prompt({ sessionID: forked.id, text: "Forked", resume: false })
        yield* s.session.resume(forked.id)
        const req = s.requests.at(-1)!
        console.log("REPRO baseline system:", JSON.stringify(req.system.map((p) => p.text)))
        console.log("REPRO system messages:", JSON.stringify(systemTexts(req)))
        console.log("REPRO full messages:", JSON.stringify(req.messages, null, 1).slice(0, 3000))
        // Expected: the baseline is Latest context and no later message overrides it with the stale value.
        expect(req.system.map((p) => p.text)).toContain("Latest context")
        expect(systemTexts(req).join("\\n")).not.toContain("Changed context")
      })
    
    '''
    assert anchor in s
    open(p,'w').write(s.replace(anchor,new+anchor,1))
    PY
    bun test $F -t "REPRO 61550" 2>&1 | grep -E "REPRO (baseline|system|full)|supersedes|\(pass\)|\(fail\)| pass$| fail$" | grep -v console.log

    Run it: bash repro.sh

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions