Summary
CoSTEERRAGStrategyV2 keeps a persistent current_generated_trace_count cursor, but each CoSTEER.develop() run can pass a fresh evolving_trace object. When the stale cursor happens to equal the length of a new trace, generate_knowledge() returns early and skips ingesting the latest repair feedback.
Verified on microsoft/RD-Agent main at:
706e907727d3f0721f6707675722155615294195
Relevant upstream code:
rdagent/components/coder/CoSTEER/knowledge_management.py
rdagent/components/coder/CoSTEER/evolving_strategy.py
Why this matters
In multi-task CoSTEER repair, this can cause already-successful candidates from the previous repair step to be scheduled again because their successful implementations were not written into success_task_to_knowledge_dict.
A typical failure mode:
- An evolve step implements several tasks.
- Most tasks pass, one task fails.
CoSTEERRAGStrategyV2.current_generated_trace_count is stale from a previous trace.
- A new trace with the same length is passed to
generate_knowledge().
generate_knowledge() returns None before recording the passing tasks.
- The next repair round does not know which tasks already passed and may reimplement the whole group instead of only repairing the failed task.
This wastes LLM calls and can regress previously successful code.
Root cause
current_generated_trace_count is scoped to the RAG strategy instance, but it is interpreted as a cursor for whichever evolving_trace object is currently passed in.
Current logic:
if len(evolving_trace) == self.current_generated_trace_count:
return None
This is only safe if the cursor is tied to the same trace identity. It is unsafe when a fresh trace has the same length as a previously processed trace.
Suggested fix
Bind the cursor to the identity of the trace object and reset it when a new trace is observed. Also reset if the cursor is larger than the current trace length, which can happen after trace truncation or fresh trace reuse.
Patch sketch:
class CoSTEERRAGStrategyV2(CoSTEERRAGStrategy):
def __init__(self, settings: CoSTEERSettings, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.current_generated_trace_count = 0
+ self._generated_trace_identity: int | None = None
self.settings = settings
def generate_knowledge(
self,
evolving_trace: list[EvoStep],
*,
return_knowledge: bool = False,
) -> Knowledge | None:
+ trace_identity = id(evolving_trace)
+ if (
+ getattr(self, "_generated_trace_identity", None) != trace_identity
+ or self.current_generated_trace_count > len(evolving_trace)
+ ):
+ self._generated_trace_identity = trace_identity
+ self.current_generated_trace_count = 0
+
if len(evolving_trace) == self.current_generated_trace_count:
return None
Optional additional hardening: in MultiProcessEvolvingStrategy.evolve_iter(), if last_feedback[index].final_decision is True and evo.sub_workspace_list[index] already exists, keep that workspace and do not schedule the task again. That protects against future knowledge-ingestion gaps.
Suggested regression tests
CoSTEERRAGStrategyV2.generate_knowledge() should process a fresh trace even when current_generated_trace_count == len(fresh_trace).
- The cursor should reset when
current_generated_trace_count > len(evolving_trace).
- Partial repair should not reschedule tasks whose previous feedback has
final_decision is True and whose workspace already exists.
Notes
This bug is most visible in factor coding workflows with multi-candidate repair, where a round can have several successful candidates and one failed candidate. The expected behavior is to carry forward successful workspaces and only repair the failing candidates.
Summary
CoSTEERRAGStrategyV2keeps a persistentcurrent_generated_trace_countcursor, but eachCoSTEER.develop()run can pass a freshevolving_traceobject. When the stale cursor happens to equal the length of a new trace,generate_knowledge()returns early and skips ingesting the latest repair feedback.Verified on
microsoft/RD-Agentmainat:Relevant upstream code:
rdagent/components/coder/CoSTEER/knowledge_management.pyrdagent/components/coder/CoSTEER/evolving_strategy.pyWhy this matters
In multi-task CoSTEER repair, this can cause already-successful candidates from the previous repair step to be scheduled again because their successful implementations were not written into
success_task_to_knowledge_dict.A typical failure mode:
CoSTEERRAGStrategyV2.current_generated_trace_countis stale from a previous trace.generate_knowledge().generate_knowledge()returnsNonebefore recording the passing tasks.This wastes LLM calls and can regress previously successful code.
Root cause
current_generated_trace_countis scoped to the RAG strategy instance, but it is interpreted as a cursor for whicheverevolving_traceobject is currently passed in.Current logic:
This is only safe if the cursor is tied to the same trace identity. It is unsafe when a fresh trace has the same length as a previously processed trace.
Suggested fix
Bind the cursor to the identity of the trace object and reset it when a new trace is observed. Also reset if the cursor is larger than the current trace length, which can happen after trace truncation or fresh trace reuse.
Patch sketch:
class CoSTEERRAGStrategyV2(CoSTEERRAGStrategy): def __init__(self, settings: CoSTEERSettings, *args, **kwargs) -> None: super().__init__(*args, **kwargs) self.current_generated_trace_count = 0 + self._generated_trace_identity: int | None = None self.settings = settings def generate_knowledge( self, evolving_trace: list[EvoStep], *, return_knowledge: bool = False, ) -> Knowledge | None: + trace_identity = id(evolving_trace) + if ( + getattr(self, "_generated_trace_identity", None) != trace_identity + or self.current_generated_trace_count > len(evolving_trace) + ): + self._generated_trace_identity = trace_identity + self.current_generated_trace_count = 0 + if len(evolving_trace) == self.current_generated_trace_count: return NoneOptional additional hardening: in
MultiProcessEvolvingStrategy.evolve_iter(), iflast_feedback[index].final_decision is Trueandevo.sub_workspace_list[index]already exists, keep that workspace and do not schedule the task again. That protects against future knowledge-ingestion gaps.Suggested regression tests
CoSTEERRAGStrategyV2.generate_knowledge()should process a fresh trace even whencurrent_generated_trace_count == len(fresh_trace).current_generated_trace_count > len(evolving_trace).final_decision is Trueand whose workspace already exists.Notes
This bug is most visible in factor coding workflows with multi-candidate repair, where a round can have several successful candidates and one failed candidate. The expected behavior is to carry forward successful workspaces and only repair the failing candidates.