Skip to content

feat(sequences): re-run cone matching when relabeling back to wildfire_smoke#585

Open
MateoLostanlen wants to merge 6 commits into
mainfrom
feat/relabel-wildfire-reattach
Open

feat(sequences): re-run cone matching when relabeling back to wildfire_smoke#585
MateoLostanlen wants to merge 6 commits into
mainfrom
feat/relabel-wildfire-reattach

Conversation

@MateoLostanlen

Copy link
Copy Markdown
Member
  • Context: when a sequence is mis-labeled as non-wildfire (e.g. other_smoke), it's detached from its triangulating alert and a fresh single-sequence alert is created. Reverting the label to wildfire_smoke previously left the sequence stuck in that lonely alert, breaking triangulation. This PR makes the revert path drop the lonely alert and re-run cone matching so the sequence merges back with its overlapping neighbours.
  • Anchor the candidate-sequence window on the sequence's own last_seen_at (not utcnow()) so relabels done hours later still find the original neighbours within SEQUENCE_RELAXATION_SECONDS.
  • Rename _attach_sequence_to_alertattach_sequence_to_alert and extract _detach_sequence_from_alerts to deduplicate the detach/refresh block.

@codecov

codecov Bot commented May 5, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.23810% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 92.31%. Comparing base (00743b2) to head (c5b1820).

Files with missing lines Patch % Lines
src/app/api/api_v1/endpoints/sequences.py 94.11% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #585      +/-   ##
==========================================
+ Coverage   92.30%   92.31%   +0.01%     
==========================================
  Files          56       56              
  Lines        2585     2603      +18     
==========================================
+ Hits         2386     2403      +17     
- Misses        199      200       +1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@MateoLostanlen MateoLostanlen requested review from Acruve15 and fe51 May 14, 2026 07:22
@MateoLostanlen MateoLostanlen marked this pull request as ready for review May 14, 2026 07:22
@Acruve15

Copy link
Copy Markdown
Collaborator

It's an interesting PR, thx! I can do a thorough review by Monday if it's ok for you.

I want to make sure I understand what happens with the triangulation and how alerts/sequences/detections concepts are linked :)

Resolve label_sequence conflict by combining both changes: keep main's
sibling-check for the non-wildfire path (skip detach/recreate when the
sequence is alone in its alert, avoiding alert-id churn) and add this
branch's revert-to-wildfire path (detach + re-run cone matching anchored
on the sequence's own last_seen_at). Keep both unit tests.
@Acruve15

Acruve15 commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

@MateoLostanlen I'll review by EOD

@Acruve15 Acruve15 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @MateoLostanlen, I added some comments, the most important one on how to handle orphan alerts :)

Can be helpful, here is the current flow for context:

sequenceDiagram
    autonumber
    participant U as User
    participant L as label_sequence
    participant CM as cone matching
    participant A as AlertSequence

    Note over U,A: T0: Alert#100 triangulates {seq_B, seq_C}

    U->>L: label seq_B OTHER_SMOKE
    L->>A: detach seq_B from Alert#100
    L->>A: create lonely Alert#101 = {seq_B}
    Note over A: Alert#100 = {seq_C}, Alert#101 = {seq_B}

    Note over U,A: T1 (later): user reconsiders
    U->>L: label seq_B WILDFIRE_SMOKE

    rect rgb(255, 235, 235)
    Note over L,A: pre-PR: no-op, seq_B stuck in Alert#101 forever
    end

    rect rgb(235, 250, 235)
    L->>A: detach seq_B from Alert#101 (deleted, empty)
    L->>CM: anchor on seq_B.last_seen_at
    alt overlap found
        CM-->>A: link seq_B to Alert#100
        Note over A: Alert#100 = {seq_B, seq_C} again
    else no overlap (orphan risk)
        CM-->>A: returns None, no link created
        Note over A: seq_B has zero alert rows
    end
    end
Loading

Longer term: I was thinking we could soft-delete AlertSequence rows (with unlinked_at timestamps) would give us audit history for free and is useful when debugging "why did this alert lose triangulation?"?

camera = cast(Camera, await cameras.get(sequence.camera_id, strict=True))
# Anchor the candidate window on the sequence's own time so old relabels still merge
await attach_sequence_to_alert(
updated, camera, cameras, sequences, alerts, reference_time=sequence.last_seen_at

@Acruve15 Acruve15 Jun 10, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency?

Suggested change
updated, camera, cameras, sequences, alerts, reference_time=sequence.last_seen_at
updated, camera, cameras, sequences, alerts, reference_time=updated.last_seen_at

await _detach_sequence_from_alerts(sequence_id, session, alerts)
camera = cast(Camera, await cameras.get(sequence.camera_id, strict=True))
# Anchor the candidate window on the sequence's own time so old relabels still merge
await attach_sequence_to_alert(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this returns None (eg no neighbour in the window or cones don't overlap?) , the sequence has zero alert links. I suggest adding a fallback to make sure we don't have orphan alters.

alert_id = await attach_sequence_to_alert(
    updated, camera, cameras, sequences, alerts, reference_time=sequence.last_seen_at
)
if alert_id is None:
    new_alert = await alerts.create(
        AlertCreate(
            organization_id=camera.organization_id,
            started_at=sequence.started_at,
            last_seen_at=sequence.last_seen_at,
            lat=None,
            lon=None,
        )
    )
    session.add(AlertSequence(alert_id=new_alert.id, sequence_id=sequence_id))
    await session.commit()

If orphaning is the intended behaviour, a one-line comment is enough for me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants