# Incident Todo Action TCOCP Sync

## Table of Contents

- [Incident Todo Action TCOCP Sync](#incident-todo-action-tcocp-sync)
  - [Table of Contents](#table-of-contents)
  - [Introduction](#introduction)
  - [Motivation or Background](#motivation-or-background)
  - [Detailed Design](#detailed-design)
    - [Scope](#scope)
    - [Current State](#current-state)
    - [Target Workflow](#target-workflow)
    - [TCOCP Issue Detection](#tcocp-issue-detection)
    - [TCOCP Issue Creation](#tcocp-issue-creation)
    - [Todo Action Persistence](#todo-action-persistence)
    - [Owner Notification](#owner-notification)
    - [Scheduled Status Sync](#scheduled-status-sync)
    - [Overdue Reminder](#overdue-reminder)
    - [Feishu Task Deprecation](#feishu-task-deprecation)
    - [Rollout Plan](#rollout-plan)
  - [Test Design](#test-design)
    - [Functional Tests](#functional-tests)
    - [Scenario Tests](#scenario-tests)
    - [Compatibility Tests](#compatibility-tests)
  - [Impacts & Risks](#impacts--risks)
  - [Unresolved Questions](#unresolved-questions)

## Introduction

This proposal changes incident postmortem todo action follow-up from Feishu Task based tracking to TCOCP Jira issue based tracking. Importing action items from a Feishu postmortem document should still create local todo actions, but it should no longer create Feishu Tasks. Each todo action should be linked to exactly one TCOCP issue in `linked_jira_issue`, and scheduled jobs should sync the todo action status from that TCOCP issue.

## Motivation or Background

The current incident todo action import flow creates both a local `incident_postmortem_todoaction` row and an associated Feishu Task. The scheduled overdue reminder job then syncs action status from Feishu Task completion state before sending reminders.

The target operating model no longer uses Feishu Tasks for production incident todo actions. Instead, follow-up work should be represented by JSM issues in the TCOCP project. This gives the workflow a Jira-native source of truth, keeps incident remediation visible in the service desk process, and avoids maintaining a second task source in Feishu.

The product requirements confirmed so far are:

- Import still creates todo actions.
- Import must not create Feishu Tasks.
- If an imported action has no linked Jira issue, create a TCOCP issue.
- If an imported action already has a TCOCP issue, do not create another TCOCP issue.
- If an imported action has only non-TCOCP Jira links, create a new TCOCP issue and place the previous Jira link information in the TCOCP description.
- After TCOCP creation, `linked_jira_issue` should store only the TCOCP issue.
- Scheduled sync should use TCOCP status only.
- Existing `feishu_task_id` values do not need migration or cleanup.

## Detailed Design

### Scope

In scope:

- Change Feishu postmortem import so it no longer creates associated Feishu Tasks.
- Add TCOCP issue creation after todo action creation when needed.
- Normalize `linked_jira_issue` to the selected TCOCP key.
- Send owner notification after a TCOCP issue is created.
- Replace scheduled Feishu Task status sync with TCOCP Jira status sync.
- Update overdue reminder cards to reference TCOCP instead of Feishu Task.

Out of scope for this document:

- Final cardkit template IDs and variable names.
- Migration or deletion of historical Feishu Tasks.
- Support for multiple TCOCP issues on one todo action.

### Current State

`CreatePostMortemToDoActionsFromFeishuDoc()` extracts action items from a Feishu postmortem document and calls `CreateIncidentPostMortemToDoActionItem()` with `CreateAssociatedLarkTask: true`.

`CreateIncidentPostMortemToDoActionItem()` creates a Feishu Task when `CreateAssociatedLarkTask` is true and stores the returned task id in `feishu_task_id`.

`NotifyOverdueToDoActions()` currently calls `SyncAllToDoActionStatusFromFeishu()` before querying overdue actions. The sync path loads rows with `feishu_task_id`, reads each Feishu Task, and updates the todo action title, description, deadline, and status.

The overdue card parameters include `feishu_task_link`, and the helper builds a Feishu Task URL from `feishu_task_id`.

### Target Workflow

The new import workflow should be:

```text
Feishu postmortem doc
  -> extract action items
  -> create local todo action
  -> inspect extracted linked_jira_issue
  -> create or reuse TCOCP issue
  -> update todo action linked_jira_issue to the TCOCP issue
  -> notify each owner with a TCOCP-created card
```

For each extracted action:

1. Strip Lark mentions from title and description.
2. Resolve owners to user emails as the current flow does.
3. Create the local todo action with `CreateAssociatedLarkTask` disabled.
4. Determine whether `linked_jira_issue` already contains a TCOCP issue.
5. If no TCOCP issue exists, create one.
6. Persist only the TCOCP key in `linked_jira_issue`.
7. Send owner notification after the TCOCP issue is available.

The todo action should remain the local record used by the incident page. TCOCP is the external source of truth for follow-up execution status.

### TCOCP Issue Detection

Add a helper to parse linked Jira issue text:

```go
type linkedJiraIssues struct {
    TCOCP    []string
    TCOC     []string
    NonTCOCP []string
}

func parseLinkedJiraIssues(raw *string) linkedJiraIssues
```

The parser should accept both plain keys and URLs. Examples:

- `TCOCP-123`
- `https://tidb.atlassian.net/browse/TCOCP-123`
- `PROJ-1, TCOCP-123`
- Multi-line cells extracted from Feishu document tables

Detection should be based on Jira key pattern and the `TCOCP-` prefix. If multiple TCOCP keys are present, the first one should be selected for the todo action and the rest should be logged as unsupported input. The implementation should not create an additional TCOCP issue when any TCOCP key is already present.

### TCOCP Issue Creation

Add an incident service helper:

```go
func (s *incidentService) ensureTCOCPIssueForToDoAction(
    ctx context.Context,
    action *entity.IncidentPostMortemToDoAction,
    incident *models.Incident,
    originalLinkedJiraIssue *string,
) (*tcocpIssue, error)
```

Behavior:

- If `originalLinkedJiraIssue` contains a TCOCP issue, return that issue and do not create a new one.
- If it contains only non-TCOCP Jira issues, create a TCOCP issue and include those original Jira references in the TCOCP description.
- If it is empty, create a TCOCP issue without previous Jira references.
- After a TCOCP issue is resolved or created, update `incident_postmortem_todoaction.linked_jira_issue` to only the TCOCP key.

The Jira creation client should be implemented through the existing Jira client/service layer rather than direct ad hoc HTTP code. Verified against Jira metadata from `TCOCP-540`: the TCOCP space is Jira project `TCOCP` (`TCOC-Postmortem`), and work type is represented by Jira issue type.

The TCOCP issue should be created in Jira project `TCOCP`, using one of these work types as the Jira issue type:

- `Mitigation SOP`
- `New alert/metric/log`
- `Operation SOP`
- `Healthcheck`
- `ConfigChange`

The LLM import prompt should classify each action item into exactly one of these work types based on the action item content.

The TCOCP description should include at least:

- Todo action title.
- Todo action description.
- Deadline.
- Owner emails.
- Incident link.
- Previous non-TCOCP Jira references, when present.

The TCOCP issue fields should be:

- `Summary`: action item content.
- `Description`: incident context, incident Ops Portal URL, postmortem document URL, and related non-TCOCP JSM references.
- `Status`: initial Jira workflow status should be `TO DO`.
- `Assignee`: action owner, resolved to Jira account by email when possible.
- `Labels`: `incident-{incident_id}` and `postmortem-action-item`.
- `Due date`: action deadline.
- `ETA`: action deadline.
- `Sync to ONCALL`: `yes` (`customfield_10060`).
- `From TCOC`: `Yes` (`customfield_13567`) only when the incident `Jira Issues` field contains at least one `TCOC-*` issue.
- `ETA`: action deadline (`customfield_13303`).
- `Priority`: action priority when present.
- Linked work items: when the incident `Jira Issues` field contains `TCOC-*`, create Jira issue links with API link type `Ticket-Oncall`; from the TCOCP side this displays as `Associated Oncall`. If no incident TCOC issue exists, skip this link.

Because these TCOCP fields are stable project configuration, implementation should write the verified Jira custom field ids directly instead of querying createmeta for every import:

- `ETA`: `customfield_13303`
- `Sync to ONCALL`: `customfield_10060`
- `From TCOC`: `customfield_13567`

### Todo Action Persistence

The existing `linked_jira_issue` column can be reused for the selected TCOCP reference. No schema change is required for the confirmed behavior.

Persistence rules:

- Empty original Jira value -> store created TCOCP.
- Existing TCOCP value -> store existing TCOCP.
- Existing non-TCOCP value -> store created TCOCP only.
- Existing mixed value -> store existing TCOCP only.

Previous non-TCOCP Jira references are not preserved in the todo action row. They are copied into the created TCOCP description only when a new TCOCP issue is created.

### Owner Notification

After TCOCP is available, send each owner a Feishu card. This should use the existing `larkclient.Notify` pattern and a new notification parameter type, for example:

```go
type IncidentToDoActionTCOCPCreatedParam struct {
    ActionTitle  string
    DueTime      string
    IncidentName string
    IncidentLink string
    TCOCPKey     string
    TCOCPLink    string
    OwnerEmail   string
}
```

The card should tell the owner:

- The TCOCP issue was created or linked.
- The DDL for the action.
- The related incident.
- The TCOCP issue link.

If owner resolution fails or an owner has no usable email, skip that owner and log the failure. If card delivery fails, the todo action and TCOCP issue should remain created; the import should return the created todo actions and report notification errors according to the final product expectation. The recommended default is best-effort notification with error logging, because Jira issue creation is the durable side effect.

### Scheduled Status Sync

Replace Feishu Task sync in `NotifyOverdueToDoActions()`:

```go
func (s *incidentService) NotifyOverdueToDoActions(ctx context.Context) error {
    if err := s.SyncAllToDoActionStatusFromTCOCP(ctx); err != nil {
        return fmt.Errorf("sync todo action status from TCOCP before overdue reminder failed: %w", err)
    }
    // existing overdue query and reminder flow
}
```

`SyncAllToDoActionStatusFromTCOCP()` should:

1. Query todo actions whose `linked_jira_issue` contains a TCOCP key and whose status is not terminal.
2. Load the TCOCP issue from Jira.
3. Map Jira status to todo action status.
4. Update the todo action status.

The method should not use `feishu_task_id` and should not call `GetFeishuTask()`.

The status mapping should be isolated in one helper:

```go
func mapTCOCPStatusToToDoActionStatus(jiraStatus string) (string, bool)
```

The boolean should indicate whether the status is known. Unknown statuses should be logged and skipped rather than forcing an incorrect todo action status.

Mapping:

- `TO DO` -> `open`
- `In Progress` -> `in_progress`
- `DONE` -> `done`
- `NONEED` -> `noneed`

### Overdue Reminder

The overdue reminder query can keep its current status and deadline filters, but the card payload should switch from Feishu Task link to TCOCP link.

Replace:

```go
FeishuTaskLink string
```

with:

```go
TCOCPKey  string
TCOCPLink string
```

For owner reminders, include:

- Action title.
- Current todo action status.
- Due time.
- Incident name and link.
- TCOCP key and link.
- Owner email.

For FB owner summaries, each action item should include its TCOCP key/link in addition to title, status, due time, and owners.

### Feishu Task Deprecation

Feishu Task support should be considered deprecated for incident todo action import and scheduled status sync.

Implementation rules:

- Do not create Feishu Tasks during postmortem import.
- Do not call `SyncAllToDoActionStatusFromFeishu()` from the scheduled overdue job.
- Do not migrate, delete, or update existing `feishu_task_id` values.
- Existing manual Feishu Task API endpoints can remain for compatibility unless a separate cleanup task removes them.
- Update import tests to assert that imported todo actions use `CreateAssociatedLarkTask: false`.

### Rollout Plan

Recommended rollout:

1. Stop Feishu Task creation during import.
2. Add TCOCP issue parsing and creation behind the import flow.
3. Add owner notification card once cardkit is available.
4. Add TCOCP status sync and replace the scheduled Feishu sync call.
5. Update overdue reminder card templates to use TCOCP links.
6. Run in dev and verify:
   - Imported actions create or reuse TCOCP correctly.
   - `linked_jira_issue` contains only TCOCP.
   - Scheduled sync updates statuses from Jira.
   - Overdue reminders include TCOCP links.

## Test Design

### Functional Tests

Add unit tests for linked Jira parsing:

- Empty input returns no issues.
- Plain `TCOCP-123` is detected.
- Jira URL containing `TCOCP-123` is detected.
- Non-TCOCP keys are separated.
- Mixed non-TCOCP and TCOCP input selects TCOCP and does not request creation.

Add unit tests for import behavior:

- Import creates todo action with `CreateAssociatedLarkTask: false`.
- Imported action without Jira creates TCOCP and stores TCOCP in `linked_jira_issue`.
- Imported action with TCOCP does not create another TCOCP.
- Imported action with only non-TCOCP Jira creates TCOCP and includes previous Jira references in description.

Add unit tests for status sync:

- TCOCP status maps to `open`, `in_progress`, `done`, or `noneed`.
- Unknown Jira status is logged and skipped.
- Rows without TCOCP are skipped.
- Rows in terminal todo action status are skipped.

### Scenario Tests

Manual or integration scenarios:

- Import a postmortem document where each action has no linked Jira issue.
- Import a document where actions already contain TCOCP links.
- Import a document where actions contain only non-TCOCP Jira links.
- Run the scheduled sync after changing TCOCP status in Jira.
- Run overdue reminder after sync and verify owner and FB owner cards.

### Compatibility Tests

Compatibility checks:

- Existing todo actions with `feishu_task_id` are not modified by TCOCP sync.
- Existing manual create/update/delete todo action API behavior remains unchanged unless it explicitly opts into TCOCP creation later.
- Existing Feishu Task API endpoints still compile and remain callable unless separately deprecated.
- Existing overdue reminder job still skips terminal todo action statuses.
- Existing `linked_jira_issue` values with non-TCOCP Jira are overwritten only when the import flow creates a TCOCP issue for that action.

## Impacts & Risks

Impacts:

- Incident todo action tracking moves from Feishu Task completion to Jira TCOCP status.
- Import becomes responsible for creating durable Jira follow-up work.
- Owner and FB owner reminders will point to TCOCP issues instead of Feishu Tasks.
- `linked_jira_issue` becomes the storage location for the canonical TCOCP issue.

Risks:

- Incorrect TCOCP field mapping could create invalid or incomplete JSM issues.
- Jira API failures during import could partially create todo actions without TCOCP links unless the implementation defines retry or rollback behavior.
- If card notification is treated as a hard failure, imports may fail after successful TCOCP creation.
- If multiple TCOCP keys appear in source text, selecting the first key may not match user intent.
- Overwriting non-TCOCP `linked_jira_issue` values removes them from the todo action row. They are preserved only in the created TCOCP description.
- Unknown Jira statuses can leave local todo action status stale until mapping is updated.

## Unresolved Questions

- Should TCOCP issue creation be atomic with todo action creation, or should todo action creation succeed even if TCOCP creation fails?
- What are the final cardkit template IDs and variable names for TCOCP-created and overdue reminder cards?
- Should existing mixed `linked_jira_issue` values with both TCOCP and non-TCOCP update the existing TCOCP description, or only normalize the local row to TCOCP?
- Should manual todo action creation in the API also auto-create TCOCP, or is auto-creation limited to postmortem import?
