# Auto Analysis Poll Dedupe And Age Gate

## Current Design

- The poller searches active Jira issues by `created >= cursor - overlap`.
- Q&A gate runs before enqueue.
- Scenario classification and auto-coverage run before `enqueueInitialJob`.
- `enqueueInitialJob` deduplicates by `(issue_key, trigger_kind, trigger_version)`.
- Worker-side L1 oncaller gate runs after a job is claimed and before Codex live analysis.
- Jira JQL date literals do not carry an offset. The poller stores cursor values
  as UTC `Date`s, but Jira interprets JQL literals in the Jira account/site
  timezone.

## Risks

- Jira overlap or a small batch size can make the same issue appear in multiple poll rounds.
- Repeated appearances currently re-run local scenario classification before DB dedupe.
- A newly created issue may be processed before the customer has had the 5-minute supplement window.
- Advancing the cursor over a too-young issue can make it miss the later age gate.
- Formatting a UTC cursor directly as a Jira JQL literal can make Jira search a
  different time window than the backend cursor comparison uses.
- With a small batch size, stale overlap issues that were already created before the
  stored cursor can starve later candidates.

## Target Contract

- Poller must skip issues younger than `AUTO_ONCALL_MIN_ISSUE_AGE_MINUTES`, default `5`, before Q&A, scenario, coverage, or enqueue.
- Too-young issues must not advance the poll cursor.
- Poller must check for an existing initial job for the same `issueKey + triggerVersion` before scenario classification.
- Existing jobs are counted as duplicates and do not re-run scenario classification or coverage.
- Jira JQL literals must be formatted in an explicit Jira timezone,
  `AUTO_ONCALL_JIRA_JQL_TIMEZONE`, defaulting to `Asia/Shanghai`.
- Overlap candidates with `created <= cursor` are stale for a successful prior
  poll and must not consume the current batch.
- Existing tickets created before the cursor must not be enqueued just because
  Jira updates them later.
- Except for too-young issues, processed current candidates must allow cursor
  forward progress through skipped Q&A, duplicate, auto-coverage skipped, or
  enqueued outcomes.
- No LLM classifier is introduced in this change.
- Worker retry, skip, success, Jira internal comment, and L1 oncaller gates remain unchanged.

## Implementation Steps

1. Add `minIssueAgeMinutes` to auto-analysis config.
2. Add a store lookup for existing initial jobs by issue key and trigger version.
3. Reorder poll processing: done check, age gate, Q&A gate, DB duplicate pre-check, scenario/coverage, enqueue.
4. Add tests for young issue cursor behavior and duplicate pre-check before scenario coverage.
5. Add an explicit Jira JQL timezone config and format JQL literals in that timezone.
6. Over-fetch bounded Jira results when a cursor exists, skip stale overlap
   candidates, and apply the configured batch size only to current candidates.
7. Document the new env var.

## Verification Checklist

- Unit tests cover default age config and env override.
- Poll test proves a too-young issue is skipped without cursor advancement.
- Poll test proves a too-young issue stops the current poll from advancing past it.
- Poll test proves a stale overlap issue cannot starve a later current candidate.
- Poll test proves an old created ticket with a newer Jira update is not enqueued.
- Poll test proves existing jobs are duplicates before scenario-based coverage can enqueue.
- Auto-analysis tests pass.
