Symptom
Re-running the same request in an existing (already-answered) execution does nothing — the agent replays its previous answer and never re-attempts tools. Field case: a delegated MCP (Atlassian) call that failed "not connected", then the user connected the account and re-sent the identical request — the execution stayed broken (the tool was never called again), while a fresh execution worked.
Proof (field log, next-SNAPSHOT-50cd5e3)
session recovered task_id=tsk_6a6caa5e… messages=7
llm_call llm_call_count=1 # prompt ENDS with the assistant's prior "Jira not connected" reply — NO new user turn appended
# no mcp_tool_call, no tool_exec — the tool was never re-attempted
The re-send's user message never reached the model, so it continued from its own last assistant turn and repeated the refusal.
Root cause — forge-core/runtime/loop.go
On a recovered session, the incoming user message is appended unless it dedups against the last user message anywhere in history:
if recovered {
msgs := mem.Messages()
lastUserIdx := -1
for j := len(msgs) - 1; j >= 0; j-- {
if msgs[j].Role == llm.RoleUser { lastUserIdx = j; break } // last user ANYWHERE
}
if lastUserIdx < 0 || msgs[lastUserIdx].Content != newMsg.Content {
mem.Append(newMsg)
}
}
The comment scopes the intent correctly — "skip if the recovered session already ends with an identical user message (avoids duplicates when users retry after a premature loop exit)" — but the implementation compares against the last user turn regardless of position. When a prior identical request was already answered (the session ends in an assistant message), a verbatim re-run matches that earlier user turn and is dropped. The model is then re-run on the poisoned transcript with no new input.
A premature-exit duplicate — the case this guard is for — leaves the session ending in a user message (persisted, never answered). That's the only shape that should dedup.
Fix
Dedup only a trailing identical user message:
if n == 0 || msgs[n-1].Role != llm.RoleUser || msgs[n-1].Content != newMsg.Content {
mem.Append(newMsg)
}
If the recovered session ends in an assistant turn, an identical incoming user message is a legitimate re-run and must be appended so the loop re-enters and re-attempts tools.
Test
- Recovered session ending in an assistant turn + an identical incoming user message → the message IS appended (loop re-enters). Regression for this bug.
- Recovered session ending in a user turn identical to the incoming message (premature-exit dup) → still skipped (no regression).
- Recovered session ending in a user turn with DIFFERENT content → appended.
Related
Symptom
Re-running the same request in an existing (already-answered) execution does nothing — the agent replays its previous answer and never re-attempts tools. Field case: a delegated MCP (Atlassian) call that failed "not connected", then the user connected the account and re-sent the identical request — the execution stayed broken (the tool was never called again), while a fresh execution worked.
Proof (field log,
next-SNAPSHOT-50cd5e3)The re-send's user message never reached the model, so it continued from its own last assistant turn and repeated the refusal.
Root cause —
forge-core/runtime/loop.goOn a recovered session, the incoming user message is appended unless it dedups against the last user message anywhere in history:
The comment scopes the intent correctly — "skip if the recovered session already ends with an identical user message (avoids duplicates when users retry after a premature loop exit)" — but the implementation compares against the last user turn regardless of position. When a prior identical request was already answered (the session ends in an assistant message), a verbatim re-run matches that earlier user turn and is dropped. The model is then re-run on the poisoned transcript with no new input.
A premature-exit duplicate — the case this guard is for — leaves the session ending in a user message (persisted, never answered). That's the only shape that should dedup.
Fix
Dedup only a trailing identical user message:
If the recovered session ends in an assistant turn, an identical incoming user message is a legitimate re-run and must be appended so the loop re-enters and re-attempts tools.
Test
Related