Claude Code commited on
Commit
7d03e70
·
1 Parent(s): 5c8e82c

god: implement INVASIVE DIAGNOSTICS Protocol — Break Hypothesis Loop with Actual Crash Logs

Browse files
Files changed (1) hide show
  1. scripts/conversation-loop.py +67 -1
scripts/conversation-loop.py CHANGED
@@ -1301,7 +1301,14 @@ def gather_context():
1301
  # 2. Environment variables
1302
  ctx["env"] = action_get_env()
1303
 
1304
- # 3. File lists (cache, refresh when stage changes)
 
 
 
 
 
 
 
1305
  cache_key = f"files_{child_state['stage']}"
1306
  if cache_key not in _context_cache:
1307
  ctx["space_files"] = action_list_files("space")
@@ -1316,11 +1323,59 @@ def gather_context():
1316
  return ctx
1317
 
1318
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1319
  def format_context(ctx):
1320
  """Format gathered context into a readable string for the LLM."""
1321
  parts = []
1322
  parts.append(f"=== HEALTH ===\n{ctx.get('health', 'unknown')}")
1323
  parts.append(f"\n=== ENVIRONMENT ===\n{ctx.get('env', 'none')}")
 
 
 
 
 
 
1324
  if ctx.get("space_files"):
1325
  parts.append(f"\n=== SPACE FILES ===\n{ctx['space_files'][:2000]}")
1326
  if ctx.get("dataset_files"):
@@ -2257,6 +2312,17 @@ def build_turn_message(speaker, other, ctx):
2257
  parts.append(f"\n{CHILD_NAME} is {child_state['stage']}. Cooldown active: {int(cooldown_remaining)}s remaining. Discuss plans but DO NOT assign [TASK] until cooldown ends.")
2258
  else:
2259
  parts.append(f"\n{CHILD_NAME} is {child_state['stage']}. No cooldown. YOU MUST write a [TASK]...[/TASK] to investigate or fix issues. Don't just discuss.")
 
 
 
 
 
 
 
 
 
 
 
2260
  # Add recent task reminder during cooldown/building
2261
  if recent_task_reminder:
2262
  last_completed, last_by, last_at = recent_task_reminder
 
1301
  # 2. Environment variables
1302
  ctx["env"] = action_get_env()
1303
 
1304
+ # 3. INVASIVE DIAGNOSTICS Fetch actual crash logs when in deadlock states
1305
+ # This breaks the "hypothesize -> check code -> repeat" loop by providing actual runtime errors
1306
+ if child_state["stage"] in ("RUNNING_APP_STARTING", "RUNTIME_ERROR", "BUILD_ERROR"):
1307
+ diagnostic = _fetch_invasive_diagnostics()
1308
+ if diagnostic:
1309
+ ctx["invasive_diagnostics"] = diagnostic
1310
+
1311
+ # 4. File lists (cache, refresh when stage changes)
1312
  cache_key = f"files_{child_state['stage']}"
1313
  if cache_key not in _context_cache:
1314
  ctx["space_files"] = action_list_files("space")
 
1323
  return ctx
1324
 
1325
 
