Use in CI
adrkit ships three GitHub Actions from this repository. They hold no database
and never approve anything. Two run with the default GITHUB_TOKEN and write a
comment or an issue; the third, the decision portal,
holds no token at all and only writes a directory for your workflow to deploy.
Comment governing decisions on a PR
Section titled “Comment governing decisions on a PR”The packages/ci Action reads a pull request’s changed files, resolves which
accepted decisions govern them via each record’s affects matchers, validates
any changed ADR records, and posts a single comment. It is read-only and
comment-only; on a read-only fork token it degrades to a log notice instead of
failing the job.

The comment above is from adrkit-demo, which keeps pull requests open for a compliant change, a violating one, a new decision, and an invalid record.
name: ADR governing decisions
on: pull_request:
permissions: contents: read pull-requests: write
jobs: governing-decisions: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - uses: mbeacom/adrkit@v0.20.0 # with: # dir: docs/adr # ADR corpus directory (default: docs/adr) # token: ${{ github.token }}The established mbeacom/adrkit/packages/ci@v0 nested form remains supported.
The Action updates its existing comment on later pushes, so each pull request
keeps one current governing-decisions comment. No additional token environment
variable is required when using the default GITHUB_TOKEN.
The comment reports marker results separately from governing decisions. Marker
scan health lists changed files that could not be inspected (absent,
unreadable, out-of-tree, or skipped at the scan cap), so an empty marker
result is not mistaken for a healthy scan. It also reports declaration overflow
after the 64-per-file or 10,000-per-batch safety limits. Marker claims needing
attention lists @adr claims that either did not resolve in this corpus or
resolved to superseded, rejected, or deprecated records. Both sections are
bounded and advisory: they never affect the Action’s exit status, and
changed-record validation errors retain priority if
the comment reaches GitHub’s size limit. Keep the default checkout rooted at
GITHUB_WORKSPACE; if a workflow checks out elsewhere, marker health will
identify files the Action could not inspect.
The Marketplace form is pinned to the immutable v0.20.0 release. For
the nested compatibility form, v0 is a moving major tag; pin an immutable
release tag or commit SHA when maximum reproducibility matters.
Publish the ARB operations queue
Section titled “Publish the ARB operations queue”The packages/ci/queue Action creates or updates exactly one dedicated issue
whose body carries the deterministic adr queue Markdown report — the tiers,
SLA state, approvals, and objections projected from the corpus’s review
metadata. It needs issues: write.
name: ADR ARB queue
on: schedule: - cron: '0 13 * * 1' # Monday 13:00 UTC workflow_dispatch:
permissions: contents: read issues: write
jobs: arb-queue: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - uses: mbeacom/adrkit/packages/ci/queue@v0 # with: # dir: docs/adr # issue-title: ADR ARB QueueThe managed issue’s body is the same report you get locally:
# ARB Queue — 2026-07-28
Corpus fingerprint: `0cd56b8…10e3eb6`8 item(s) | 0 corpus finding(s) | 0 item(s) with findings
## Queue Items
| # | ID | Title | Tier | SLA State | Deadline | Approvals | Objections ||---|----|-------|------|-----------|----------|-----------|------------|| 1 | 0003 | Ship as a Spec Kit extension plus a standalone CLI … | arb | within-sla | 2026-08-27 | 0/- | 0 || 2 | 0006 | License Apache-2.0 with a DCO and develop in a single … | arb | within-sla | 2026-08-27 | 0/- | 0 |Identical inputs produce byte-for-byte identical output, so a re-run only
changes the issue when the corpus actually changed. Run the same projection
locally with adr queue, or publish its totalItems
as a README badge — see Badges.
Publish a decision portal to GitHub Pages
Section titled “Publish a decision portal to GitHub Pages”The packages/ci/portal Action builds a static, read-only decision portal from
the corpus: the record list and every record, status counts, the supersession
graph, the review queue, and governance by path. It writes the bundle into a
directory and does nothing else. It holds no token, needs only contents: read,
and never deploys. Your workflow uploads the directory with
actions/upload-pages-artifact and deploys it with actions/deploy-pages in a
separate job that alone holds pages: write and id-token: write
(ADR-0054).
Set the repository’s Settings → Pages → Source to GitHub Actions, then:
name: ADR decision portal
on: push: branches: [main] workflow_dispatch:
permissions: contents: read
concurrency: group: pages cancel-in-progress: false
jobs: build: runs-on: ubuntu-latest permissions: contents: read steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - id: portal uses: mbeacom/adrkit/packages/ci/portal@vX.Y.Z # an immutable adrkit release tag # with: # dir: docs/adr # out: _site # base-url: https://<owner>.github.io/<repo>/ # as-of: 2026-07-05 # publish-private: 'true' - uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0 with: path: _site
deploy: needs: build runs-on: ubuntu-latest permissions: pages: write id-token: write environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} steps: - id: deployment uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1The default shallow checkout is enough: the Action reads HEAD, the tracked
file list, and the working tree’s status, never history. The bundle uses
relative assets and hash routes, so it works at a project path
(/<repo>/) or a custom domain without base-url; base-url feeds absolute
links only.
The step’s outcome follows the portal generator’s exit code, which it also sets
as the exit-code output:
exit-code |
Bundle | Step |
|---|---|---|
0 |
written | succeeds |
1 |
written, with every corpus error shown in a banner on every page and governance by path withheld | succeeds, with a warning annotation |
2 |
not written; an existing out is left as it was |
fails |
So a corpus with error findings still publishes, and says so on every page, while a corpus that cannot be read publishes nothing and leaves the last deployed site in place.
adrkit builds its own corpus this way on every deploy of this site, from the
committed Action in the same checkout, and serves it at
adrkit.dev/decisions/. The bundle needs no base-url to work
under a subpath: its assets are relative and its routes are hash routes.