tao-shen Claude Opus 4.6 commited on
Commit
c7d4e65
Β·
1 Parent(s): 44f1609

fix: block write_file during BUILDING/APP_STARTING, fix guard bypass

Browse files

- Add APP_STARTING to building-state guard (was only BUILDING/RESTARTING)
- Add guards to the [CONTENT] write_file handler which previously bypassed
all safety checks (building-state guard + cooldown)
- Clearer error messages explaining that writes during build reset progress

Without this fix, Adam & Eve could write to Space files during APP_STARTING
and during BUILDING via the [CONTENT] handler, causing infinite rebuild loops.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Files changed (1) hide show
  1. scripts/conversation-loop.py +30 -8
scripts/conversation-loop.py CHANGED
@@ -442,9 +442,30 @@ def parse_and_execute_actions(raw_text):
442
  key = f"write_file:{target}:{path}"
443
  if key not in executed:
444
  executed.add(key)
445
- result = action_write_file(target, path, content)
446
- results.append({"action": key, "result": result})
447
- print(f"[ACTION] {key} β†’ {result[:100]}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
448
 
449
  # 2. Handle all [ACTION/Action/ζ“δ½œ/动作: ...] tags β€” case-insensitive, multilingual
450
  for match in re.finditer(r'\[(?:ACTION|Action|action|ζ“δ½œ|动作)\s*[::]\s*([^\]]+)\]', raw_text):
@@ -468,10 +489,11 @@ def parse_and_execute_actions(raw_text):
468
  if len(results) >= 1:
469
  break
470
 
471
- # Block restart/write when Cain is building β€” just wait
472
- if child_state["stage"] in ("BUILDING", "RESTARTING") and name in ("restart", "write_file", "set_env", "set_secret"):
473
  result = (f"β›” BLOCKED: Cain is currently {child_state['stage']}. "
474
- "Do NOT restart or make changes β€” wait for the build to finish. "
 
475
  "Use [ACTION: check_health] to monitor progress.")
476
  results.append({"action": action_str, "result": result})
477
  print(f"[BLOCKED] {name} β€” Cain is {child_state['stage']}")
@@ -552,8 +574,8 @@ def parse_and_execute_actions(raw_text):
552
  break
553
 
554
  # Apply same blocking rules
555
- if child_state["stage"] in ("BUILDING", "RESTARTING") and name in ("restart", "write_file", "set_env", "set_secret"):
556
- result = (f"β›” BLOCKED: Cain is currently {child_state['stage']}. Wait for it to finish.")
557
  results.append({"action": action_str, "result": result})
558
  print(f"[BLOCKED-emoji] {name} β€” Cain is {child_state['stage']}")
559
  break
 
442
  key = f"write_file:{target}:{path}"
443
  if key not in executed:
444
  executed.add(key)
445
+ # Guard: block write_file during BUILDING/APP_STARTING/RESTARTING
446
+ if target == "space" and child_state["stage"] in ("BUILDING", "RESTARTING", "APP_STARTING"):
447
+ result = (f"β›” BLOCKED: Cain is currently {child_state['stage']}. "
448
+ "Writing to Space during build RESETS the entire build from scratch. "
449
+ "Wait for it to finish, then try again.")
450
+ results.append({"action": key, "result": result})
451
+ print(f"[BLOCKED] {key} β€” Cain is {child_state['stage']}")
452
+ # Guard: rebuild cooldown
453
+ elif target == "space" and last_rebuild_trigger_at > 0:
454
+ elapsed = time.time() - last_rebuild_trigger_at
455
+ if elapsed < REBUILD_COOLDOWN_SECS:
456
+ remaining = int(REBUILD_COOLDOWN_SECS - elapsed)
457
+ result = (f"β›” BLOCKED: Rebuild cooldown active ({remaining}s remaining). "
458
+ "Every write_file to Space triggers a full rebuild.")
459
+ results.append({"action": key, "result": result})
460
+ print(f"[BLOCKED] {key} β€” rebuild cooldown ({remaining}s remaining)")
461
+ else:
462
+ result = action_write_file(target, path, content)
463
+ results.append({"action": key, "result": result})
464
+ print(f"[ACTION] {key} β†’ {result[:100]}")
465
+ else:
466
+ result = action_write_file(target, path, content)
467
+ results.append({"action": key, "result": result})
468
+ print(f"[ACTION] {key} β†’ {result[:100]}")
469
 
470
  # 2. Handle all [ACTION/Action/ζ“δ½œ/动作: ...] tags β€” case-insensitive, multilingual
471
  for match in re.finditer(r'\[(?:ACTION|Action|action|ζ“δ½œ|动作)\s*[::]\s*([^\]]+)\]', raw_text):
 
489
  if len(results) >= 1:
490
  break
491
 
492
+ # Block restart/write when Cain is building/starting β€” just wait
493
+ if child_state["stage"] in ("BUILDING", "RESTARTING", "APP_STARTING") and name in ("restart", "write_file", "set_env", "set_secret"):
494
  result = (f"β›” BLOCKED: Cain is currently {child_state['stage']}. "
495
+ "Do NOT restart or make changes β€” wait for it to finish starting. "
496
+ "Every write_file during build RESETS the entire build from scratch. "
497
  "Use [ACTION: check_health] to monitor progress.")
498
  results.append({"action": action_str, "result": result})
499
  print(f"[BLOCKED] {name} β€” Cain is {child_state['stage']}")
 
574
  break
575
 
576
  # Apply same blocking rules
577
+ if child_state["stage"] in ("BUILDING", "RESTARTING", "APP_STARTING") and name in ("restart", "write_file", "set_env", "set_secret"):
578
+ result = (f"β›” BLOCKED: Cain is currently {child_state['stage']}. Wait for it to finish starting. Writing during build RESETS it.")
579
  results.append({"action": action_str, "result": result})
580
  print(f"[BLOCKED-emoji] {name} β€” Cain is {child_state['stage']}")
581
  break