fix(status): prevent false Completed status when mandatory pipeline steps have not run

Add a terminal-step guard (send_to_all_destinations) to all status
calculation paths so that files are only marked Completed once the
entire processing pipeline has been recorded.

- get_file_overall_status: require TERMINAL_STEP to be present
- get_files_processing_status: same guard for bulk status
- get_step_summary: count missing terminal step as queued so
  total_main_steps > main_completed when pipeline is incomplete
- apply_status_filter: SQL sub-query requires terminal step for
  completed filter
- process_document: call initialize_file_steps after creating a new
  file record so all mandatory steps are pre-created as pending

Define TERMINAL_STEP constant in step_manager.py and reference it in
file_status.py and file_queries.py to avoid magic strings.

Tests updated: add send_to_all_destinations to completed-file
fixtures; add test verifying initialize_file_steps is called for
new files.

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-02-27 00:51:14 +00:00
parent 515f5b97e9
commit e6dd39c27d
7 changed files with 165 additions and 12 deletions
+23 -1
View File
@@ -29,6 +29,10 @@ OPTIONAL_PROCESSING_STEPS = {
"check_for_duplicates": settings.enable_deduplication, # Only if deduplication is enabled
}
# The terminal step is the last mandatory step in the processing pipeline.
# A file is only considered "completed" once this step has been recorded.
TERMINAL_STEP = "send_to_all_destinations"
# Combine steps based on configuration
MAIN_PROCESSING_STEPS = []
if settings.enable_deduplication:
@@ -252,7 +256,14 @@ def get_file_overall_status(db: Session, file_id: int) -> Dict:
elif in_progress_steps > 0:
status = "processing"
elif completed_steps + skipped_steps == total_steps:
status = "completed"
# Only mark as completed if the terminal processing step has been recorded.
# Without this guard, files where later pipeline steps have not yet started
# would be falsely marked as "completed" (e.g. only the first 3 steps ran).
existing_step_names = {s.step_name for s in steps}
if TERMINAL_STEP in existing_step_names:
status = "completed"
else:
status = "pending"
else:
status = "pending"
@@ -340,6 +351,17 @@ def get_step_summary(db: Session, file_id: int) -> Dict:
main_counts[status] += 1
main_steps_count += 1
# Ensure the terminal step is always counted in total_main_steps.
# If the terminal step has not been recorded yet, the pipeline is not
# complete; counting it as "queued" prevents the status banner from
# showing "Completed" before the full pipeline has run.
# Note: if TERMINAL_STEP already appears in `steps`, the loop above has
# already incremented `main_steps_count` for it, so we only add here when
# the step is absent from the DB entirely.
if not any(step.step_name == TERMINAL_STEP for step in steps if step.step_name in REAL_MAIN_STEPS):
main_steps_count += 1
main_counts["queued"] += 1
return {
"main": main_counts,
"uploads": upload_counts,