# Filing issues to GitHub or GitLab

> Connecting a tracker, what goes into a draft, commenting on an issue that already exists, and the guards against duplicate filings.

Source: https://triagic.com/docs/desktop/issues

Integrations are how the agent *reads* your systems. Issue trackers are how a
finished triage *leaves* Triagic.

## Connecting a tracker [#connecting-a-tracker]

On the desktop **Integrations** page, under **Issue trackers → Connect tracker**,
paste an access token.

| Provider   | Token scope                                                                                                                 | Self-managed                                                                      |
| ---------- | --------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| **GitHub** | Classic: `repo`. Fine-grained: **Issues: Read and write** + **Metadata: Read-only**, with the target repositories selected. | Set **Host** to your GitHub Enterprise Server URL; the `/api/v3` root is derived. |
| **GitLab** | `api`. (`read_api` can list projects but cannot create issues.) Reporter or above on the project.                           | Set **Host** to your instance URL; `/api/v4` is derived.                          |

### Which kind of token [#which-kind-of-token]

Both providers take more than one, in the same `Authorization` header, so there is no
method to choose on the form: paste whichever you have.

**GitHub** accepts a classic personal access token, a fine-grained one, and a **GitHub
App installation token**. The installation token is the narrowest of the three: it is
scoped to the app's installation rather than to a person, so it does not carry your own
access to everything else and it does not stop working when you leave.

**GitLab** accepts a personal, **project** or group access token, and an OAuth token. A
**project access token with the `api` scope and the Reporter role** is the one to
prefer: it can reach that project and nothing else. Create it under **Settings → Access
tokens** on the project itself, not in your own user settings.

Tokens are **per user** (yours, not your organization's), encrypted at rest like
every other credential, and never shown again after you save.

Saving verifies the token first. If the provider rejects it, nothing is stored, so a
bad token cannot sit around until the first time someone tries to file. The connected
account is shown on the card once it works.

### A self-managed host behind an internal CA [#a-self-managed-host-behind-an-internal-ca]

When a GitHub Enterprise Server or self-managed GitLab presents a certificate issued by
your own CA rather than a public one, the connection fails during the TLS handshake,
before the token is ever checked, so the error names the certificate and not the
credential. Two fields on the same form cover that case. Both appear only once **Host**
is set, because against github.com and gitlab.com they mean nothing.

| Field                      | Required        | What to put                                                                                                                                                                                     |
| -------------------------- | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **CA certificate path**    | no              | An absolute path on the machine running Triagic to that CA's PEM bundle. It applies to this tracker's requests alone. Nothing else the server talks to changes trust.                           |
| **Verify TLS certificate** | no, defaults on | Turn off only for a self-signed instance you cannot supply a CA bundle for. Traffic stays encrypted, but nothing then proves which host is on the other end. Try the CA certificate path first. |

The bundle is read fresh on every request, so replacing the file at that path takes
effect on the next call rather than at the next restart. A path Triagic cannot read is
rejected when you save, and the error names the path. It doesn't fall back to the
system trust store, which would work right up until the day that host stopped chaining
to a public root.

Existing trackers are unaffected: with neither field set, connections verify against the
system trust store exactly as before.

## Filing an issue [#filing-an-issue]

**Create issue** appears on any finished Console answer and in a ticket's AI
Investigation header.

The dialog is draft-first: Triagic assembles a draft from what it already has
(summary, root cause, evidence, funnel drop-off, tools queried, and a link back to the
ticket or thread) and you edit it before anything is posted. What you see is exactly
what gets created.

You pick:

* **Repository / project.** Type-ahead over what your token can see, or paste
  `owner/repo` directly. The last one you used per tracker is remembered.
* **Labels and assignees.** Pulled live from that project.

> **Note:** Duplicate filings need an explicit confirmation
>
> Issues already filed from the same triage are listed at the top of the dialog, and
> filing another requires you to confirm. This is what stops two people raising the same
> incident twice.

## Commenting on an issue that already exists [#commenting-on-an-issue-that-already-exists]

The dialog has two modes, and the second one is **Comment on existing**. Same draft,
same evidence, but it lands as a comment on an issue that is already open instead of
creating a new one. This is the answer to "we already have a bug for this", which is
otherwise a copy-paste into the browser.

Pick the repository or project as usual, then search it. The search runs
**provider-side**, not over what Triagic has seen: on GitHub through the search API
restricted to issues, so pull requests (which can't take an issue comment) never
appear in the list; on GitLab through the project's own issue search. An empty query
lists what was updated most recently, which is usually the one you want. Results show
the issue key, its title and whether it is open or closed.

The comment posts as a normal comment on GitHub and as a **note** on GitLab. When it
lands, the dialog offers **Open comment**, the deep link to the comment itself, not
just to the issue.

> **Note:** Commenting links the issue to the ticket too
>
> The issue joins the ticket's **Linked issues** exactly as a filed one would, so a
> triage that ended in a comment is as traceable as one that ended in a new bug.
> Commenting twice from the same ticket does not stack a second row: an identical link
> is reused rather than duplicated.

The duplicate warning in **New issue** mode points here explicitly, which is the
intended path when what you were about to file already exists.

## What is deliberately left out [#what-is-deliberately-left-out]

The reporter's email and user id are **not** included in the generated body. An
engineering tracker usually has a wider audience than the support console, and
identity fields are not what makes an issue actionable. Merchant and store names stay,
because those are what an engineer needs to reproduce.

## Afterwards [#afterwards]

Created issues are recorded and shown as **Linked issues** on the ticket. That row
survives disconnecting the tracker. The issue still exists upstream, so the link
should still work.

## Permissions and audit [#permissions-and-audit]

Drafts and filings are provenance-checked: you cannot draft from a ticket you cannot
already read, or from someone else's Console thread.

Every filing writes an `issue.create` audit entry with provider, project, issue key,
labels and source; every comment writes an `issue.comment` entry with provider,
project, issue key and the ticket or thread it came from. Connecting and disconnecting
a tracker write their own entries.
