codex review fixes: duplicate-post recovery, fsync, fatal corrupt state, narrower comment drop
- create_post: on HTTPError, look for an existing post tagged ledger:<id> (insert-then-error recovery) and adopt it before retrying - save_state: fsync tmp file before rename - load_state: corrupt state is fatal (auto-reseed would duplicate every open item's post) - comment: drop only on 500/404/410; 401/403/429/502/503 retry next poll Accepted as-is (with rationale): same-second ordering relies on the feed replaying the JSONL in append order + Python's stable sort; seed-window add+close races reduce to the documented 'predates bridge' skip. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01YECpkAwQUwgu7NVy91R8fW
This commit is contained in:
co-authored by
Claude Fable 5
parent
1a3ab878b4
commit
e005931cb5
@@ -97,7 +97,11 @@ def load_state() -> dict:
|
||||
try:
|
||||
return json.loads(STATE_FILE.read_text(encoding="utf-8"))
|
||||
except json.JSONDecodeError:
|
||||
log(f"WARN state file corrupt, starting fresh: {STATE_FILE}")
|
||||
# Starting fresh here would re-seed every open item -> a duplicate
|
||||
# post per item. Refuse to run until a human inspects the file.
|
||||
log(f"FATAL state file corrupt: {STATE_FILE} — fix or remove it "
|
||||
"deliberately; refusing to reseed automatically")
|
||||
sys.exit(1)
|
||||
return {"checkpoint": "", "seen": [], "map": {}, "seeded": False,
|
||||
"channel_id": ""}
|
||||
|
||||
@@ -105,7 +109,10 @@ def load_state() -> dict:
|
||||
def save_state(state: dict) -> None:
|
||||
STATE_FILE.parent.mkdir(parents=True, exist_ok=True)
|
||||
tmp = STATE_FILE.with_suffix(".json.tmp")
|
||||
tmp.write_text(json.dumps(state, indent=1), encoding="utf-8")
|
||||
with open(tmp, "w", encoding="utf-8") as f:
|
||||
json.dump(state, f, indent=1)
|
||||
f.flush()
|
||||
os.fsync(f.fileno())
|
||||
os.replace(tmp, STATE_FILE)
|
||||
|
||||
|
||||
@@ -239,10 +246,36 @@ def send_message(channel_id: str, content: str, thread_id=None) -> dict:
|
||||
body)
|
||||
|
||||
|
||||
def find_existing_post(state: dict, ledger_id: str):
|
||||
"""Recovery lookup: the target API can insert a row and then fail, so a
|
||||
retried create must first check whether a post tagged ledger:<id> already
|
||||
exists. Bounded to the most recent 200 channel messages."""
|
||||
try:
|
||||
msgs = api("GET", f"/api/workspaces/{WORKSPACE_ID}/comms/channels/"
|
||||
f"{ensure_channel(state)}/messages?limit=200") or []
|
||||
except Exception:
|
||||
return None
|
||||
needle = f"ledger:{ledger_id}"
|
||||
for m in msgs:
|
||||
if not m.get("thread_id") and needle in (m.get("content") or ""):
|
||||
return m.get("id")
|
||||
return None
|
||||
|
||||
|
||||
def create_post(state: dict, item: dict) -> None:
|
||||
if item["id"] in state["map"]:
|
||||
return # already mirrored (seed/feed overlap)
|
||||
msg = send_message(ensure_channel(state), post_body(item))
|
||||
try:
|
||||
msg = send_message(ensure_channel(state), post_body(item))
|
||||
except urllib.error.HTTPError:
|
||||
# The insert may have landed before the error (proven insert-then-500
|
||||
# behaviour). Adopt an existing post instead of duplicating on retry.
|
||||
existing = find_existing_post(state, item["id"])
|
||||
if existing:
|
||||
state["map"][item["id"]] = existing
|
||||
log(f"post {item['id'][:8]} -> adopted existing {existing}")
|
||||
return
|
||||
raise # genuinely not created; retry next poll
|
||||
state["map"][item["id"]] = msg["id"]
|
||||
log(f"post {item['id'][:8]} -> {msg['id']} ({item.get('title', '')[:60]})")
|
||||
|
||||
@@ -255,11 +288,15 @@ def comment(state: dict, ledger_id: str, content: str, what: str) -> None:
|
||||
try:
|
||||
msg = send_message(ensure_channel(state), content, thread_id=post_id)
|
||||
except urllib.error.HTTPError as e:
|
||||
# mib007 comms can 500 AFTER the insert (proven: mention-path crash),
|
||||
# so retrying a failed comment risks duplicates. Comments are
|
||||
# best-effort: log and move on instead of blocking the checkpoint.
|
||||
log(f"DROP {what} {ledger_id[:8]}: HTTP {e.code} (comment not retried)")
|
||||
return
|
||||
# 500: mib007 comms can fail AFTER the insert (proven mention-path
|
||||
# crash) — retrying risks duplicates, so drop. 404/410: the post is
|
||||
# gone — retrying can never succeed. Everything else (401/403/429/
|
||||
# 502/503) is safe to retry next poll.
|
||||
if e.code in (500, 404, 410):
|
||||
log(f"DROP {what} {ledger_id[:8]}: HTTP {e.code} "
|
||||
"(comment not retried)")
|
||||
return
|
||||
raise
|
||||
log(f"{what:6} {ledger_id[:8]} -> comment {msg['id']}")
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user