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:
Nirav Patel
2026-08-22 14:47:34 -04:00
co-authored by Claude Fable 5
parent 1a3ab878b4
commit e005931cb5
+43 -6
View File
@@ -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)
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)")
# 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']}")