Skip to content

FP lane gaining missed-smoke stays annotated with no boxes, and exports #355

Description

@Chouffe

An FP lane sitting at annotated can gain missed-smoke — and therefore start needing localization — without changing stage, so neither the localization guard nor the FP→smoke demotion fires. The lane ends up at annotated, needing localization, with zero boxes: on /localize/done and in /export/alerts.

This is the state #346 set out to prevent. PR #352 closed every path into annotated; this one never crosses that boundary.

Reproduced

SEEDED:      [('annotated', {'annotation': []})]   # FP lane's empty placeholder
PATCH {"has_missed_smoke": true}
PATCH status: 200
RESULT stage: annotated
RESULT has_missed_smoke: True
IN /localize/done: True
IN /export/alerts: True

Why both guards miss it

  • The localization guard (sequence_annotations.py, assert_localization_complete) requires the stage to be changing into annotated. Here target_processing_stage == existing.processing_stage == ANNOTATED, so it is skipped. Widening it would not help either: the FP exit already wrote empty annotated-stage detection annotations via auto_create_detection_annotations, so assert_localization_complete would pass on those rows.
  • The FP→smoke promotion (is_fp_promotion) requires target_processing_stage == SEQ_ANNOTATION_DONE. A flags-only PATCH leaves the stage alone, so the demotion — which would delete those empty rows and re-arm auto-annotate — never runs.

Reachable from the UI, not just the API

frontend/src/pages/LocalizeAlertPage.tsx sends exactly this PATCH from the missed-smoke row:

apiClient.updateSequenceAnnotation(annotationId, { has_missed_smoke: value })

and missedSmokeAnnotationId falls back to "the first lane that has an annotation at all", which can be the FP lane. The row renders in done mode.

Suggested fix

Key is_fp_promotion on the flag transition rather than the target stage: a lane at annotated that did not previously need localization but does after this edit should demote to seq_annotation_done, whatever stage the payload names. That is the intent issue #275 already established — the current predicate just happens to detect it only when the client also sends the new stage.

Worth confirming as part of that work: a flags-only PATCH would then change processing_stage without the client asking, which callers should expect.

Found by review of #352; not a regression (the same gap existed before it, via the existing == SEQ_ANNOTATION_DONE form of the guard).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions