# Dual-Message Lifecycle Contract

<!-- auto-oncall-optimization-record
{
  "schemaVersion": "auto-oncall-optimization-record-v1",
  "optimizationId": "dual-message-lifecycle-contract-2026-08-12",
  "problems": [
    {
      "problemId": "paired-response-lifecycle-is-not-canonical",
      "summary": "The customer and internal messages can stop being one canonical answer across finalization, answer identity, replay, evaluation, optimizer context, and historical Jira write-back, allowing an explicit customer request to remain internal_only or one audience's message to be lost.",
      "status": "optimized_verified",
      "issueKeys": ["APID-29325", "ST-2881"],
      "sourceRefs": [
        "https://tidb.atlassian.net/browse/APID-29325#problem:paired-response-lifecycle-is-not-canonical",
        "https://tidb.atlassian.net/browse/ST-2881#problem:paired-response-lifecycle-is-not-canonical"
      ],
      "signalTextIncludesAny": [
        "customer request remains internal_only",
        "paired response lifecycle is not canonical",
        "answer fingerprint ignores internal message",
        "replay or optimizer loses internal message",
        "对客反问仍是 internal_only",
        "双消息没有成对持久化",
        "对内消息未进入 replay 或 optimizer"
      ],
      "observedThrough": "2026-08-12",
      "effectiveAt": "2026-08-12T07:32:19Z",
      "verifiedAt": "2026-08-12T07:51:50Z",
      "attemptedAction": "Define one dual-message lifecycle contract and make Final Response, structural L1 fallback, paired answer fingerprinting and persistence, Jira replay/write-back, Daily/Recent Evaluation, and Single-ticket Optimizer consume the same customer/internal answer pair without changing diagnosis or Provider behavior.",
      "evidenceUrl": "https://github.com/tidbcloud/aisupport/blob/main/auto-oncall-services/docs/opts/2026-08-12-dual-message-lifecycle-contract.md"
    }
  ]
}
-->

- Optimization ID: `dual-message-lifecycle-contract-2026-08-12`
- Owner: Final Response and downstream paired-response consumers
- Source references: `APID-29325`, `ST-2881`, 2026-08-12 end-to-end review
- Implementation status: deployed to staging
- Effective status: `optimized_verified`

## Bounded problem

`customerReplyMarkdown` and `internalHandoffMarkdown` are intended to describe one
answer for two audiences, but the lifecycle did not have one independent
contract. Individual consumers could version, persist, replay, evaluate, or
write only part of the pair. A customer request expressed in the final text
could also disagree with the typed clarification state and remain
`internal_only`.

This downstream lifecycle gap was identified after the case-specific typed
clarification and Jira paired-delivery verification recorded in
`2026-08-12-l1-clarification-dual-message.md`. That earlier verified status
remains valid for its bounded cases; it is not evidence that these additional
consumers preserve the pair.

This record covers only the paired-response producer/consumer boundary. It does
not change diagnosis, Provider routing, SQL/Slow Query selection, evidence
acquisition, publication cohorts, or production-operation authority.

## Candidate change

- Make `docs/DUAL_MESSAGE_CONTRACT.md` the required lifecycle contract and
  mount it from `AGENTS.md`.
- Treat any actual request for customer-supplied context as
  `human_required`, while allowing a resolved classifier-time gap with no
  final question to remain automatic.
- Preserve and version both messages together through persistence, replay,
  Jira delivery, Daily/Recent Evaluation, and Single-ticket Optimizer context.
- Reuse the original historical internal comment during write-back instead of
  creating a second comment from a current default trigger version.

## Verification

- Source PR [#822](https://github.com/tidbcloud/aisupport/pull/822) and
  app-delivery PRs
  [#76999](https://github.com/tidbcloud/app-delivery/pull/76999) and
  [#77002](https://github.com/tidbcloud/app-delivery/pull/77002) were merged.
  Both staging Deployments run image `ec638946`; the runtime and Optimizer Pods
  are Ready with zero restarts. The live database contains
  `clarification_simulation_json`.
- `APID-29325` read replay `981c5854-e041-4d9d-81ce-e0954165a1e8`
  kept the internally owned lookup gap out of the customer request and returned
  a complete pair with `internal_only`. Write replay
  `894268db-3227-433c-a1f1-2e70acaa624f` persisted paired fingerprint
  `bd7897a1...ddd19` and updated existing comment `23506981`.
- `ST-2881` read replay `de80f0ce-4aeb-4b53-98b9-4cb1789e2193`
  asked one customer-owned configuration question and returned
  `human_required` with clarification required. Write replay
  `5c188b04-8c22-4dc8-904d-5106cea2f1f1` persisted paired fingerprint
  `28c09c18...fbe1` and updated existing comment `23502445`; the historical and
  current idempotency mappings resolve to that same comment rather than a
  duplicate.
- Control replay `APID-29304` run
  `d4a7e3f2-d254-4018-9777-ddecce54d32f` contained no customer question and
  remained `internal_only`.
- Both Jira comments were read back by exact ID as `public=false` and contain
  separate customer and internal sections. The latest-run API returned both
  messages, the paired fingerprint, clarification state, requested context,
  execution state, and the same Jira comment ID for both written runs.
- Source CI passed all 759 TypeScript tests plus Optimizer Go test, race, vet,
  and build checks. The deployed Optimizer uses the same `ec638946` image and
  its analysis base URL targets the deployed runtime. No autonomous repair job
  was started merely to re-test the read-only context loader.

## Next validation action

Monitor later customer-clarification and internal-handoff cases for regression.
The next scheduled Daily/Recent Evaluation and any naturally triggered
Single-ticket Optimizer job should retain both messages; neither requires a
synthetic repair trigger after this case-specific verification.
