You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Compaction loses all context on the immediately executing task #1571
This has happened a few times to me over the past days. After compaction happens, context from the previously executing action seems to become completely lost which effectively requires manually stitching things back together. This is not consistently happening and I do not know how to reproduce reliably.
Here's an example which happened right now:
you can see right after compaction that it's trying to figure out what it was doing right before (e.g. about to check screenshots it had just taken)
I'm happy to share any local files which could help debug this.
Affected version
GitHub Copilot CLI 0.0.413-0.
Steps to reproduce the behavior
No response
Expected behavior
Expected behavior would be for copilot to continue with ongoing work after compaction without needing to reinfer everything from repo state.
The loss at compaction is specific, and it is the same thing every time: the summary keeps the goals, the decisions, the plan state, and your instructions, and it drops the cursor. What was executing at that moment, what the last tool call produced, and which action was queued next live only in the raw turns. After the swap the model re-derives all of it from repo state, which is the screenshot behavior in your example.
One prerequisite: the build in the report predates the mechanisms below (preCompact landed in 1.0.5, compaction checkpoints in 1.0.85, current is 1.0.88). On a current build the loop runs today.
Move the cursor into a file. NEXT.md at the repo root, three lines, overwritten in place so it never grows into a second summarization problem:
# NEXT
doing: comparing login-flow screenshots 3 and 4 in .copilot/tmp
next: patch src/auth/login.ts:212, rerun the flow test
verified: flow test still fails before the patch
Add the rule that keeps it current. Copilot CLI loads AGENTS.md as agent instructions (nearest file in the tree wins), so this block is part of every session's instructions:
## Work state- Before starting a multi-step task, write NEXT.md. Update it after each completed step and before anything slow.
- If earlier turns are not visible, read NEXT.md first and continue from its `next` line.
Snapshot the transcript at the compaction boundary, and reorient on resume, with one hook file. Create .github/hooks/lifecycle.json (or the same file under ~/.copilot/hooks/ to apply it everywhere):
{
"version": 1,
"hooks": {
"preCompact": [
{
"type": "command",
"bash": "d=.copilot-transcripts; mkdir -p \"$d\"; cp \"$(jq -r .transcriptPath)\"\"$d/transcript-$(date +%Y%m%d-%H%M%S).jsonl\"",
"powershell": "$d='.copilot-transcripts'; New-Item -ItemType Directory -Force $d | Out-Null; $p=(ConvertFrom-Json ([Console]::In.ReadToEnd())).transcriptPath; Copy-Item $p (\"$d/transcript-{0}.jsonl\" -f (Get-Date -Format 'yyyyMMdd-HHmmss'))",
"cwd": ".",
"timeoutSec": 10
}
],
"sessionStart": [
{
"type": "command",
"bash": "printf '{\"additionalContext\": \"If NEXT.md exists, read it and continue from its next line.\"}'",
"powershell": "Write-Output '{\"additionalContext\": \"If NEXT.md exists, read it and continue from its next line.\"}'",
"cwd": "."
}
]
}
}
The preCompact entry fires right before compaction, manual or automatic, and its payload carries the transcript path (it arrives on stdin), so the full record gets copied while it still exists. The sessionStart entry returns additionalContext, which the CLI injects at the top of a new or resumed session. Command hooks fire on resume, prompt hooks do not. Put .copilot-transcripts/ in your .gitignore, and note the bash side needs jq.
Test the loop in two minutes: start a task, let it get mid-step, drop a NEXT.md in place, run /compact by hand (manual compactions fire the same hook, and Esc cancels if you change your mind), then ask what it was doing and what comes next. It should answer from the file instead of re-deriving from the repo, and .copilot-transcripts should hold a fresh copy. If it re-derives, the AGENTS.md block did not load, and /instructions lists the instruction files the session actually discovered. /context shows where you stand against the window, and /session checkpoints lists what each compaction kept when you need to read back.
I think “task context” should be split from the prose summary.
The summary can remain lossy, but compaction should preserve a tiny structured continuation record verbatim:
current objective
active step
last completed tool/result
pending next action
relevant artifact/path refs
blocked/waiting state
The key invariant is: compaction may summarize history, but it must not synthesize or rewrite the execution cursor.
A deterministic regression test could compact between a tool result and the next planned action, then assert the post-compaction agent continues with exactly that next action without rediscovering state from the repo.
The record only stays verbatim if nothing has to author it. A model-written continuation record is stale at an arbitrary compaction point, because it reflects the last time the model chose to update it, not the last thing that happened. Some of your fields are mechanical though. The last tool call and its result, path refs included, sit in the transcript tail and can be carried over byte for byte with zero synthesis. Pending next action is not in the transcript at all unless the model said it out loud before the call, so the regression test as written passes or fails on prompt style rather than on the compactor.
The enforceable version: pin the last assistant turn plus its tool call and result outside the summary, verbatim, on every compaction. That is the cursor. It is always current and needs nothing from the model. Put the structured record on top for objective and blocked state, the fields only the model knows. The test then has two assertions: the pinned tail is byte identical before and after, and the first post-compaction tool call matches the one an uncompacted run produces.
Activity
John-Rood commentedon Sep 25, 2026
The loss at compaction is specific, and it is the same thing every time: the summary keeps the goals, the decisions, the plan state, and your instructions, and it drops the cursor. What was executing at that moment, what the last tool call produced, and which action was queued next live only in the raw turns. After the swap the model re-derives all of it from repo state, which is the screenshot behavior in your example.
One prerequisite: the build in the report predates the mechanisms below (preCompact landed in 1.0.5, compaction checkpoints in 1.0.85, current is 1.0.88). On a current build the loop runs today.
# NEXT doing: comparing login-flow screenshots 3 and 4 in .copilot/tmp next: patch src/auth/login.ts:212, rerun the flow test verified: flow test still fails before the patch.github/hooks/lifecycle.json(or the same file under~/.copilot/hooks/to apply it everywhere):{ "version": 1, "hooks": { "preCompact": [ { "type": "command", "bash": "d=.copilot-transcripts; mkdir -p \"$d\"; cp \"$(jq -r .transcriptPath)\" \"$d/transcript-$(date +%Y%m%d-%H%M%S).jsonl\"", "powershell": "$d='.copilot-transcripts'; New-Item -ItemType Directory -Force $d | Out-Null; $p=(ConvertFrom-Json ([Console]::In.ReadToEnd())).transcriptPath; Copy-Item $p (\"$d/transcript-{0}.jsonl\" -f (Get-Date -Format 'yyyyMMdd-HHmmss'))", "cwd": ".", "timeoutSec": 10 } ], "sessionStart": [ { "type": "command", "bash": "printf '{\"additionalContext\": \"If NEXT.md exists, read it and continue from its next line.\"}'", "powershell": "Write-Output '{\"additionalContext\": \"If NEXT.md exists, read it and continue from its next line.\"}'", "cwd": "." } ] } }The preCompact entry fires right before compaction, manual or automatic, and its payload carries the transcript path (it arrives on stdin), so the full record gets copied while it still exists. The sessionStart entry returns additionalContext, which the CLI injects at the top of a new or resumed session. Command hooks fire on resume, prompt hooks do not. Put
.copilot-transcripts/in your .gitignore, and note the bash side needs jq.Test the loop in two minutes: start a task, let it get mid-step, drop a NEXT.md in place, run /compact by hand (manual compactions fire the same hook, and Esc cancels if you change your mind), then ask what it was doing and what comes next. It should answer from the file instead of re-deriving from the repo, and
.copilot-transcriptsshould hold a fresh copy. If it re-derives, the AGENTS.md block did not load, and /instructions lists the instruction files the session actually discovered. /context shows where you stand against the window, and /session checkpoints lists what each compaction kept when you need to read back.kvnloo commentedon Sep 27, 2026
I think “task context” should be split from the prose summary.
The summary can remain lossy, but compaction should preserve a tiny structured continuation record verbatim:
The key invariant is: compaction may summarize history, but it must not synthesize or rewrite the execution cursor.
A deterministic regression test could compact between a tool result and the next planned action, then assert the post-compaction agent continues with exactly that next action without rediscovering state from the repo.
John-Rood commentedon Sep 27, 2026
The record only stays verbatim if nothing has to author it. A model-written continuation record is stale at an arbitrary compaction point, because it reflects the last time the model chose to update it, not the last thing that happened. Some of your fields are mechanical though. The last tool call and its result, path refs included, sit in the transcript tail and can be carried over byte for byte with zero synthesis. Pending next action is not in the transcript at all unless the model said it out loud before the call, so the regression test as written passes or fails on prompt style rather than on the compactor.
The enforceable version: pin the last assistant turn plus its tool call and result outside the summary, verbatim, on every compaction. That is the cursor. It is always current and needs nothing from the model. Put the structured record on top for objective and blocked state, the fields only the model knows. The test then has two assertions: the pinned tail is byte identical before and after, and the first post-compaction tool call matches the one an uncompacted run produces.