Skip to content

Document ticketing integration patterns and Linear/Jira troubleshooting#171

Draft
samgutentag wants to merge 1 commit into
mainfrom
sam-gutentag/ticketing-integrations
Draft

Document ticketing integration patterns and Linear/Jira troubleshooting#171
samgutentag wants to merge 1 commit into
mainfrom
sam-gutentag/ticketing-integrations

Conversation

@samgutentag
Copy link
Copy Markdown
Member

Reopens the work from #55, which was merged then reverted on 2026-06-01 (it was not ready to merge). Content is unchanged from the original branch; needs content verification against source before re-merging. Reverted merge commit removed from main.

Net-new page consolidating the cross-cutting topics for Linear and Jira
ticketing integrations: button vs webhook vs custom-handler tradeoffs,
the link-back asymmetry between manual and webhook-created tickets,
webhook idempotency (flaky -> healthy -> flaky duplicates), CODEOWNERS
routing not being supported by the built-in connectors, the beta auto-
analyses 100/month rate limit, and Jira-specific troubleshooting for
401 invalid credentials and cross-project link permissions.

Sourced from clustered customer Q&A (cluster_id ticketing-integration-
linear-jira, verdict partial, 10 pairs across 6 customers). Sits as a
sibling page under flaky-tests/management/ticketing/ next to the
existing Linear and Jira integration pages. Linear and Jira pages each
get a small cross-link callout so users hitting these issues find the
new page from where they already are.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@samgutentag
Copy link
Copy Markdown
Member Author

Code verification (2026-06-01): 10 confirmed / 0 contradicted / 0 ambiguous / 1 unverifiable

