Spaces:
Runtime error
Runtime error
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>
- 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 |
-
|
| 446 |
-
|
| 447 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
|
|
|
| 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
|