1326
+ def _fetch_invasive_diagnostics():
1327
+ """Fetch actual crash logs and error details from HF API.
1328
+ Bypasses frontend/a2a-proxy to get real runtime errors."""
1329
+ if not child_state["created"]:
1330
+ return None
1331
+
1332
+ diagnostic_parts = []
1333
+ try:
1334
+ # Fetch detailed runtime error from HF Spaces API
1335
+ rresp = requests.get(
1336
+ f"https://huggingface.co/api/spaces/{CHILD_SPACE_ID}/runtime",
1337
+ headers={"Authorization": f"Bearer {HF_TOKEN}"}, timeout=10)
1338
+ if rresp.ok:
1339
+ rdata = rresp.json()
1340
+ error_message = rdata.get("errorMessage", "")
1341
+ if error_message:
1342
+ diagnostic_parts.append(f"=== RUNTIME ERROR MESSAGE ===\n{error_message[:2000]}")
1343
+
1344
+ # Also check stage and runtime info
1345
+ stage = rdata.get("stage", "")
1346
+ if stage:
1347
+ diagnostic_parts.append(f"\n=== RUNTIME STAGE ===\n{stage}")
1348
+
1349
+ # Check if there's runtime info
1350
+ runtime_info = rdata.get("runtime", {})
1351
+ if runtime_info:
1352
+ diagnostic_parts.append(f"\n=== RUNTIME INFO ===\n{str(runtime_info)[:500]}")
1353
+ except Exception as e:
1354
+ diagnostic_parts.append(f"=== DIAGNOSTIC FETCH ERROR ===\n{e}")
1355
+
1356
+ # Try to fetch recent logs from the Space's exposed endpoint (if available)
1357
+ try:
1358
+ lresp = requests.get(f"{CHILD_SPACE_URL}/api/logs", timeout=5)
1359
+ if lresp.ok:
1360
+ logs = lresp.text
1361
+ diagnostic_parts.append(f"\n=== RECENT LOGS (last 1000 chars) ===\n{logs[-1000:]}")
1362
+ except:
1363
+ pass # Endpoint might not exist, that's OK
1364
+
1365
+ return "\n".join(diagnostic_parts) if diagnostic_parts else None
1366
+
1367
+
1368
  def format_context(ctx):
1369
  """Format gathered context into a readable string for the LLM."""
1370
  parts = []
1371
  parts.append(f"=== HEALTH ===\n{ctx.get('health', 'unknown')}")
1372
  parts.append(f"\n=== ENVIRONMENT ===\n{ctx.get('env', 'none')}")
1373
+
1374
+ # INVASIVE DIAGNOSTICS — Show crash logs FIRST (before file lists)
1375
+ # This ensures agents see actual runtime errors before hypothesizing
1376
+ if ctx.get("invasive_diagnostics"):
1377
+ parts.append(f"\n=== INVASIVE DIAGNOSTICS (ACTUAL CRASH LOGS) ===\n{ctx['invasive_diagnostics']}")
1378
+
1379
  if ctx.get("space_files"):
1380
  parts.append(f"\n=== SPACE FILES ===\n{ctx['space_files'][:2000]}")
1381
  if ctx.get("dataset_files"):
 
2312
  parts.append(f"\n{CHILD_NAME} is {child_state['stage']}. Cooldown active: {int(cooldown_remaining)}s remaining. Discuss plans but DO NOT assign [TASK] until cooldown ends.")
2313
  else:
2314
  parts.append(f"\n{CHILD_NAME} is {child_state['stage']}. No cooldown. YOU MUST write a [TASK]...[/TASK] to investigate or fix issues. Don't just discuss.")
2315
+
2316
+ # INVASIVE DIAGNOSTICS PROTOCOL — Break hypothesis loop when in DEADLOCK states
2317
+ # When Cain is stuck starting, agents MUST verify against actual runtime errors
2318
+ if child_state["stage"] == "RUNNING_APP_STARTING":
2319
+ parts.append(f"\n🔴 INVASIVE DIAGNOSTICS REQUIRED:")
2320
+ parts.append(f"{CHILD_NAME} is DEADLOCKED in APP_STARTING state!")
2321
+ parts.append(f"⛑️ CHECK THE 'INVASIVE DIAGNOSTICS' SECTION ABOVE — it contains ACTUAL crash logs from runtime!")
2322
+ parts.append(f"🛑 DO NOT hypothesize based on source code reading. The crash logs tell you the TRUTH.")
2323
+ parts.append(f"📋 Your [TASK] must address the SPECIFIC Python Exception shown in the diagnostics.")
2324
+ parts.append(f"🚨 PUSH a fix NOW. Trial-and-error > 10 turns of discussion.")
2325
+
2326
  # Add recent task reminder during cooldown/building
2327
  if recent_task_reminder:
2328
  last_completed, last_by, last_at = recent_task_reminder