Claim Verdict Source
Event type is v2.test_case.status_changed confirmed flake-detection/src/types.ts:265
new_status / previous_status use UPPERCASE FLAKY / HEALTHY / BROKEN confirmed flake-detection/src/types.ts:9
Webhook payload nests test_case.id confirmed flake-detection/src/types.ts:326
Webhook payload includes test_case.codeowners confirmed flake-detection/src/types.ts:330
AI analysis event type is test_case.investigation_completed confirmed svix-publish-schemas .../test-case-investigation-completed.ts
POST /flaky-tests/link-ticket-to-test-case endpoint exists confirmed public-api/src/routes_protected.ts:3513
POST /flaky-tests/get-test-details endpoint exists confirmed public-api/src/routes_protected.ts:2996
external_ticket_id is PROJECT_KEY-NUMBER (e.g. ABC-1234) confirmed public-api/src/schema.ts:786
Jira connection returns "Invalid Credentials" on 401 (token must match email's account) confirmed ticketing/.../jira_cloud_integration_client.ts:182
"Unable to find ticket" is the error when a linked/imported ticket cannot be resolved confirmed ticketing/src/service.ts:62
Beta auto-analyses limit is 100/month per workspace unverifiable no numeric constant found in source

No contradictions. Nothing here blocks publishing on accuracy grounds. The one unverifiable item is the "100 auto-analyses per month per workspace" beta cap. It does not contradict any source. I could not find a 100 / per month / monthly-budget constant in the analysis service (trunk/services/flaky-tests-analysis) or the investigation-poller infra (trunk2 ts/apps/infra/.../flaky-tests-investigation-poller). The cap is most likely a business / config / DB-driven limit rather than a hardcoded constant, so absence of a code literal is expected. Recommend confirming the exact number with the Flaky Tests / Autofix team before publishing, since it is a hard number a reader will rely on.

Note on the legacy v1 event (no doc change needed): there is a second, legacy event test_case.status_changed (the v1 monolith event) that uses current_status with lowercase healthy / flaky / broken. The docs correctly use the v2 event and UPPERCASE values, so the new_status === "FLAKY" filter in the docs is accurate. Do not "fix" it to lowercase.


Source #1 — Event type v2.test_case.status_changed (confirmed)

File: trunk-io/trunk2/ts/packages/flake-detection/src/types.ts#L263-L266

export const V2_TEST_CASE_STATUS_CHANGED_SCHEMA = z.object({
  type: z
    .literal("v2.test_case.status_changed")
    .describe("The type of webhook event."),

Reasoning: The base Zod schema in the detection engine pins type to the literal v2.test_case.status_changed. The svix-publish example object also emits type: "v2.test_case.status_changed". This is the v2 detection-engine event the docs subscribe to.

Source #2 — UPPERCASE status enum on new_status / previous_status (confirmed)

File: trunk-io/trunk2/ts/packages/flake-detection/src/types.ts#L9

export const TestCaseStatusSchema = z.enum(["HEALTHY", "FLAKY", "BROKEN"]);

Both fields use this enum:

  previous_status: TestCaseStatusSchema.describe("The previous status of the test case."),
  new_status: TestCaseStatusSchema.describe("The new status of the test case."),

Reasoning: new_status and previous_status are both typed as TestCaseStatusSchema, which is z.enum(["HEALTHY", "FLAKY", "BROKEN"]) — UPPERCASE. The docs' new_status === "FLAKY" and previous_status === "HEALTHY" match exactly. The same UPPERCASE enum is independently re-declared in the public API (public-api/src/schema.ts:794 TEST_CASE_STATUS_ENUM_SCHEMA).

Trap (do not flag): there is a legacy event test_case.status_changed (no v2. prefix) in the same package whose Enriched type uses current_status with lowercase "healthy" | "flaky" | "broken" (detection-engine-webhook-event-handler/src/types.ts:42-51). That is the v1 monolith event, not what the docs use. Do not cite it to contradict the docs.

Source #3test_case.id in webhook payload (confirmed)

File: trunk-io/trunk2/ts/packages/flake-detection/src/types.ts#L326-L333

export const V2_TEST_CASE_WEBHOOK_PAYLOAD_SCHEMA = z.object({
  id: z.string().uuid().describe("A stable unique identifier for the test."),
  name: z.string().describe("The name of the test."),
  classname: z.string().describe("The classname of the test."),
  codeowners: z
    .array(z.string().describe("Code owners for the test."))
    .describe("Code owners associated with the test."),

Reasoning: The enriched event nests this payload under test_case (see ENRICHED_V2_TEST_CASE_STATUS_CHANGED_SCHEMA ... .extend({ test_case: V2_TEST_CASE_WEBHOOK_PAYLOAD_SCHEMA })). So test_case.id is the stable test identifier — exactly what the docs say to store in the ticket for link-back / dedup.

Source #4test_case.codeowners in webhook payload (confirmed)

File: trunk-io/trunk2/ts/packages/flake-detection/src/types.ts#L330-L333

  codeowners: z
    .array(z.string().describe("Code owners for the test."))
    .describe("Code owners associated with the test."),

Reasoning: codeowners is a string[] on the test_case payload. The docs' claim that the payload includes test_case.codeowners but the built-in connectors do not route on it is a behavioral statement about the transformations, not about the field's existence — the field itself is confirmed present.

Source #5 — AI analysis event test_case.investigation_completed (confirmed)

File: trunk-io/trunk2/ts/packages/tools/svix-publish-schemas/src/events/test-case-investigation-completed.ts

  example: {
    type: "test_case.investigation_completed",
    investigation_id: "584b9c41-f660-4940-881f-42abab23d830",
    trigger: "AUTOMATIC",

Reasoning: The investigation-completed event emits type: "test_case.investigation_completed" and carries a trigger field (AUTOMATIC for auto-analysis, manual otherwise). This matches the docs' claim that this is the AI-analysis event and that manual runs still emit it.

Source #6POST /flaky-tests/link-ticket-to-test-case (confirmed)

File: trunk-io/trunk/trunk/services/public-api/src/routes_protected.ts#L3513-L3572

    path: "/flaky-tests/link-ticket-to-test-case",
    fullPath: "/flaky-tests/link-ticket-to-test-case",
    ...
          request: { test_case_id: testCaseId, external_ticket_id: externalTicketId, repo },

Reasoning: The protected route is registered at /flaky-tests/link-ticket-to-test-case and takes test_case_id + external_ticket_id. The docs' link-back / dedup patterns that call this endpoint with the test case ID and a ticket reference are accurate.

Source #7POST /flaky-tests/get-test-details (confirmed)

File: trunk-io/trunk/trunk/services/public-api/src/routes_protected.ts#L2996-L2997

    path: "/flaky-tests/get-test-details",
    fullPath: "/flaky-tests/get-test-details",

Request schema (public-api/src/schema.ts:870):

export const GET_TEST_DETAILS_REQUEST_SCHEMA = z.object({
  repo: REPO_SCHEMA,
  org_url_slug: ORG_SCHEMA,
  test_id: z.string().uuid()...

Reasoning: The endpoint exists and takes a test case ID (request field is test_id, a UUID). The docs reference it by path for the "check whether a ticket is already linked" dedup pattern; this is advisory prose about response inspection, not a field-name claim, and the endpoint/purpose check out.

Source #8external_ticket_id format PROJECT_KEY-NUMBER (confirmed)

File: trunk-io/trunk/trunk/services/public-api/src/schema.ts#L786-L790

  external_ticket_id: z.string().openapi({
    description:
      "The external identifier of the ticket. For Jira this is the ticket number prefixed by the Project Key. For Linear this is the ticket number prefixed by the Team Identifier",
    examples: ["KAN-123", "TRUNK-1234"],
  }),

Reasoning: The schema description states the Jira format is "the ticket number prefixed by the Project Key" with example KAN-123 — i.e. PROJECT_KEY-NUMBER. The docs' ABC-1234 example and case-sensitive project-key note match the contract.

Source #9 — Jira "Invalid Credentials" on 401, validated against the token owner's account (confirmed)

File: trunk-io/trunk/trunk/services/ticketing/src/integrations/jira_cloud_integration_client.ts#L182-L189

    // Validate email and apiToken
    try {
      await client.myself.getCurrentUser();
    } catch (err) {
      if (err instanceof HttpException && err.status === 401) {
        const message = "Connection Failed: Invalid Credentials";

Reasoning: Credentials are validated by calling Jira's myself.getCurrentUser() with the supplied email + token; a 401 surfaces as "Invalid Credentials". Because the token authenticates to whatever Atlassian account issued it, a token from a different account than the entered email is exactly the failure mode the docs describe. The "Browse projects" / cross-project access guidance is consistent with the separate project-key validation (client.projects.getProject → "Invalid Project Key" on 404) directly below.

Source #10 — "Unable to find ticket" error (confirmed)

File: trunk-io/trunk/trunk/services/ticketing/src/service.ts#L62

      if (!ticket) {
        const message = "Unable to find ticket";
        logger.warn(message, { call: call.request.toObject() });
        callback({ code: Status.NOT_FOUND, message }, null);

Reasoning: "Unable to find ticket" is a real ticketing-service error, returned by both getTicket and importTicket (the import path uses externalTicketId). When the token can't read the target project, Jira returns the ticket as not found and Trunk surfaces this string — matching the docs' cross-project troubleshooting section.

Source #11 — 100 auto-analyses/month/workspace beta cap (unverifiable)

Searched: trunk/services/flaky-tests-analysis/src/* (analysis.ts, index.ts, summary.ts, utils.ts), trunk2 ts/apps/infra/resources/flaky-tests-investigation-poller.ts, and code search for MAX_INVESTIGATIONS, MAX_AUTO_INVESTIGATIONS, per month, monthly, 100.

Reasoning: No hardcoded 100 / monthly-cap constant turned up in the analysis service or the investigation-poller infra. This is consistent with the cap being a business / config / DB-driven limit (typical for a beta quota) rather than a source literal. Absence of a code constant is not a contradiction. Confirm the exact number (100) and scope (per workspace, per month) with the Flaky Tests / Autofix team before publishing, since it is a hard figure readers will plan against. The adjacent claim that v2.test_case.status_changed is not rate-limited is consistent: that event comes from the detection engine, not the AI investigation pipeline.

@samgutentag samgutentag added the code-verified-partial verify-docs-against-code: confirmed claims, some unverifiable. label Jun 1, 2026
Copy link
Copy Markdown
Member Author

Verification status: unknown - June 2, 2026

Could not determine rollout state from available signals.

  • Flag state: LaunchDarkly not consulted; no feature flag identified
  • Eng PR links: None. PR body notes this reopens work from Document ticketing integration patterns and Linear/Jira troubleshooting #55 (merged then reverted 2026-06-01); no eng PR reference in current body.
  • Flag: none
  • Signals checked: PR body; no eng PR refs to verify against
  • Suggested next action: Add eng PR references or confirm ticketing integration patterns are current, then re-run sweep.

Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

code-verified-partial verify-docs-against-code: confirmed claims, some unverifiable.

Development

Successfully merging this pull request may close these issues.

1 participant