Skip to content

feat(runtime): CoT-pruning + developer-loop aggregation (#6173)#6239

Merged
houko merged 3 commits into
mainfrom
feat/6173-cot-pruning-loop-aggregation
Jun 19, 2026
Merged

feat(runtime): CoT-pruning + developer-loop aggregation (#6173)#6239
houko merged 3 commits into
mainfrom
feat/6173-cot-pruning-loop-aggregation

Conversation

@houko

@houko houko commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements the two context-engineering features proposed in #6173 by @pavver: CoT-pruning and developer-loop aggregation.
Both are opt-in, default-off passes that run inside the existing compaction pass and reshape only the kept (verbatim) messages.

Features

CoT-pruning[compaction] strip_reasoning_after_turns (default 0, disabled).
Strips <think>...</think> spans from text and drops ContentBlock::Thinking blocks from assistant turns older than the configured number of assistant turns.

Developer-loop aggregation[compaction] aggregate_developer_loops (default false) + max_loop_steps_before_aggregate (default 5).
Collapses a run of consecutive developer-tool steps into the first step, a deterministic placeholder, and the last step.

Both knobs are wired through CompactionTomlConfig (global config.toml), the per-agent CompactionOverrides (agent.toml, refs #5476), and the runtime CompactionConfig.

How the design constraints are satisfied

  • Cache boundary. Both passes run only in compactor::compact_messages, never in prepare_llm_messages / the per-turn hot path, so they do not invalidate the provider prompt cache more often than compaction already does.
  • ReasoningEchoPolicy. CoT-pruning is a no-op for Echo models (DeepSeek V4 Flash, [Bug] deepseek-v4-flash lacks multi-turn reasoning_content handling — fails after tool calls #4842) — those providers require the reasoning echoed back on tool_calls turns or the API returns 400. The policy is already a parameter of compact_messages.
  • Storage fidelity. Only the compacted working session is reshaped; the append-only message journal retains the originals. Compaction already rewrites the working session, so this adds no new lossiness to the store of record.
  • Tool-pairing safety. Loop aggregation only ever removes whole tool-use/result pairs, so no tool_use_id is orphaned. Non-loop or short runs are left verbatim.
  • Tool taxonomy, not a name list. Developer tools are identified via the existing classify_toolToolApprovalClass::{Mutating, ExecCapable}, not a hardcoded list (answers the proposal's Q1).
  • Determinism (Enforce deterministic ordering for LLM-bound registries to stabilize prompt cache #3298). The aggregation placeholder uses sorted/de-duped tool names and no timestamp.

Changes

  • crates/librefang-types/src/config/types.rs — three new CompactionTomlConfig fields + defaults.
  • crates/librefang-types/src/agent.rs — matching CompactionOverrides fields + resolve / is_empty.
  • crates/librefang-runtime/src/compactor.rsprune_stale_reasoning, aggregate_developer_loops, helpers, integration into compact_messages, and unit tests.
  • crates/librefang-api/tests/fixtures/kernel_config_schema.golden.json — regenerated for the three new fields.
  • CHANGELOG.md[Unreleased] entries.

Tests

Unit tests in compactor.rs: reasoning stripped past the threshold / kept within it, Echo-policy no-op, zero-disabled no-op, <think> strip (case-insensitive / multiple / unclosed), long-run aggregation with tool-pairing preserved, short-run and disabled no-ops, and read-only tools not aggregated.

Verification

Local cargo/Docker verification was not run on the authoring host: it has no native Rust toolchain, and the sanctioned Docker dev image compiles into a volume that exhausted the constrained internal disk.
Per the repo's no-local-toolchain fallback, CI is the verification gate here — cargo fmt, clippy -D warnings, the kernel_config_schema golden test, and librefang-runtime / librefang-types unit tests all run there.
The golden fixture was hand-updated to match schemars output; if CI's golden test reports a diff, I'll reconcile it.

Closes #6173.

…#6173)

Adds two opt-in, default-off context-engineering passes to the compaction layer, implementing the proposal in #6173 by @pavver.
Both run only at the compaction boundary, never on the per-turn hot path, so they do not invalidate the provider prompt cache more often than compaction already does.

CoT-pruning is gated by `[compaction] strip_reasoning_after_turns` (default 0, disabled).
It strips `<think>...</think>` spans and `Thinking` content blocks from assistant turns older than the configured number of assistant turns.
It is skipped entirely for models whose `ReasoningEchoPolicy` is `Echo` (DeepSeek V4 Flash), which require the reasoning echoed back on tool_calls turns or the API returns 400.

Developer-loop aggregation is gated by `[compaction] aggregate_developer_loops` (default false) with `max_loop_steps_before_aggregate` (default 5).
It collapses long runs of consecutive developer-tool steps into the first step, a deterministic placeholder, and the last step.
Developer tools are identified by the existing `ToolClass` taxonomy (`Mutating` / `ExecCapable`), not a hardcoded name list.
Whole tool-use/result pairs are removed together so no `tool_use_id` is ever orphaned.

Both knobs are wired through `CompactionTomlConfig`, the per-agent `CompactionOverrides` in agent.toml, and the runtime `CompactionConfig`, and the kernel-config golden fixture is updated to match.
The append-only message journal retains the original messages; only the compacted working session is reshaped.
@github-actions github-actions Bot added size/L 250-999 lines changed area/docs Documentation and guides area/runtime Agent loop, LLM drivers, WASM sandbox and removed size/L 250-999 lines changed labels Jun 19, 2026
…by-one

Collapse all multi-line ///doc and // block comments in the new
context-engineering code to a single line each per the project style rule.

Fix `aggregate_developer_loops`: the threshold condition was `steps.len() >
max_steps`, so with the default max_steps=5 a run needed 6 steps to trigger —
contradicting the field description "minimum N steps before triggers". Change
to `>=` so exactly N steps triggers aggregation. Add a boundary test that
covers this case.

@houko houko left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Consecutive Role::User messages in aggregated output (developer_loop_placeholder, compactor.rs)

The placeholder inserted between two elided steps has role: Role::User.
In the aggregated sequence the surrounding layout is:

[tool_use  — Assistant]   ← first step
[tool_result — User]      ← first step result
[placeholder — User]      ← ⚠ consecutive User turn
[tool_use  — Assistant]   ← last step
[tool_result — User]

tool_result and placeholder are both Role::User, producing two consecutive user-role messages.
Most provider APIs (including Anthropic's) accept consecutive user turns in practice — they are coalesced before forwarding — but the Anthropic Messages API rejects two consecutive user messages when they both contain only text blocks if there is no preceding assistant turn (see the "alternating roles" validation in their API docs).
Since compaction is meant to produce a well-formed session, worth checking whether the output of compact_messages is ever fed raw into a provider call without the normal role-coalescing pass.

If the session already runs through a role-merge step before the final API call this is fine; if not, consider making the placeholder Role::Assistant (a brief summary note) or a ContentBlock::ToolResult-style synthetic user block that pairs with a synthetic tool-use so the sequence stays alternating.


Generated by Claude Code

@github-actions github-actions Bot added the size/L 250-999 lines changed label Jun 19, 2026
@houko
houko enabled auto-merge (squash) June 19, 2026 17:22
@houko
houko merged commit 438e6af into main Jun 19, 2026
31 checks passed
@houko
houko deleted the feat/6173-cot-pruning-loop-aggregation branch June 19, 2026 18:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/docs Documentation and guides area/runtime Agent loop, LLM drivers, WASM sandbox size/L 250-999 lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Proposal] Advanced Context Engineering: CoT-Pruning & Developer Loop Aggregation

2 participants