fix(stop): record and purge only the row's own or a verified id; a dead herdr records none (Issue #63)
This commit is contained in:
1 parent
b8a30d5b93
commit
3a2219454e
2 files changed
+178
-20
No files matched your search
@@ -5,8 +5,8 @@
|
||||
# [--reason <reason>] [--purge-conversation] [--yes]
|
||||
#
|
||||
# 동작: 항상 graceful stop 입니다. send-keys 로 정상 종료를 유도하고
|
||||
# (미종료 시 SIGTERM → SIGKILL 폴백), kill 직전에 이 워크스페이스의
|
||||
# conversation id 를 row 에 확정 기록해 다음 resume 이 tier-1(race-free)
|
||||
# (미종료 시 SIGTERM → SIGKILL 폴백), kill 직전에 이 세션(pane)의 검증된
|
||||
# conversation id 만 row 에 확정 기록해 다음 resume 이 tier-1(race-free)
|
||||
# 으로 복원되게 합니다. status 는 running -> stopped 로 전이합니다.
|
||||
# 멱등: 이미 stopped 면 no-op + exit 0.
|
||||
#
|
||||
@@ -198,12 +198,6 @@ if [ "$PURGE" = "1" ] && [ "$YES" != "1" ]; then
|
||||
exit 3
|
||||
fi
|
||||
|
||||
# purge 대상 UUID 를 워크스페이스 격리해서 해결 (P0-C — 전역 참조 금지)
|
||||
PURGE_UUID=""
|
||||
if [ "$PURGE" = "1" ] && [ -n "$TARGET_CWD" ]; then
|
||||
PURGE_UUID=$(find_workspace_uuid "$TARGET_CWD" "$AGENT" "$SESSION_NAME" || true)
|
||||
fi
|
||||
|
||||
NOW_ISO=$(date -u +'%Y-%m-%dT%H:%M:%SZ')
|
||||
NOW_EPOCH=$(date +%s)
|
||||
|
||||
@@ -215,19 +209,25 @@ if herdr has-session -t "$SESSION_NAME" 2>/dev/null; then
|
||||
LAST_STATUS=$(herdr capture-pane -t "$SESSION_NAME" -p -S -10 2>/dev/null | tr '\n' ' ' | head -c 500 || true)
|
||||
fi
|
||||
|
||||
# 캡처: kill 직전에 conversation id 를 해결 (process/jsonl 이 아직 살아있을 때).
|
||||
# find_workspace_uuid 가 tier-1(row) -> tier-2(workspace-scoped disk scan)
|
||||
# 를 알아서 시도하므로 herdr 생사와 무관하게 동작.
|
||||
CAPTURED_UUID=""
|
||||
if [ "$CAPTURE_ID" = "1" ] && [ -n "$TARGET_CWD" ]; then
|
||||
CAPTURED_UUID=$(capture_conversation_id "$AGENT" "$TARGET_CWD" "$SESSION_NAME" || true)
|
||||
if [ -n "$CAPTURED_UUID" ]; then
|
||||
echo "captured conversation id: $CAPTURED_UUID"
|
||||
else
|
||||
echo "WARN: no conversation id resolved before stop (nothing on disk yet)"
|
||||
fi
|
||||
# Issue #63: before the kill, decide the id this stop records and a purge
|
||||
# deletes (lib_py.agents.session_ids): the row's own id; else, for an agent
|
||||
# that writes its id after its first turn, only what the #40 capture proves is
|
||||
# this pane's (nothing once herdr is dead - the reason goes on the row); claude
|
||||
# and grok keep find_workspace_uuid. An id another row records is never used.
|
||||
CAPTURED_UUID="" CAPTURE_STATUS="" PURGE_UUID="" STOP_ID_NOTE=""
|
||||
if [ -n "$TARGET_CWD" ]; then
|
||||
PANE_PID_NOW=""
|
||||
[ "$HERDR_ALIVE" = "1" ] && PANE_PID_NOW=$(herdr list-panes -t "$SESSION_NAME" -F '#{pane_pid}' 2>/dev/null | head -1 || true)
|
||||
eval "$(mam_session_ids stop --session "$SESSION_NAME" --agent "$AGENT" --cwd "$TARGET_CWD" \
|
||||
--herdr-alive "$HERDR_ALIVE" --pane-pid "$PANE_PID_NOW" --purge "$PURGE")" || true
|
||||
if [ -n "$CAPTURED_UUID" ]; then echo "captured conversation id: $CAPTURED_UUID"
|
||||
else echo "WARN: no conversation id recorded before stop${STOP_ID_NOTE:+ - $STOP_ID_NOTE}"; fi
|
||||
fi
|
||||
|
||||
|
||||
|
||||
# (end of pre-kill id decision)
|
||||
|
||||
delegate_publish_event "$DELEGATE_JOB_ID" progress "terminating" "$DELEGATE_JOB_TOKEN" "$DELEGATE_JOB_ATTEMPT"
|
||||
|
||||
# graceful 종료: send-keys 로 정상 종료 유도 → 폴백 체인 (SIGTERM → SIGKILL).
|
||||
@@ -304,7 +304,7 @@ atomic_dump_yaml "$AGENT_SESSIONS_YAML" \
|
||||
SESSION_NAME="$SESSION_NAME" AGENT="$AGENT" PURGE="$PURGE" \
|
||||
NOW_ISO="$NOW_ISO" NOW_EPOCH="$NOW_EPOCH" LAST_STATUS="$LAST_STATUS" \
|
||||
PURGE_UUID="$PURGE_UUID" TARGET_CWD="$TARGET_CWD" \
|
||||
REASON="$REASON" CAPTURED_UUID="$CAPTURED_UUID" <<'PYEOF'
|
||||
REASON="$REASON" CAPTURED_UUID="$CAPTURED_UUID" CAPTURE_STATUS="$CAPTURE_STATUS" STOP_ID_NOTE="$STOP_ID_NOTE" <<'PYEOF'
|
||||
import shutil
|
||||
name = os.environ['SESSION_NAME']
|
||||
agent = os.environ['AGENT']
|
||||
@@ -326,6 +326,19 @@ if target is None:
|
||||
print(f"ERROR: disappeared during script: {name}", flush=True)
|
||||
raise SystemExit(1)
|
||||
|
||||
from lib_py.agents.autopin import claimed_elsewhere, pin_row
|
||||
capture_status = os.environ.get('CAPTURE_STATUS', '').strip()
|
||||
note = os.environ.get('STOP_ID_NOTE', '').strip()
|
||||
claimed = set(claimed_elsewhere(target, d))
|
||||
if captured and captured in claimed:
|
||||
print(f"WARN: refusing to record {captured} on {name}; another row already claims it", flush=True)
|
||||
if not note:
|
||||
note = f"{captured} is already claimed by another row"
|
||||
captured = ''
|
||||
if purge_uuid and purge_uuid in claimed:
|
||||
print(f"WARN: refusing to purge {purge_uuid} for {name}; another row claims it", flush=True)
|
||||
purge_uuid = ''
|
||||
|
||||
if not purge:
|
||||
target['status'] = 'stopped'
|
||||
target['stopped_at'] = now
|
||||
@@ -350,7 +363,11 @@ if captured and not purge:
|
||||
target['opencode_session_id_own'] = captured
|
||||
elif agent == 'codex':
|
||||
target['codex_session_id_own'] = captured
|
||||
if capture_status == 'captured':
|
||||
pin_row(target, agent, captured)
|
||||
target['resumable'] = True
|
||||
elif not purge and note:
|
||||
target['last_visible_status'] = f"stop: {note}"
|
||||
|
||||
if purge and purge_uuid:
|
||||
from lib_py.agents.registry import get_adapter
|
||||
|
||||
Reference in new issue
Block a user