fix(reset,stop): warn before a reset purges, skip and prune stale registries, refuse a guessed purge

reset.sh without --yes says the conversations are deleted for good before it
asks; without a terminal the refusal says so too. A registry that cannot be
created is skipped, since nothing can be registered there, and the directories
the fence created are removed on exit when still empty. stop --purge-conversation
refuses a conversation the row does not record instead of deleting the folder's
newest one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
This commit is contained in:
GodopuandClaude Opus 5.5 committed 2026-10-31 07:56:07 +09:00
1 parent 12e140322d
commit 022c656052
9 files changed
+201 -28

No files matched your search

@@ -36,7 +36,7 @@ while [ $# -gt 0 ]; do
done
if [ "$DRY_RUN" != "1" ] && [ "$YES" != "1" ] && [ ! -t 0 ]; then
echo "ERROR: not a terminal; run with -y/--yes (or --dry-run to inspect)" >&2
echo "ERROR: not a terminal, so the reset cannot ask; it permanently deletes conversations: run with -y/--yes once that is confirmed (or --dry-run to inspect)" >&2
exit 3
fi
@@ -115,11 +115,13 @@ case "$_acquire_rc" in
*) _refuse_locked "cannot determine whether a review loop or another reset is running (ps unavailable?)" ;;
esac
FENCED=()
CREATED=()
_release_reset_lock() {
local d
for d in ${FENCED[@]+"${FENCED[@]}"}; do
[ "$(_mam_lock_field "$d/.reset-in-progress" pid 2>/dev/null || true)" != "$$" ] || rm -f "$d/.reset-in-progress"
done
[ "${#CREATED[@]}" -eq 0 ] || MAM_STATE_JSON="$STATE_JSON" "$PY" -m lib_py.reset prune-created --workspace "$WS" "${CREATED[@]}" || true
mam_release_loop_lock "$RESET_MARKER" || true
mam_release_loop_lock "$MARKER" || true
}
@@ -154,6 +156,7 @@ fence_refusal=""
while IFS= read -r line; do
case "$line" in
"fenced "*) FENCED+=("${line#fenced }") ;;
"created "*) CREATED+=("${line#created }") ;;
"refused "*) fence_refusal="${line#refused }" ;;
esac
done <<< "$fence_out"
@@ -174,16 +177,19 @@ if [ "$pre_rc" -ne 0 ]; then
exit 1
fi
_confirm_purge() {
local ans=""
echo "WARNING: this permanently deletes the conversations of the agents to reset above; they cannot be recovered."
read -r -p "$1 [y/N]: " ans || ans=""
case "$ans" in
y|Y|yes|Yes|YES) ;;
*) echo "Reset cancelled; nothing was changed"; exit 0 ;;
esac
}
if [ -f "$SPEC_FILE" ]; then
MAM_STATE_JSON="$STATE_JSON" "$PY" -m lib_py.reset dry-run --workspace "$WS" || exit 1
if [ "$YES" != "1" ]; then
ans=""
read -r -p "Resume reset from $SPEC_FILE? [y/N]: " ans || ans=""
case "$ans" in
y|Y|yes|Yes|YES) ;;
*) echo "Reset cancelled; nothing was changed"; exit 0 ;;
esac
fi
[ "$YES" = "1" ] || _confirm_purge "Really resume the reset from $SPEC_FILE?"
else
PLAN_JSON="$(MAM_STATE_JSON="$STATE_JSON" "$PY" -m lib_py.reset plan --workspace "$WS")" || exit 1
TARGET_COUNT="$(printf '%s' "$PLAN_JSON" | "$PY" -c 'import json,sys; print(len(json.load(sys.stdin).get("targets", [])))')"
@@ -192,14 +198,7 @@ else
echo "nothing to reset"
exit 0
fi
if [ "$YES" != "1" ]; then
ans=""
read -r -p "Purge and recreate $TARGET_COUNT agents? [y/N]: " ans || ans=""
case "$ans" in
y|Y|yes|Yes|YES) ;;
*) echo "Reset cancelled; nothing was changed"; exit 0 ;;
esac
fi
[ "$YES" = "1" ] || _confirm_purge "Really purge and recreate $TARGET_COUNT agents?"
printf '%s' "$PLAN_JSON" | "$PY" -m lib_py.reset write-spec --workspace "$WS" || exit 1
fi