Skip to content

feat(analyzer): add attribution fields to queue windows#153

Merged
bryangingechen merged 7 commits intomasterfrom
opened-by-closed-by-0
Apr 4, 2026
Merged

feat(analyzer): add attribution fields to queue windows#153
bryangingechen merged 7 commits intomasterfrom
opened-by-closed-by-0

Conversation

@bryangingechen
Copy link
Copy Markdown
Contributor

A new QueueWindowEventType TextChoices enum:

Value FK column populated Notes
REQUIRED_LABEL_ADDED timeline_event A required label was added
REQUIRED_LABEL_REMOVED timeline_event A required label was removed
FORBIDDEN_LABEL_ADDED timeline_event A forbidden label was added
FORBIDDEN_LABEL_REMOVED timeline_event A forbidden label was removed
CI_PASSED check_run or status_context CI state flipped to eligible
CI_FAILED check_run or status_context CI state flipped to ineligible
PR_OPENED timeline_event PR reopened (or opened fresh)
DRAFT_CONVERTED timeline_event PR converted from draft to ready
CONVERTED_TO_DRAFT timeline_event PR converted to draft
PR_CLOSED timeline_event PR closed or merged
HEAD_PUSHED timeline_event Force-push; resets CI state for new SHA
INITIAL_STATE (all null) PR was already eligible at window start
RULESET_EFFECTIVE (all null) Ruleset effective_from boundary reached
UNKNOWN (all null) Defensive fallback; should not appear in practice

At most one of the three FK columns is non-null for any open/close event; which one is set
is determined by event_type.

New Fields on PRQueueWindow

opened_by_event_type     = CharField(max_length=32, null=True, choices=QueueWindowEventType.choices)
opened_by_timeline_event = ForeignKey("syncer.PRTimelineEvent",   null=True, on_delete=SET_NULL, related_name="+")
opened_by_check_run      = ForeignKey("syncer.CommitCheckRun",     null=True, on_delete=SET_NULL, related_name="+")
opened_by_status_context = ForeignKey("syncer.CommitStatusContext",null=True, on_delete=SET_NULL, related_name="+")
opened_at_head_sha       = CharField(max_length=40, null=True)

closed_by_event_type     = CharField(max_length=32, null=True, choices=QueueWindowEventType.choices)
closed_by_timeline_event = ForeignKey("syncer.PRTimelineEvent",   null=True, on_delete=SET_NULL, related_name="+")
closed_by_check_run      = ForeignKey("syncer.CommitCheckRun",     null=True, on_delete=SET_NULL, related_name="+")
closed_by_status_context = ForeignKey("syncer.CommitStatusContext",null=True, on_delete=SET_NULL, related_name="+")
closed_at_head_sha       = CharField(max_length=40, null=True)

closed_by_* and closed_at_head_sha are null for windows that are still open (to_ts = None).

All FK columns use on_delete=SET_NULL. If a referenced row is deleted out-of-band the FK
silently becomes null; the rebuild-on-deletion mechanism (see below) ensures this state is
transient in practice.

prepared with claude code

bryangingechen and others added 7 commits April 4, 2026 00:30
Captures the design for adding opened_by_*/closed_by_* fields to
PRQueueWindow, the QueueWindowEventType enum, the head SHA fields,
the invariants around rebuild-on-deletion in the CI expire task, and
operational notes for deploying against an existing install.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…QueueWindow

Ten new nullable fields track what caused each queue window to open/close:
opened/closed_by_event_type (discriminator enum), _timeline_event,
_check_run, _status_context FKs (SET_NULL), and _at_head_sha.
Migration 0025 is pure ADD COLUMN with no backfill; safe on live DB.
Updates doc 038 to note the new FK dependency from PRQueueWindow.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ows_with_rules

Adds WindowAttribution and AttributedQueueWindow dataclasses. Both the
label-only and CI paths in _queue_windows_with_rules now produce fully
attributed windows (event type, FK, head SHA for open and close).
Adds _timeline_event_type and _determine_ci_boundary_attribution helpers.
queue_windows_for_pr strips attribution for backward compatibility.

Tests in test_queue_window_attribution.py cover 18 cases across the
label-only and CI paths. Attribution not yet persisted to DB (W3).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…dows_for_ruleset

Adds _attribution_kwargs helper to unpack WindowAttribution into model
field kwargs with FK-id comparison to avoid redundant fetches. Passes
attribution through bulk_create and bulk_update paths. Pre-migration
windows with null event_type are overwritten on next rebuild.

Tests in test_queue_window_attribution_persist.py cover all FK variants,
PR_CLOSED, HEAD_PUSHED, re-rebuild overwrite, and pre-migration rows.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ttribution backfill

Expire task: adds _invalidate_queue_windows_for_ci_rows helper that marks
PRQueueWindowBuildState stale and enqueues process_pr_task before each of
the four CI deletion passes. Extends return dict with prs_invalidated_*
counts. Tests in test_expire_stale_ci_attribution.py (6 cases).

Sweep: adds attribution_backfill Exists subquery detecting pre-migration
windows (opened_by_event_type IS NULL) and post-expire-task partial
failures (CI event_type with both CI FKs null). Threads has_attribution_
backfill through annotation, prefilter, batch query, and _is_ruleset_
stale_for_pr. Updates collect_convergence.py to match. Fixes two
pre-existing tests whose bare PRQueueWindow fixtures were now correctly
detected as stale. Tests in test_rebuild_queue_windows_sweep_attribution
.py (4 cases).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… sweep/convergence tests

The new attribution_backfill staleness condition added in W4b correctly
detects PRQueueWindow rows with null opened_by_event_type as pre-migration
and triggers rebuilds. Eight existing tests that created bare
PRQueueWindow objects (simulating up-to-date windows) were spuriously
triggering this condition, causing unexpected rebuilds or inflated stale
counts.

Fixed by adding opened_by_event_type=QueueWindowEventType.INITIAL_STATE
to the relevant window creates in test_rebuild_queue_windows_sweep_task.py
(5 tests) and test_collect_convergence_task.py (3 tests).

Also includes ruff formatting fixes to zulip_bot test files.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Removes chunk-by-chunk rollout notes; captures final schema, invariants
(I1-I7), attribution computation logic, expire-task obligation, sweep
staleness extension, and all deployment subtleties including the
UNKNOWN/deletion distinction and mutable check run FKs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@bryangingechen bryangingechen merged commit 0e2aef5 into master Apr 4, 2026
4 checks passed
@bryangingechen bryangingechen deleted the opened-by-closed-by-0 branch April 4, 2026 13:57
bryangingechen added a commit that referenced this pull request Apr 4, 2026
Add the opened_by_* and closed_by_* fields added in #153 to
list_display, list_filter, raw_id_fields, and readonly_fields.
Also add the previously missing duration/rollup fields to readonly_fields.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant