Add orphaned job prevention in generate_book_task

- Guard checks at task start verify: run exists in DB, project folder exists,
  bible.json exists and is parseable, and bible has at least one book
- Any failed check marks the run as 'failed' and returns early, preventing
  jobs from writing to the wrong book or orphaned project directories

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-22 10:05:59 -05:00
parent 98a330c416
commit bcba67a35f

View File

@@ -107,6 +107,56 @@ def generate_book_task(run_id, project_path, bible_path, allow_copy=True, feedba
_task_log(f"Task picked up by Huey worker. project_path={project_path}") _task_log(f"Task picked up by Huey worker. project_path={project_path}")
# 0. Orphaned Job Guard — verify that all required resources exist before
# doing any work. If a run, project folder, or bible is missing, terminate
# silently and mark the run as failed to prevent data being written to the
# wrong book or project.
db_path_early = os.path.join(config.DATA_DIR, "bookapp.db")
try:
with sqlite3.connect(db_path_early, timeout=10) as _conn:
_row = _conn.execute("SELECT id FROM run WHERE id = ?", (run_id,)).fetchone()
if not _row:
_task_log(f"ABORT: Run #{run_id} no longer exists in DB. Terminating silently.")
return
except Exception as _e:
_task_log(f"WARNING: Could not verify run #{run_id} existence: {_e}")
if not os.path.isdir(project_path):
_task_log(f"ABORT: Project folder missing ({project_path}). Marking run #{run_id} as failed.")
try:
_robust_update_run_status(db_path_early, run_id, 'failed',
end_time=datetime.utcnow().isoformat())
except Exception: pass
return
if not os.path.isfile(bible_path):
_task_log(f"ABORT: Bible file missing ({bible_path}). Marking run #{run_id} as failed.")
try:
_robust_update_run_status(db_path_early, run_id, 'failed',
end_time=datetime.utcnow().isoformat())
except Exception: pass
return
# Validate that the bible has at least one book entry
try:
with open(bible_path, 'r', encoding='utf-8') as _bf:
_bible_check = json.load(_bf)
if not _bible_check.get('books'):
_task_log(f"ABORT: Bible has no books defined. Marking run #{run_id} as failed.")
try:
_robust_update_run_status(db_path_early, run_id, 'failed',
end_time=datetime.utcnow().isoformat())
except Exception: pass
return
except Exception as _e:
_task_log(f"ABORT: Could not parse bible ({bible_path}): {_e}. Marking run #{run_id} as failed.")
try:
_robust_update_run_status(db_path_early, run_id, 'failed',
end_time=datetime.utcnow().isoformat())
except Exception: pass
return
# 1. Setup Logging # 1. Setup Logging
log_filename = f"system_log_{run_id}.txt" log_filename = f"system_log_{run_id}.txt"