# L1 Oncaller Gate Plan

Date: 2026-07-06

## Current Design

The Jira poller fetches active issues and enqueues jobs. The worker fetches the
current Jira issue, normalizes it into a `SupportCase`, runs live Codex
analysis, writes or updates one Jira internal comment, then marks the job
completed and sends a Feishu notification.

Current Jira normalization reliably exposes `assignee` and `reporter` as person
fields. Existing custom fields in the fetch list are used for org, cluster,
tenant, and raw alert data; they are not known oncaller person fields.

## Risks

- Running live analysis before confirming the oncaller can create an internal
  comment for a non-L1 owner.
- Treating `reporter` as the owner can allow customer/requester identity to pass
  the gate incorrectly.
- Unknown Jira custom fields can be option objects rather than users.
- If department membership cannot be confirmed, allowing the job would violate
  the L1-only automation boundary.

## Target Contract

- `assignee` is the canonical Jira oncaller source.
- Configured Jira custom fields in `AUTO_ONCALL_JIRA_ONCALLER_FIELD_IDS` are
  secondary oncaller sources when they normalize to user objects.
- `reporter` is normalized for context but is never an oncaller source.
- A job is allowed only when the oncaller matches an allowed email, an allowed
  Feishu open ID, an explicitly configured department path, or an email
  membership hit from the configured Feishu L1 department IDs.
- Unknown, missing, or unconfirmed oncaller identity marks the job `skipped`
  before creating a workspace, calling Codex, or writing Jira comments.
- The Feishu organization lookup boundary is a read-only Contact API client
  that recursively enumerates configured L1 departments and checks whether the
  Jira oncaller email appears in their member lists. It can still be injected in
  tests.
- Missing Contact API permission or Contact API request failure is treated as a
  worker failure so it can retry and alert instead of being mislabeled as a
  non-L1 ticket.
- Successful directory lookups that still do not find the oncaller email in a
  configured allowed department, or explicitly allowed user, are treated as
  unconfirmed and skipped.

## Implementation Steps

1. Normalize Jira person fields with field provenance.
2. Add an oncaller gate module with env-based allowlists and injectable
   directory lookup.
3. Add a live Feishu Contact API directory client that lists configured
   departments and child departments, then matches department member email
   addresses against the Jira oncaller email.
4. Invoke the gate in the auto-analysis worker immediately after Jira fetch and
   `SupportCase` normalization.
5. Add tests for allow, skip, custom field, injected directory, and live Contact
   API client decisions.
6. Document the gate contract and env variables.

## Verification Checklist

- `npm run typecheck`
- `npm test`
- Confirm non-L1 skip tests do not call runner or Jira comment POST.
- Confirm skipped oncaller jobs are not reported as `succeeded`.
