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
+17 -3
View File
@@ -11,6 +11,7 @@ from sqlalchemy import or_
from sqlalchemy.orm import Query, Session
from app.models import FileProcessingStep, FileRecord
from app.utils.step_manager import TERMINAL_STEP
def apply_status_filter(query: Query, db: Session, status: Optional[str]) -> Query:
@@ -98,6 +99,9 @@ def apply_status_filter(query: Query, db: Session, status: Optional[str]) -> Que
query = query.filter(FileRecord.id.in_(db.query(subq.c.file_id)))
elif status == "completed":
# Files where all real steps are either success or skipped (no failures or in_progress)
# and the terminal send_to_all_destinations step has been recorded.
# Excluding the terminal-step requirement allows files that only completed the
# first few pipeline stages to be falsely labelled as "completed".
# Exclude duplicates from completed
query = query.filter(FileRecord.is_duplicate.is_(False))
@@ -113,9 +117,19 @@ def apply_status_filter(query: Query, db: Session, status: Optional[str]) -> Que
.subquery()
)
# Select files with real steps that don't have issues
query = query.filter(FileRecord.id.in_(db.query(files_with_real_steps.c.file_id))).filter(
~FileRecord.id.in_(db.query(files_with_issues.c.file_id))
# Get files that have the terminal processing step recorded
files_with_terminal_step = (
db.query(FileProcessingStep.file_id)
.filter(FileProcessingStep.step_name == TERMINAL_STEP)
.distinct()
.subquery()
)
# Select files with real steps, no issues, and terminal step present
query = (
query.filter(FileRecord.id.in_(db.query(files_with_real_steps.c.file_id)))
.filter(~FileRecord.id.in_(db.query(files_with_issues.c.file_id)))
.filter(FileRecord.id.in_(db.query(files_with_terminal_step.c.file_id)))
)
elif status == "duplicate":
# Files marked as duplicates