Skip to content

Add protected context compaction for backports - #834

Merged
opohorel merged 1 commit into
packit:mainfrom
opohorel:backport_context_management
Sep 22, 2026
Merged

opohorel merged 1 commit into
packit:mainfrom
opohorel:backport_context_management

Conversation

@opohorel

Copy link
Copy Markdown
Collaborator

Context compaction is opt-in and disabled by default. It is only enabled when BACKPORT_CONTEXT_MANAGEMENT=true is explicitly configured. I was trying to find optimal backport where the compaction would happen, but our agents are pretty good with token consumption already. If merged we can turn this feature on only when we are sure that it works as expected.

@qodo-for-packit

Copy link
Copy Markdown

PR Summary by Qodo

Add opt-in protected context compaction for backports

✨ Enhancement 🧪 Tests 📝 Documentation ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Enables opt-in context compaction for backport and incremental repair agents.
• Preserves task instructions and maintainer-rule exchanges during validated, atomic memory
 compaction.
• Documents configuration and adds comprehensive factory, runner, and protection tests.
Diagram

graph TD
  ENV["Feature flag"] --> FACTORY["Backport factory"] --> AGENT["Reasoning agent"] --> TOOL["Manage context"] --> RUNNER["Agent runner"] --> CHECK{"Valid history?"}
  CHECK -->|Valid| MEMORY["Compacted memory"]
  CHECK -->|Invalid| KEEP["Original memory"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Reuse generic compaction unchanged
  • ➕ Requires substantially less new runner and validation logic.
  • ➕ Keeps all agents on one compaction implementation.
  • ➖ Cannot guarantee verbatim retention of maintainer-rule exchanges.
  • ➖ Existing behavior may reorder protected task boundaries.
  • ➖ Provides weaker rollback guarantees for malformed histories.
2. Automatic token-threshold compaction
  • ➕ Reduces reliance on the model choosing when to compact.
  • ➕ Can enforce predictable context-size limits.
  • ➖ Requires reliable token accounting across providers.
  • ➖ May compact during sensitive workflow transitions.
  • ➖ Still needs protected-history validation and durable summarization.

Recommendation: Use the PR's opt-in, model-initiated protected compaction for the initial rollout. It safely extends the existing mechanism while preserving authoritative rule evidence and failing transactionally; automatic thresholding can be considered later after observing summary quality and token savings.

Files changed (11) +1320 / -10

Enhancement (5) +394 / -10
backport_agent.pyEnable protected compaction in backport agents +10/-2

Enable protected compaction in backport agents

• Reads the opt-in environment flag, appends context-management instructions, and configures rule-fetch tools as protected. It also reuses the selected model when constructing the agent.

ymir/agents/backport_agent.py

_context_management.j2Define safe backport compaction guidance +52/-0

Define safe backport compaction guidance

• Adds instructions for scheduling compaction, constructing cumulative summaries, preserving workflow state, respecting retained rules, and continuing incremental repairs.

ymir/agents/prompts/backport/_context_management.j2

_runner.pyApply compaction with protected-tool policy +14/-3

Apply compaction with protected-tool policy

• Accepts protected tool names, adjusts the manage-context description for sequential models, and passes the policy into compaction after constrained and unconstrained tool turns.

ymir/agents/reasoning_agent/_runner.py

agent.pyPropagate protected context configuration +4/-0

Propagate protected context configuration

• Stores protected tool names as an immutable tuple and forwards them into runner creation and cloned agents.

ymir/agents/reasoning_agent/agent.py

context_management.pyAdd transactional protected-history compaction +314/-5

Add transactional protected-history compaction

• Introduces ordered history blocks, exact tool-call/result validation, protected exchange pinning, provider-safe continuation checks, and atomic candidate-memory replacement. Invalid requests or histories leave original memory untouched while legacy behavior remains available without a protection policy.

ymir/agents/reasoning_agent/context_management.py

Tests (4) +912 / -0
test_backport_context_management.pyTest backport context-management configuration +130/-0

Test backport context-management configuration

• Covers feature gating, prompt composition, model capabilities, tool selection, sequential and parallel descriptions, and clone policy preservation.

ymir/agents/tests/unit/test_backport_context_management.py

test_context_management_protection.pyTest deterministic protected compaction +532/-0

Test deterministic protected compaction

• Verifies verbatim rule retention, chronology, mixed tool batches, repeated compactions, malformed-history rollback, atomic memory replacement, and legacy fallback behavior.

ymir/agents/tests/unit/test_context_management_protection.py

test_context_management_runner.pyTest runner-level compaction safety +242/-0

Test runner-level compaction safety

• Ensures standalone compaction produces provider-serializable continuation history and parallel compaction waits for companion tools before modifying memory.

ymir/agents/tests/unit/test_context_management_runner.py

test_jinja2_templates.pyValidate the context-management prompt partial +8/-0

Validate the context-management prompt partial

• Checks that the new template contains cumulative-summary, failure-retention, rule-preservation, dependency-ordering, and repair-history guidance.

ymir/agents/tests/unit/test_jinja2_templates.py

Documentation (1) +11 / -0
README-agents.mdDocument opt-in backport context management +11/-0

Document opt-in backport context management

• Documents the feature flag, protected instructions and rule exchanges, sequential versus parallel scheduling, and incremental-repair history behavior.

README-agents.md

Other (1) +3 / -0
beeai-agent.envExpose the context-management feature flag +3/-0

Expose the context-management feature flag

• Adds a commented BACKPORT_CONTEXT_MANAGEMENT example and explains that compaction applies to backports and build repairs.

templates/beeai-agent.env

@qodo-for-packit

qodo-for-packit Bot commented Sep 17, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Earlier user instructions are discarded ✓ Resolved 🐞 Bug ≡ Correctness ⭐ New
Description
_apply_protected_context_compaction classifies every block without the protected-task marker as
removable and drops it unless it is recent or contains a protected tool call.
ReasoningAgent._process_input accepts a list of messages but marks only its final UserMessage as
protected, so an earlier user instruction in that supported input form is removed on a later
compaction and is not guaranteed to appear in the model-authored summary.
Code

ymir/agents/reasoning_agent/context_management.py[R476-477]

+        selected_indexes = task_indexes | pinned_indexes | recent_indexes
+        removed_indexes = set(exchange_indexes) - selected_indexes
Relevance

●●● Strong

List inputs can lose earlier user instructions because only the final message is protected during
compaction.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The agent input path explicitly accepts a list but applies YMIR_PROTECTED_META_KEY only to the
final user message. The new compactor converts each unmarked message into a normal removable block,
selects only task, pinned, and recent blocks, and skips all others, so an earlier input instruction
can be silently discarded.

ymir/agents/reasoning_agent/agent.py[144-168]
ymir/agents/reasoning_agent/context_management.py[301-327]
ymir/agents/reasoning_agent/context_management.py[468-506]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

Issue description
Protected compaction removes earlier `UserMessage` inputs when callers provide the supported multi-message input form, because only the last user message receives the protected-task marker.

Fix Focus Areas
- ymir/agents/reasoning_agent/context_management.py[468-477]
- ymir/agents/reasoning_agent/context_management.py[493-506]

Recommended Fix
Treat ordinary user input messages as protected during protected compaction, including user messages that preceded the final rendered task message. Exempt only internally generated context-summary markers so later compactions can still replace the previous summary; add a regression test with two user messages followed by tool exchanges and verify both instructions remain after compaction.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Untrusted instructions persist as user input ✓ Resolved 🐞 Bug ⛨ Security
Description
_apply_protected_context_compaction places the model-supplied durable_summary into a
UserMessage, although that summary can incorporate untrusted repository, pull-request, or tool
output. When context management is enabled for a backport, injected instructions can survive
compaction with user-message authority and influence later repository-writing or shell-tool calls.
Code

ymir/agents/reasoning_agent/context_management.py[R413-417]

+                    UserMessage(
+                        "[Context summary — earlier tool traffic was compacted]\n\n"
+                        f"{pending.durable_summary}",
+                        meta={YMIR_CONTEXT_SUMMARY_META_KEY: True},
+                    )
Relevance

●● Moderate

Security concern is plausible, but repository precedent does not directly establish rejecting model
summaries as user messages.

PR-#757
PR-#775

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The PR activates context management for backport agents, whose tool set includes shell and
repository mutation tools. The compaction path directly constructs a user-role message from the
model-controlled summary after tool output has been added to memory, so untrusted tool content
copied into that summary is replayed at higher conversational authority on the next turn.

ymir/agents/backport_agent.py[228-229]
ymir/agents/backport_agent.py[231-258]
ymir/agents/backport_agent.py[275-298]
ymir/agents/reasoning_agent/_runner.py[614-620]
ymir/agents/reasoning_agent/context_management.py[90-97]
ymir/agents/reasoning_agent/context_management.py[411-419]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

Issue description

Protected compaction promotes the model-provided `durable_summary` into a `UserMessage`. Repository and remote-tool output is untrusted and can be incorporated into that summary, so it must not be replayed as user-authority instructions on later model turns.

Fix Focus Areas
- ymir/agents/reasoning_agent/context_management.py[411-419]
- ymir/agents/reasoning_agent/_runner.py[614-620]
- ymir/agents/backport_agent.py[275-298]

Recommended Fix

Represent compacted summaries as explicitly untrusted context rather than a user message. Update request serialization so summaries retain factual continuity while being delivered with tool/document provenance and an unambiguous instruction-data boundary; ensure provider-valid message sequencing remains intact.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Large sibling outputs never compact ✗ Dismissed 🐞 Bug ➹ Performance ⭐ New
Description
_apply_protected_context_compaction pins an entire exchange when any call uses a protected rule
tool, and strip_manage_context_from_exchange removes only compaction traffic while retaining every
unrelated sibling call and result. When a rule fetch is parallelized with a large file, log, or
shell result, that output survives every later compaction and can continue consuming the context
window indefinitely.
Code

ymir/agents/reasoning_agent/context_management.py[R470-474]

+        pinned_indexes = {
+            index
+            for index, block in enumerate(original_blocks)
+            if any(call.tool_name in protected_tool_names for call in block.tool_calls)
+        }
Relevance

●●● Strong

Protected mixed exchanges retain unrelated large outputs indefinitely, creating a concrete
context-growth performance issue.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Protected indexes are selected when any call in a block has a protected name, so the whole block is
retained. Retained blocks are then cleaned only through strip_manage_context_from_exchange, whose
implementation removes manage_context pairs but leaves other sibling calls and results; the new
mixed-batch test explicitly confirms that a view sibling and its result remain alongside a
protected rule fetch.

ymir/agents/reasoning_agent/context_management.py[470-477]
ymir/agents/reasoning_agent/context_management.py[496-506]
ymir/agents/reasoning_agent/context_management.py[233-280]
ymir/agents/tests/unit/test_context_management_protection.py[248-290]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Protected rule-tool pinning retains the entire parallel exchange, including unrelated sibling calls and potentially large results, across every future compaction.

## Fix Focus Areas
- ymir/agents/reasoning_agent/context_management.py[470-474]
- ymir/agents/reasoning_agent/context_management.py[496-503]

## Recommended Fix
When retaining a protected-tool exchange outside the recent window, preserve the protected calls and their exactly paired results but remove nonprotected sibling calls and results. Keep all siblings only while the exchange is selected as recent, and validate the rewritten protected subset using the existing pairing and snapshot safeguards.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Reasoning history blocks compaction ✓ Resolved 🐞 Bug ☼ Reliability
Description
_has_invalid_assistant_continuation rejects every retained assistant message without tool calls or
text, including legitimate reasoning-only output previously stored by the runner. When
keep_recent_exchanges retains such a retry response, protected compaction is skipped and the
original uncompressed history remains active.
Code

ymir/agents/reasoning_agent/context_management.py[R361-363]

+    return (bool(messages) and isinstance(messages[-1], AssistantMessage)) or any(
+        isinstance(message, AssistantMessage) and not message.get_tool_calls() and not message.get_texts()
+        for message in messages
Relevance

●●● Strong

Deterministic validation bug prevents compaction for legitimate reasoning-only assistant history.

PR-#726

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The unconstrained runner stores response.output when a forced final-answer call is missing, so a
reasoning-only assistant response can enter memory. The new validation rejects any retained
assistant having neither tool calls nor text, while existing tests explicitly construct assistant
messages whose only content is MessageReasoningContent; retaining one through a larger
recent-exchange window therefore makes _apply_protected_context_compaction return false.

ymir/agents/reasoning_agent/_runner.py[627-650]
ymir/agents/reasoning_agent/context_management.py[359-364]
ymir/agents/tests/unit/test_context_management.py[130-140]
ymir/agents/reasoning_agent/context_management.py[443-477]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Protected context compaction rejects legitimate reasoning-only assistant messages retained from earlier model retries, leaving the original history uncompressed.

## Fix Focus Areas
- ymir/agents/reasoning_agent/context_management.py[359-364]
- ymir/agents/reasoning_agent/context_management.py[407-444]
- ymir/agents/reasoning_agent/_runner.py[627-650]

## Recommended Fix
Treat standalone assistant blocks containing only reasoning as removable during protected compaction instead of retaining and rejecting them. Preserve the terminal-assistant safety check, and add a runner-level test where a reasoning-only retry precedes a valid compaction with `keep_recent_exchanges` large enough to include that retry.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Custom memory behavior is discarded ✗ Dismissed 🐞 Bug ☼ Reliability
Description
_apply_protected_context_compaction always creates an UnconstrainedMemory and assigns it to
state.memory instead of preserving the supplied memory implementation. Any caller that enabled the
new protected-tool policy with a custom BaseMemory loses its storage, limit, persistence, or
instrumentation behavior after the first successful compaction.
Code

ymir/agents/reasoning_agent/context_management.py[R459-461]

+        candidate_memory = UnconstrainedMemory()
+        expected_candidate = [_message_snapshot(message) for message in new_messages]
+        await candidate_memory.add_many(new_messages)
Relevance

●●● Strong

Concrete reliability regression discards caller-provided memory implementations and their
persistence or instrumentation behavior.

PR-#726

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The generic agent constructor and run-state type both allow any BaseMemory, but the newly added
protected-compaction branch constructs and installs one concrete replacement class. This differs
from the legacy path, which resets and repopulates the existing memory object.

ymir/agents/reasoning_agent/context_management.py[459-480]
ymir/agents/reasoning_agent/context_management.py[509-533]
ymir/agents/reasoning_agent/agent.py[47-80]
ymir/agents/reasoning_agent/types.py[55-63]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

Issue description

Protected compaction replaces every configured memory object with `UnconstrainedMemory`. `ReasoningAgent` accepts arbitrary `BaseMemory` implementations, so compaction must preserve the configured implementation and its behavior.

Fix Focus Areas
- ymir/agents/reasoning_agent/context_management.py[459-480]
- ymir/agents/reasoning_agent/agent.py[47-80]
- ymir/agents/reasoning_agent/types.py[55-63]

Recommended Fix

Create the candidate by cloning `original_memory`, reset that clone, and populate it with the validated compacted messages before atomically assigning it to state. Keep the existing transactional failure behavior if cloning, reset, or population fails.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View medium (1)
6. Compaction failures hide their cause ✓ Resolved 🐞 Bug ◔ Observability
Description
_apply_protected_context_compaction catches every validation or memory-population exception but
logs only its class name without the message or traceback. All distinct validation failures collapse
to messages such as ValueError, so operators cannot determine which invariant rejected compaction
or locate unexpected implementation failures.
Code

ymir/agents/reasoning_agent/context_management.py[R473-476]

+        logger.warning(
+            "Skipped protected context compaction: candidate validation failed (%s)",
+            type(error).__name__,
+        )
Relevance

●● Moderate

More diagnostic logging is reasonable, but broad exception swallowing and message detail have mixed
historical treatment.

PR-#775
PR-#727

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The protected path raises the same ValueError class for blank summaries, malformed exchanges,
changed retained blocks, reordered history, invalid continuations, changed tasks, and
candidate-memory mutations. The single catch handler emits only type(error).__name__, and it also
swallows unexpected exceptions from deep-copying and candidate_memory.add_many, leaving no
traceback or specific rejection reason.

ymir/agents/reasoning_agent/context_management.py[375-391]
ymir/agents/reasoning_agent/context_management.py[428-469]
ymir/agents/reasoning_agent/context_management.py[470-477]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Protected compaction catches all candidate-building failures but records only the exception type, making expected validation rejections and unexpected implementation errors indistinguishable.

## Fix Focus Areas
- ymir/agents/reasoning_agent/context_management.py[375-477]

## Recommended Fix
Log the exception message and traceback when candidate construction fails, for example with `logger.exception`, while retaining the transactional `False` return behavior. Keep validation error messages free of task, rule, or tool-result content so diagnostics do not expose sensitive context.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
✅ Compliance rules (platform): 8 rules
Review mode: 🧠 Deep: This push adds substantial, security-sensitive context-compaction logic across multiple runtime paths, including protected tool preservation, provider-specific message transformation, concurrency, rollback, and opt-in agent integration.

Grey Divider

Tip of the day
💡 Did you know, you can route each action level your way: inline, summary, both, or drop

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Previous reviews

Review updated until commit f2e12fd

Results up to commit 2af51b5 🧠 Deep


🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Action required
1. Untrusted instructions persist as user input ✓ Resolved 🐞 Bug ⛨ Security
Description
_apply_protected_context_compaction places the model-supplied durable_summary into a
UserMessage, although that summary can incorporate untrusted repository, pull-request, or tool
output. When context management is enabled for a backport, injected instructions can survive
compaction with user-message authority and influence later repository-writing or shell-tool calls.
Code

ymir/agents/reasoning_agent/context_management.py[R413-417]

+                    UserMessage(
+                        "[Context summary — earlier tool traffic was compacted]\n\n"
+                        f"{pending.durable_summary}",
+                        meta={YMIR_CONTEXT_SUMMARY_META_KEY: True},
+                    )
Relevance

●● Moderate

Security concern is plausible, but repository precedent does not directly establish rejecting model
summaries as user messages.

PR-#757
PR-#775

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The PR activates context management for backport agents, whose tool set includes shell and
repository mutation tools. The compaction path directly constructs a user-role message from the
model-controlled summary after tool output has been added to memory, so untrusted tool content
copied into that summary is replayed at higher conversational authority on the next turn.

ymir/agents/backport_agent.py[228-229]
ymir/agents/backport_agent.py[231-258]
ymir/agents/backport_agent.py[275-298]
ymir/agents/reasoning_agent/_runner.py[614-620]
ymir/agents/reasoning_agent/context_management.py[90-97]
ymir/agents/reasoning_agent/context_management.py[411-419]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

Issue description

Protected compaction promotes the model-provided `durable_summary` into a `UserMessage`. Repository and remote-tool output is untrusted and can be incorporated into that summary, so it must not be replayed as user-authority instructions on later model turns.

Fix Focus Areas
- ymir/agents/reasoning_agent/context_management.py[411-419]
- ymir/agents/reasoning_agent/_runner.py[614-620]
- ymir/agents/backport_agent.py[275-298]

Recommended Fix

Represent compacted summaries as explicitly untrusted context rather than a user message. Update request serialization so summaries retain factual continuity while being delivered with tool/document provenance and an unambiguous instruction-data boundary; ensure provider-valid message sequencing remains intact.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended
2. Custom memory behavior is discarded ✗ Dismissed 🐞 Bug ☼ Reliability
Description
_apply_protected_context_compaction always creates an UnconstrainedMemory and assigns it to
state.memory instead of preserving the supplied memory implementation. Any caller that enabled the
new protected-tool policy with a custom BaseMemory loses its storage, limit, persistence, or
instrumentation behavior after the first successful compaction.
Code

ymir/agents/reasoning_agent/context_management.py[R459-461]

+        candidate_memory = UnconstrainedMemory()
+        expected_candidate = [_message_snapshot(message) for message in new_messages]
+        await candidate_memory.add_many(new_messages)
Relevance

●●● Strong

Concrete reliability regression discards caller-provided memory implementations and their
persistence or instrumentation behavior.

PR-#726

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The generic agent constructor and run-state type both allow any BaseMemory, but the newly added
protected-compaction branch constructs and installs one concrete replacement class. This differs
from the legacy path, which resets and repopulates the existing memory object.

ymir/agents/reasoning_agent/context_management.py[459-480]
ymir/agents/reasoning_agent/context_management.py[509-533]
ymir/agents/reasoning_agent/agent.py[47-80]
ymir/agents/reasoning_agent/types.py[55-63]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

Issue description

Protected compaction replaces every configured memory object with `UnconstrainedMemory`. `ReasoningAgent` accepts arbitrary `BaseMemory` implementations, so compaction must preserve the configured implementation and its behavior.

Fix Focus Areas
- ymir/agents/reasoning_agent/context_management.py[459-480]
- ymir/agents/reasoning_agent/agent.py[47-80]
- ymir/agents/reasoning_agent/types.py[55-63]

Recommended Fix

Create the candidate by cloning `original_memory`, reset that clone, and populate it with the validated compacted messages before atomically assigning it to state. Keep the existing transactional failure behavior if cloning, reset, or population fails.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Reasoning history blocks compaction ✓ Resolved 🐞 Bug ☼ Reliability
Description
_has_invalid_assistant_continuation rejects every retained assistant message without tool calls or
text, including legitimate reasoning-only output previously stored by the runner. When
keep_recent_exchanges retains such a retry response, protected compaction is skipped and the
original uncompressed history remains active.
Code

ymir/agents/reasoning_agent/context_management.py[R361-363]

+    return (bool(messages) and isinstance(messages[-1], AssistantMessage)) or any(
+        isinstance(message, AssistantMessage) and not message.get_tool_calls() and not message.get_texts()
+        for message in messages
Relevance

●●● Strong

Deterministic validation bug prevents compaction for legitimate reasoning-only assistant history.

PR-#726

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The unconstrained runner stores response.output when a forced final-answer call is missing, so a
reasoning-only assistant response can enter memory. The new validation rejects any retained
assistant having neither tool calls nor text, while existing tests explicitly construct assistant
messages whose only content is MessageReasoningContent; retaining one through a larger
recent-exchange window therefore makes _apply_protected_context_compaction return false.

ymir/agents/reasoning_agent/_runner.py[627-650]
ymir/agents/reasoning_agent/context_management.py[359-364]
ymir/agents/tests/unit/test_context_management.py[130-140]
ymir/agents/reasoning_agent/context_management.py[443-477]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Protected context compaction rejects legitimate reasoning-only assistant messages retained from earlier model retries, leaving the original history uncompressed.

## Fix Focus Areas
- ymir/agents/reasoning_agent/context_management.py[359-364]
- ymir/agents/reasoning_agent/context_management.py[407-444]
- ymir/agents/reasoning_agent/_runner.py[627-650]

## Recommended Fix
Treat standalone assistant blocks containing only reasoning as removable during protected compaction instead of retaining and rejecting them. Preserve the terminal-assistant safety check, and add a runner-level test where a reasoning-only retry precedes a valid compaction with `keep_recent_exchanges` large enough to include that retry.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Compaction failures hide their cause ✓ Resolved 🐞 Bug ◔ Observability
Description
_apply_protected_context_compaction catches every validation or memory-population exception but
logs only its class name without the message or traceback. All distinct validation failures collapse
to messages such as ValueError, so operators cannot determine which invariant rejected compaction
or locate unexpected implementation failures.
Code

ymir/agents/reasoning_agent/context_management.py[R473-476]

+        logger.warning(
+            "Skipped protected context compaction: candidate validation failed (%s)",
+            type(error).__name__,
+        )
Relevance

●● Moderate

More diagnostic logging is reasonable, but broad exception swallowing and message detail have mixed
historical treatment.

PR-#775
PR-#727

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The protected path raises the same ValueError class for blank summaries, malformed exchanges,
changed retained blocks, reordered history, invalid continuations, changed tasks, and
candidate-memory mutations. The single catch handler emits only type(error).__name__, and it also
swallows unexpected exceptions from deep-copying and candidate_memory.add_many, leaving no
traceback or specific rejection reason.

ymir/agents/reasoning_agent/context_management.py[375-391]
ymir/agents/reasoning_agent/context_management.py[428-469]
ymir/agents/reasoning_agent/context_management.py[470-477]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Protected compaction catches all candidate-building failures but records only the exception type, making expected validation rejections and unexpected implementation errors indistinguishable.

## Fix Focus Areas
- ymir/agents/reasoning_agent/context_management.py[375-477]

## Recommended Fix
Log the exception message and traceback when candidate construction fails, for example with `logger.exception`, while retaining the transactional `False` return behavior. Keep validation error messages free of task, rule, or tool-result content so diagnostics do not expose sensitive context.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread ymir/agents/reasoning_agent/context_management.py
Comment thread ymir/agents/reasoning_agent/context_management.py
Comment thread ymir/agents/reasoning_agent/context_management.py
Comment thread ymir/agents/reasoning_agent/context_management.py
@opohorel
opohorel force-pushed the backport_context_management branch from 2af51b5 to 8bd2e1c Compare September 17, 2026 14:32
@opohorel

Copy link
Copy Markdown
Collaborator Author

/agentic_review

Comment thread ymir/agents/reasoning_agent/context_management.py
Comment thread ymir/agents/reasoning_agent/context_management.py
@qodo-for-packit

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 8bd2e1c

@opohorel
opohorel force-pushed the backport_context_management branch from 8bd2e1c to dd7dc7c Compare September 18, 2026 07:30

@lbarcziova lbarcziova left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Comment thread README-agents.md
Comment on lines +84 to +85
Backport context management is initially disabled. Set
`BACKPORT_CONTEXT_MANAGEMENT=true` to let the model compact older investigation

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

any easy way to detect an issue suitable for testing this, when running in prod?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pretty much anything that is not a simple backport. sadly as the LLMs got better, it is harder to get them to grow the context over 50k or so. they don't get confused like they used to

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and could we detect this even programatically to flag this?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure. I'll probably just look through the trace server occasionally and try to see which BackportWorkflow had big cost and run it locally. also the next thing would be to find components which have rules, to see if they were kept in place after compaction

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sounds good

@opohorel
opohorel force-pushed the backport_context_management branch from dd7dc7c to f2e12fd Compare September 22, 2026 07:13
@opohorel
opohorel merged commit 9b5aad8 into packit:main Sep 22, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants