Spaces:
Runtime error
Runtime error
Claude Code commited on
Commit Β·
a67fdae
1
Parent(s): 6a122fb
god: implement Hard Reset override for discussion loops
Browse files- scripts/conversation-loop.py +67 -0
scripts/conversation-loop.py
CHANGED
|
@@ -135,6 +135,11 @@ _last_push_time = 0.0 # timestamp of last successful push
|
|
| 135 |
_turns_since_last_push = 0 # turns since last push (resets on push)
|
| 136 |
_push_count_this_task = 0 # pushes made during the CURRENT CC task (resets on new task)
|
| 137 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
def _init_push_count_from_workspace():
|
| 139 |
"""Initialize push count from existing workspace commits.
|
| 140 |
This persists push tracking across conversation loop restarts."""
|
|
@@ -1235,6 +1240,11 @@ def enrich_task_with_context(task_desc, ctx):
|
|
| 1235 |
# Only dynamic state β static knowledge (architecture, rules, env vars) is in CLAUDE.md
|
| 1236 |
parts.append(f"\nCurrent stage: {child_state['stage']}")
|
| 1237 |
parts.append(f"Health: {ctx.get('health', 'unknown')}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1238 |
return "\n".join(parts)
|
| 1239 |
|
| 1240 |
|
|
@@ -2178,6 +2188,37 @@ RULES:
|
|
| 2178 |
- CONFIG_ERROR with collision = [ACTION: delete_env:KEY] then [ACTION: restart]
|
| 2179 |
- English first, then --- separator, then Chinese translation""")
|
| 2180 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2181 |
return "\n".join(parts)
|
| 2182 |
|
| 2183 |
|
|
@@ -2588,6 +2629,32 @@ while True:
|
|
| 2588 |
# Reset discussion loop counter since we're starting fresh
|
| 2589 |
_discussion_loop_count = 0
|
| 2590 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2591 |
# Note: Aggressive CC auto-termination based on push frequency is removed.
|
| 2592 |
# God monitors push frequency and proposes mechanism fixes when needed.
|
| 2593 |
# The normal CLAUDE_TIMEOUT auto-kill in do_turn() handles truly stuck processes.
|
|
|
|
| 135 |
_turns_since_last_push = 0 # turns since last push (resets on push)
|
| 136 |
_push_count_this_task = 0 # pushes made during the CURRENT CC task (resets on new task)
|
| 137 |
|
| 138 |
+
# Hard Reset override β FORCE_PUSH mode for breaking discussion loops
|
| 139 |
+
_force_push_mode = False # When True, bypass normal flow and force task generation
|
| 140 |
+
_force_push_trigger_time = 0.0 # When FORCE_PUSH was triggered
|
| 141 |
+
_force_push_skip_termination = False # If True, skip termination (already terminated)
|
| 142 |
+
|
| 143 |
def _init_push_count_from_workspace():
|
| 144 |
"""Initialize push count from existing workspace commits.
|
| 145 |
This persists push tracking across conversation loop restarts."""
|
|
|
|
| 1240 |
# Only dynamic state β static knowledge (architecture, rules, env vars) is in CLAUDE.md
|
| 1241 |
parts.append(f"\nCurrent stage: {child_state['stage']}")
|
| 1242 |
parts.append(f"Health: {ctx.get('health', 'unknown')}")
|
| 1243 |
+
# Hardcoded fix hint for common port binding issues
|
| 1244 |
+
if "port" in task_desc.lower() or "bind" in task_desc.lower() or child_state['stage'] in ("RUNTIME_ERROR", "BUILD_ERROR"):
|
| 1245 |
+
parts.append(f"\nPORT BINDING FIX HINT:")
|
| 1246 |
+
parts.append(f"The child process uses `uvicorn.run(app, host=\"0.0.0.0\", port=7860)`.")
|
| 1247 |
+
parts.append(f"Ensure app.py reflects this exactly β app must be bound to 0.0.0.0:7860.")
|
| 1248 |
return "\n".join(parts)
|
| 1249 |
|
| 1250 |
|
|
|
|
| 2188 |
- CONFIG_ERROR with collision = [ACTION: delete_env:KEY] then [ACTION: restart]
|
| 2189 |
- English first, then --- separator, then Chinese translation""")
|
| 2190 |
|
| 2191 |
+
# CHATTER DETECTION: Check if last 3 messages are pure discussion without [TASK] or code
|
| 2192 |
+
# If agents are stuck in conversational loops, force them to act
|
| 2193 |
+
if len(history) >= 3 and not cc_status["running"]:
|
| 2194 |
+
recent_texts = [h.get("text", "") for h in history[-3:]]
|
| 2195 |
+
conversational_keywords = ["let's", "maybe", "i think", "perhaps", "could we", "should we", "we could", "it might"]
|
| 2196 |
+
chatter_count = 0
|
| 2197 |
+
for text in recent_texts:
|
| 2198 |
+
text_lower = text.lower()
|
| 2199 |
+
# Check if message has [TASK], code blocks (```), or actions
|
| 2200 |
+
has_substance = ("[TASK]" in text or "[ACTION:" in text or "```" in text)
|
| 2201 |
+
# Check if message is mostly conversational
|
| 2202 |
+
is_chatter = any(kw in text_lower for kw in conversational_keywords)
|
| 2203 |
+
if is_chatter and not has_substance:
|
| 2204 |
+
chatter_count += 1
|
| 2205 |
+
if chatter_count >= 3: # All 3 recent messages are chatter without substance
|
| 2206 |
+
parts.append(f"\nπ¨ SYSTEM: STOP DISCUSSION. EXECUTE [TASK] or PUSH.")
|
| 2207 |
+
parts.append(f"Agents are stuck in conversational loop. Write ONLY [TASK]...[/TASK] this turn.")
|
| 2208 |
+
|
| 2209 |
+
# FORCE_PUSH MODE OVERRIDE: Hard reset for discussion loops
|
| 2210 |
+
# When triggered, force agents to generate a task regardless of CC status
|
| 2211 |
+
global _force_push_mode, _force_push_skip_termination
|
| 2212 |
+
if _force_push_mode:
|
| 2213 |
+
parts.append(f"\nπ¨π¨π¨ FORCE_PUSH MODE ACTIVATED π¨π¨π¨")
|
| 2214 |
+
parts.append(f"Discussion loop detected with ZERO pushes. You MUST write a [TASK]...[/TASK] this turn.")
|
| 2215 |
+
if not _force_push_skip_termination:
|
| 2216 |
+
parts.append(f"FIRST: Use [ACTION: terminate_cc] to kill stuck CC.")
|
| 2217 |
+
parts.append(f"THEN: Write [TASK]...[/TASK] with a concrete code fix.")
|
| 2218 |
+
else:
|
| 2219 |
+
parts.append(f"CC is idle. Write [TASK]...[/TASK] NOW with a concrete code fix.")
|
| 2220 |
+
parts.append(f"DO NOT discuss. DO NOT plan. Write task ONLY.")
|
| 2221 |
+
|
| 2222 |
return "\n".join(parts)
|
| 2223 |
|
| 2224 |
|
|
|
|
| 2629 |
# Reset discussion loop counter since we're starting fresh
|
| 2630 |
_discussion_loop_count = 0
|
| 2631 |
|
| 2632 |
+
# HARD RESET OVERRIDE: Detect "all talk no action" deadlock
|
| 2633 |
+
# Trigger: discussion_loop_count > 3 AND zero pushes (last_push_time == 0)
|
| 2634 |
+
# This means agents have been discussing for 4+ turns with ZERO progress.
|
| 2635 |
+
if not _force_push_mode and _discussion_loop_count > 3 and _last_push_time == 0:
|
| 2636 |
+
print(f"[FORCE-PUSH] HARD RESET TRIGGERED: {_discussion_loop_count} discussion turns with ZERO pushes!")
|
| 2637 |
+
_force_push_mode = True
|
| 2638 |
+
_force_push_trigger_time = time.time()
|
| 2639 |
+
# Auto-terminate CC if running (bypass 90s wait if idle > 2 min)
|
| 2640 |
+
cc_idle_time = time.time() - (_last_cc_output_time if _last_cc_output_time > 0 else time.time())
|
| 2641 |
+
if cc_status["running"]:
|
| 2642 |
+
if cc_idle_time > 120: # Idle > 2 minutes, skip wait
|
| 2643 |
+
print(f"[FORCE-PUSH] CC idle > 2min, terminating immediately")
|
| 2644 |
+
action_terminate_cc()
|
| 2645 |
+
_force_push_skip_termination = True
|
| 2646 |
+
else:
|
| 2647 |
+
print(f"[FORCE-PUSH] CC running but active, will terminate on next agent turn")
|
| 2648 |
+
_force_push_skip_termination = False
|
| 2649 |
+
else:
|
| 2650 |
+
_force_push_skip_termination = True # CC already idle
|
| 2651 |
+
|
| 2652 |
+
# Reset FORCE_PUSH mode after 5 minutes (safety valve)
|
| 2653 |
+
if _force_push_mode and time.time() - _force_push_trigger_time > 300:
|
| 2654 |
+
print(f"[FORCE-PUSH] Mode timeout (300s), resetting to normal")
|
| 2655 |
+
_force_push_mode = False
|
| 2656 |
+
_force_push_skip_termination = False
|
| 2657 |
+
|
| 2658 |
# Note: Aggressive CC auto-termination based on push frequency is removed.
|
| 2659 |
# God monitors push frequency and proposes mechanism fixes when needed.
|
| 2660 |
# The normal CLAUDE_TIMEOUT auto-kill in do_turn() handles truly stuck processes.
|