Skip to content

Setup

The pipeline uses three separate runners: trusted Prepare, untrusted Propose, and trusted Apply. Prepare builds an offline packet with issue bodies, existing labels, types and priorities. Propose classifies that packet; Apply verifies evidence and validates proposals before writing.

Propose runs the Copilot CLI with a fine-grained copilot_token PAT for model access; GITHUB.md covers that credential. Prepare and Apply use the GitHub App below.

Shared prerequisites

GitHub App for trusted reads and writes

Live runs require an organisation App. Public user-owned targets require dry-run. Configure the App for the intended repositories:

Setting Value
App permissions issues: write and metadata: read on repositories; issue_fields: read and issue_types: read on the organisation
Installation Target repositories; all repositories for an organisation-wide scan
Client id Passed as the github_app_client_id input
Private key Passed as the github_app_private_key secret

Prepare holds the private key and mints issues: read plus metadata: read for snapshots and packet assembly. Apply verifies the prepared evidence before minting its own token, including the organisation definition reads. Its issue-write grant requires live mode with a successful proposal job and accepted summary; all other paths request read. Propose receives neither the App key nor an installation token, including through App-token post actions. Its job-native GITHUB_TOKEN has contents: read and no other permissions.

Installation tokens expire after an hour. The repository input also scopes both mints to that repository. Every bot repository in the organisation names the credentials by role: vars.BOT_APP_CLIENT_ID and secrets.BOT_APP_PRIVATE_KEY. Here they hold the LF/RelEng Issues Triage Bot App, whose slug config/bot.json records.

Without App credentials, trusted jobs use their job-native token for dry-run reads within its access. The scan fetches issues from public repositories alone, whichever token reads, because the packet travels to a runner that audits egress (see the design's section 7.1 for the exposure that remains). Live mode refuses to start without an App client ID. The current live App mint and field/type writes still need remote validation; offline tests do not verify GitHub's permission handling.

Where credentials live

Model credentials are repository secrets on the calling repository, matching the name each guide gives. Organisation secrets work too when more than one repository calls the pipeline; the workflow sees nothing but the value handed to its named secret input.

Copilot requires a personal fine-grained PAT with Copilot Requests and no repository permissions. Caller-native GITHUB_TOKEN authentication is no longer supported. The github_pat_ prefix check does not test the token remotely or prove the absence of extra grants; the caller must review them. See GITHUB.md.

The App key belongs in trusted jobs, never Propose. Treat assets_repository and assets_ref as trusted code choices: Prepare resolves the ref once, and downstream jobs use its commit SHA. Do not run unreviewed assets with secrets.

Validation

Start with the offline tests and the linting suite:

uv run python -B -m unittest discover -s tests -v
prek run --all-files

The workflow-contract tests use the PyYAML development dependency. The local suite, three-job Copilot dry-run and both secretless PR invocations passed; see Design §11 for run evidence and limits.

For further validation, a maintainer can dispatch the agent against a reviewed, trusted ref:

gh workflow run testing.yaml

This check uses dry_run: true and retriage: true. It can examine labelled issues but does not guarantee eligible issues or non-empty proposals. It passes no App credentials; live App token minting and real writes remain untested and require a separate controlled organisation-App run. The Copilot step rejects a missing or non-PAT token before launching the CLI; the prefix check does not test authentication.

Inspect all three artefacts:

  • Evidence, 7 days: snapshot, normalized exclusions and offline issue packet when the agent runs. Prepare supplies the artefact ID and snapshot/exclusion digests directly to Apply.
  • Session, 7 days: prompt, summary and Copilot logs. Apply extracts this separately and copies a bounded regular summary, excluding other files.
  • Results, 90 days: verified before-state, available after-state, accepted summary, apply outcomes and diff report. Raw session logs stay out of this bundle. The report requires snapshot-step success before using after-state and observes labels, not priority/type.

Scope, failures and recovery

A scan reaching the 1,000-result search cap fails visibly before exclusion filtering. Restrict repository rather than accepting an incomplete scan. Exclusions use repository names, trim whitespace, ignore blank lines and normalize case. A non-empty exclude_repos input, separated by commas and/or spaces, overrides the bundled list; file-based lists allow # comments.

Live organisation configuration read failures are fatal. Dry-run alone permits known endpoint absence or missing permission and records unavailable priority/type values as dropped. Rate limits, transient failures and unknown errors still fail. The applier refuses an issue whose existing Priority it cannot resolve and skips any issue with a Priority, including during retriage.

Workflow-level concurrency covers the full prepare/propose/apply sequence of a live run per caller repository and target owner. GitHub may supersede pending runs; cancel-in-progress: false is not a FIFO queue. Dry runs write nothing and lock within their own caller run, so this lock never makes them wait on or cancel live runs or other runs' dry runs. A caller's own concurrency group applies first: keep dry runs out of any group the caller shares with live runs, as the bundled scheduled caller does. Different callers targeting the same organisation need coordination.

A run/attempt/UUID namespace avoids reusable and matrix artefact collisions without caller-supplied names. Downloads use producer IDs, so rerunning Apply without its producers can reuse their available evidence. Final result names append the current attempt.

Writes are nontransactional: a label change can succeed before a type or Priority write fails. Cancellation does not undo completed writes, and live checks do not prevent races with human edits. Inspect apply-result.json and the current issue before targeted recovery. Replay often needs retriage: true after labels have changed, but must never overwrite a human Priority. There is no automatic rollback or guarantee that rerunning will complete a partial application.

Further reading

  • Design — the architecture, the containment model, and the reasoning behind the Copilot CLI integration
  • Repository README — calling the reusable workflow from another repository