Skip to content

GitHub Action

The quickest way to try Dispatch on a repository you already control.

Setup

bash
npx @dispatch-triage/cli init

That writes .github/dispatch.yml, a workflow, and a directory for template overrides. Then add your TypeSafe key as a repository secret named TYPESAFE_API_KEY.

The workflow

yaml
name: Dispatch

on:
  issues:
    types: [opened, edited, reopened]
  pull_request:
    types: [opened, edited, ready_for_review, synchronize]
  issue_comment:
    types: [created]

permissions:
  contents: read
  issues: write
  pull-requests: write

concurrency:
  group: dispatch-${{ github.event.issue.number || github.event.pull_request.number }}

jobs:
  triage:
    runs-on: ubuntu-latest
    steps:
      - uses: JeelGajera/Dispatch/apps/action@v0.1.0
        with:
          jev-key: ${{ secrets.TYPESAFE_API_KEY }}

      - if: always()
        uses: actions/upload-artifact@v4
        with:
          name: dispatch-decisions-${{ github.run_id }}
          path: .dispatch/decisions.jsonl
          if-no-files-found: ignore

The concurrency group matters: GitHub's secondary rate limits punish bursts of content creation, and two runs racing on one item can produce two comments.

Reading the decision log

In shadow mode the artifact is the whole point. Download it, read what Dispatch would have done, and disagree with it in .github/dispatch.yml until you stop disagreeing.

Pull requests from forks

Read this before using pull_request_target

pull_request from a fork gets a read-only token and no secrets. That is correct and safe, and it means Dispatch cannot label the pull request or read your API key.

pull_request_target gets a write token and your secrets, and runs the workflow from the base branch. It is safe only if you never check out or execute the pull request's code. An attacker opening a pull request controls that code completely.

So the sample workflow has no checkout step, and must never gain one. Dispatch reads pull request metadata through the API and executes nothing from the head.

If you need to build or test fork code, do it in a separate pull_request workflow with no secrets.

See examples/workflows/fork-pull-requests.yml.

Escalations

Some rules depend on time passing — "this has been waiting on a reproduction for fourteen days" is not something GitHub notifies about. Run those on a schedule:

yaml
on:
  schedule:
    - cron: '30 3 * * *'

jobs:
  escalate:
    runs-on: ubuntu-latest
    steps:
      - uses: JeelGajera/Dispatch/apps/action@v0.1.0
        with:
          jev-key: ${{ secrets.TYPESAFE_API_KEY }}
          command: escalate

Inputs

InputDefaultNotes
jev-keyRequired. Pass a secret, never a literal.
config-path.github/dispatch.yml
modefrom configOverrides the configured mode.
commandtriagetriage, escalate, or backfill.
dry-runfalseDecide and log, whatever the mode says.
upload-logtrueWrite the decision log for the artifact step.

Duplicate detection

Runs automatically on newly opened issues when dedup.enabled is true. It costs one extra provider request, and none at all when the index turns up nothing plausible.

yaml
dedup:
  enabled: true
  candidates: 30
  reportThreshold: 0.75
  includeClosed: true

It applies possible-duplicate and posts the ranked candidates with their probabilities. It never closes anything, at any confidence, in any mode.

The index is built as Dispatch sees items, so it is empty on day one and gets better as it runs.

Escalations

command: escalate applies the escalations: block. It is driven by the decision log rather than a repository scan, so it only ever follows up on items Dispatch itself labelled — and it stops the moment a maintainer removes that label, because that removal was their decision.

/dispatch explain

Posts the full question, answer and confidence table into the bot comment, including what was held back and why. This is the command that makes a decision arguable rather than mysterious.

MIT licensed.