nisse-review failed on #159 and #160 because each hit its 40-turn limit. After about 10 turns, every request was over the model's window. Fitting then cut older file reads to 600 characters, so the model read the same files again: driver.py four times. It also moved the cut forward one turn on every call, so llama.cpp reprocessed the request each time, and turns took about 3 minutes.
What changes
Compaction (nisse_core/models/compact.py): before each driver call, once the request passes compact_at (0.6) of the prompt budget, the same model writes notes on its run so far.
The notes replace everything between the brief and the last few turns: at most compact_keep_turns (3), within compact_to (0.25) of the budget.
The notes request reuses the run's own request, system prompt and tools, with one message added, so the server's cache covers everything but the notes.
Code appends a list of the calls the notes replace, so the model knows what it can look at again.
The result is a compaction event (already in L7). It renders where its range was, and a later compaction folds in the earlier one.
If the notes fail (the call fails, the notes are empty, or the request is still too big afterwards), it is logged as context.compaction_failed. The run goes on, fitting cuts instead, and compaction isn't tried again for 3 turns.
Fitting is now the last guard:
it applies compactions before measuring;
it records what it cut (Trim), and logs each cut as context.trimmed, as asked in review of the plan.
Wiring: both agents' drivers and the nisse-dev benchmark. Compaction's model call goes through Interruptible, so a stop breaks it off. The settings are under tuning.context and checked at startup.
Logging:context.compacted records tokens before and after, seconds taken, and the range replaced.
In the tests' 20k-token window, compaction starts at about 61% of the budget and leaves about 26%.
the prompt start staying the same between compactions;
folding;
calls never split from their results;
failed or too-long notes falling back, with the failure logged;
a stop while the notes are written;
compact_at: 0;
views hiding an earlier run's compaction.
test_context.py: nothing is logged for a request that fits; a cut is recorded and logged; a compaction counts for what it replaced.
test_render.py: placement and folding.
Agent config and build wiring tests.
I checked ten of the safety rules by breaking each one in turn; the tests caught all ten.
Unit tier: 948 passed.
Not in this PR
nisse-dev's helper loop (delegation.py), which runs its own loop rather than the driver.
Dropping earlier reads of a file that was read again.
Smaller diffs in the brief.
Raising the turn limit.
After merge
Restart the reviewer and let it review a large PR (this one touches 20 files). Then compare turns used, time per turn, and whether it posts, and look for context.trimmed.
Closes #158.
nisse-review failed on #159 and #160 because each hit its 40-turn limit. After about 10 turns, every request was over the model's window. Fitting then cut older file reads to 600 characters, so the model read the same files again: `driver.py` four times. It also moved the cut forward one turn on every call, so llama.cpp reprocessed the request each time, and turns took about 3 minutes.
## What changes
- **Compaction** (`nisse_core/models/compact.py`): before each driver call, once the request passes `compact_at` (0.6) of the prompt budget, the same model writes notes on its run so far.
- The notes replace everything between the brief and the last few turns: at most `compact_keep_turns` (3), within `compact_to` (0.25) of the budget.
- The notes request reuses the run's own request, system prompt and tools, with one message added, so the server's cache covers everything but the notes.
- Code appends a list of the calls the notes replace, so the model knows what it can look at again.
- The result is a `compaction` event (already in L7). It renders where its range was, and a later compaction folds in the earlier one.
- **If the notes fail** (the call fails, the notes are empty, or the request is still too big afterwards), it is logged as `context.compaction_failed`. The run goes on, fitting cuts instead, and compaction isn't tried again for 3 turns.
- **Fitting is now the last guard:**
- it applies compactions before measuring;
- it records what it cut (`Trim`), and logs each cut as `context.trimmed`, as asked in review of the plan.
- **Wiring:** both agents' drivers and the nisse-dev benchmark. Compaction's model call goes through `Interruptible`, so a stop breaks it off. The settings are under `tuning.context` and checked at startup.
- **Logging:** `context.compacted` records tokens before and after, seconds taken, and the range replaced.
In the tests' 20k-token window, compaction starts at about 61% of the budget and leaves about 26%.
## Tests
- `nisse-core/tests/test_compact.py` (12 tests) covers:
- the brief and recent turns kept word for word;
- the notes request extends the run's own request;
- the prompt start staying the same between compactions;
- folding;
- calls never split from their results;
- failed or too-long notes falling back, with the failure logged;
- a stop while the notes are written;
- `compact_at: 0`;
- views hiding an earlier run's compaction.
- `test_context.py`: nothing is logged for a request that fits; a cut is recorded and logged; a compaction counts for what it replaced.
- `test_render.py`: placement and folding.
- Agent config and build wiring tests.
- I checked ten of the safety rules by breaking each one in turn; the tests caught all ten.
- Unit tier: 948 passed.
## Not in this PR
- nisse-dev's helper loop (`delegation.py`), which runs its own loop rather than the driver.
- Dropping earlier reads of a file that was read again.
- Smaller diffs in the brief.
- Raising the turn limit.
## After merge
Restart the reviewer and let it review a large PR (this one touches 20 files). Then compare turns used, time per turn, and whether it posts, and look for `context.trimmed`.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01J5Utyfzw7gF7UhjRDEtjTk
Before each driver call, over compact_at (0.6) of the prompt budget, the
same model is asked for notes over the very request it was about to get,
plus one message, so the server's cache covers all but the notes. The notes,
with code's list of the calls they replace, become a compaction event that
renders in place of its range, keeping word for word what started the work
and the last few turns. Rare, large steps keep the prompt's start unchanged
between them. A later compaction folds in the earlier; one from an earlier
run's view is hidden.
Failed notes are logged and fitting takes over. Fitting now applies
compactions before it measures, records what it cuts (Trim) and logs each
cut as context.trimmed. Wired into both agents' drivers and the benchmark;
nisse-dev's helper loop is a follow-up.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J5Utyfzw7gF7UhjRDEtjTk
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #158.
nisse-review failed on #159 and #160 because each hit its 40-turn limit. After about 10 turns, every request was over the model's window. Fitting then cut older file reads to 600 characters, so the model read the same files again:
driver.pyfour times. It also moved the cut forward one turn on every call, so llama.cpp reprocessed the request each time, and turns took about 3 minutes.What changes
nisse_core/models/compact.py): before each driver call, once the request passescompact_at(0.6) of the prompt budget, the same model writes notes on its run so far.compact_keep_turns(3), withincompact_to(0.25) of the budget.compactionevent (already in L7). It renders where its range was, and a later compaction folds in the earlier one.context.compaction_failed. The run goes on, fitting cuts instead, and compaction isn't tried again for 3 turns.Trim), and logs each cut ascontext.trimmed, as asked in review of the plan.Interruptible, so a stop breaks it off. The settings are undertuning.contextand checked at startup.context.compactedrecords tokens before and after, seconds taken, and the range replaced.In the tests' 20k-token window, compaction starts at about 61% of the budget and leaves about 26%.
Tests
nisse-core/tests/test_compact.py(12 tests) covers:compact_at: 0;test_context.py: nothing is logged for a request that fits; a cut is recorded and logged; a compaction counts for what it replaced.test_render.py: placement and folding.Not in this PR
delegation.py), which runs its own loop rather than the driver.After merge
Restart the reviewer and let it review a large PR (this one touches 20 files). Then compare turns used, time per turn, and whether it posts, and look for
context.trimmed.🤖 Generated with Claude Code
https://claude.ai/code/session_01J5Utyfzw7gF7UhjRDEtjTk