Skip to content

Commit 100dbcb

Browse files
jvalin17claude
andcommitted
Replace stop/handoff with warn-only — compaction continues session
Sessions never stop. Context pressure is handled by Claude Code's built-in compaction. Session limits and drift detection only warn so the agent wraps up current work instead of starting new tasks. Removed: grace periods, stopped states, auto-handoff triggers from session_limits. PostCompact just increments counter and warns. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent cf733ad commit 100dbcb

4 files changed

Lines changed: 80 additions & 419 deletions

File tree

hooks/session_limits.py

Lines changed: 19 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
"""Session limit enforcement (grace period, handoff triggers) for PreToolUse."""
1+
"""Session limit enforcement — warn only, never stop.
2+
3+
Sessions continue through compaction. Limits produce warnings
4+
so the agent prioritizes current work over starting new tasks.
5+
No handoff, no stop, no restart — compaction handles context pressure.
6+
"""
27

3-
from auto_handoff import trigger_auto_handoff
48
from session_state import (
59
FALLBACK_MAX_EXCHANGES,
6-
GRACE_TOOL_CALLS,
710
HARD_THRESHOLD_BYTES,
811
SessionState,
912
check_thresholds,
@@ -12,81 +15,29 @@
1215

1316

1417
def apply_session_limits(state: SessionState) -> tuple:
15-
"""Apply byte/time/compaction limits. Returns (state, response_message).
18+
"""Apply byte/time limits. Returns (state, response_message).
1619
17-
When continue_mode is False, limits only warn — no handoff, no stop.
18-
Stopping a session without auto-restart is pointless.
20+
Never stops the session. Warns once when thresholds are hit
21+
so the agent wraps up current work instead of starting new tasks.
22+
Compaction handles context pressure naturally.
1923
"""
2024
triggered, stop_reason = check_thresholds(state)
2125

22-
if triggered and not state.continue_mode:
23-
# No auto-restart — just warn, don't kill the session
24-
if not state.warned:
25-
state.warned = True
26-
return state, (
27-
f"SESSION LIMIT WARNING: {stop_reason}. "
28-
f"Auto-continuation is off, so the session will continue. "
29-
f"Quality may degrade. Consider wrapping up current work."
30-
)
31-
return state, ""
32-
33-
if triggered:
34-
return _handle_limit_triggered(state, stop_reason)
26+
if triggered and not state.warned:
27+
state.warned = True
28+
return state, (
29+
f"SESSION LIMIT: {stop_reason}. "
30+
f"The session will continue (compaction handles context pressure). "
31+
f"Quality may degrade. Wrap up current work before starting new tasks."
32+
)
3533

3634
if should_warn(state) and not state.warned:
3735
state.warned = True
38-
response = (
36+
return state, (
3937
f"SESSION WARNING: {state.exchanges}/{FALLBACK_MAX_EXCHANGES} exchanges, "
4038
f"{state.cumulative_output_bytes:,}/{HARD_THRESHOLD_BYTES:,} bytes. "
41-
f"You are approaching the session limit. Finish current work "
39+
f"Approaching session limit. Finish current work "
4240
f"and do not start new slabs or features."
4341
)
44-
return state, response
4542

4643
return state, ""
47-
48-
49-
def _handle_limit_triggered(state: SessionState, stop_reason: str) -> tuple:
50-
"""Handle limit when continue_mode is True — write handoff and stop."""
51-
is_time_triggered = "minutes" in stop_reason
52-
53-
if is_time_triggered and state.stopped < 2:
54-
state.stopped = 2
55-
trigger_auto_handoff(state, stop_reason)
56-
return state, (
57-
f"SESSION TIME LIMIT: {stop_reason}.\n\n"
58-
f"HANDOFF.md has been written automatically by the hook.\n"
59-
f"A fresh session will be launched automatically.\n"
60-
f"Finish your current task, then write HANDOFF.md."
61-
)
62-
63-
if state.stopped == 0:
64-
state.stopped = 1
65-
state.stop_at_tool_call = state.tool_calls + GRACE_TOOL_CALLS
66-
return state, (
67-
f"SESSION LIMIT REACHED: {stop_reason}. "
68-
f"You have {GRACE_TOOL_CALLS} tool calls remaining to wrap up.\n\n"
69-
f"Finish your current task, then IMMEDIATELY:\n"
70-
f"1. Write HANDOFF.md — current status, what's done (commits), "
71-
f"what's next (spec text copied, not summarized), decisions, "
72-
f"code change plan for next slab\n"
73-
f"2. Update project-state.md Resume section\n"
74-
f"3. A fresh session will be launched automatically after this one ends.\n\n"
75-
f"Do NOT start new tasks. Finish current task and hand off."
76-
)
77-
78-
grace_remaining = state.stop_at_tool_call - state.tool_calls
79-
if grace_remaining <= 0:
80-
if state.stopped != 2:
81-
state.stopped = 2
82-
trigger_auto_handoff(state, stop_reason)
83-
return state, (
84-
f"HARD STOP: Grace period exhausted. {stop_reason}.\n\n"
85-
f"HANDOFF.md has been written automatically by the hook.\n"
86-
f"A fresh session will be launched automatically."
87-
)
88-
89-
return state, (
90-
f"SESSION LIMIT: {grace_remaining} tool calls remaining "
91-
f"before hard stop. Finish current work and write HANDOFF.md."
92-
)

hooks/session_monitor.py

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -73,19 +73,11 @@ def _strict_integrity_response(state: SessionState) -> Optional[str]:
7373
f"- Drift score: {drift:.2f}\n"
7474
)
7575
if drift > 0.8:
76-
if state.continue_mode:
77-
response += (
78-
"\nCRITICAL DRIFT: Score exceeds 0.8. SESSION RESTART required.\n"
79-
"Write HANDOFF.md immediately and exit. "
80-
"A fresh session will be launched automatically."
81-
)
82-
state.stopped = 2
83-
else:
84-
response += (
85-
"\nCRITICAL DRIFT: Score exceeds 0.8. "
86-
"Query the real system before continuing. "
87-
"Quality is degrading."
88-
)
76+
response += (
77+
"\nCRITICAL DRIFT: Score exceeds 0.8. "
78+
"Query the real system before continuing. "
79+
"Quality is degrading."
80+
)
8981
elif drift > 0.6:
9082
response += (
9183
"\nHIGH DRIFT: Score exceeds 0.6. "
@@ -206,20 +198,17 @@ def handle_post_tool_use(
206198

207199

208200
def handle_post_compact(state: SessionState) -> tuple:
209-
"""Handle PostCompact event. Context was compressed — trigger auto-handoff."""
210-
state.compactions += 1
201+
"""Handle PostCompact event. Context was compressed — session continues.
211202
212-
stop_reason = (
213-
f"Context compacted ({state.compactions} time(s)) "
214-
f"— context window is full"
215-
)
216-
trigger_auto_handoff(state, stop_reason)
217-
state.stopped = 2
203+
Compaction is normal. The session keeps running with compressed context.
204+
We just note it and warn that quality may degrade.
205+
"""
206+
state.compactions += 1
218207

219208
response = (
220-
"CONTEXT COMPACTED: Context window was compressed by Claude Code. "
221-
"HANDOFF.md has been written automatically by the hook. "
222-
"Finish your current task, then write HANDOFF.md."
209+
f"CONTEXT COMPACTED ({state.compactions}x): Claude Code compressed the context. "
210+
f"The session continues. Quality may degrade with repeated compactions. "
211+
f"Consider wrapping up current work."
223212
)
224213
return state, response
225214

0 commit comments

Comments
 (0)