adopt · forge profile · github

Assay on GitHub

GitHub is the forge Assay runs on today: the reference profile, the one the desk verbs cover almost entirely, and the baseline every other forge profile is measured against. Assay’s briefs, registers, lifecycle, board, and statusgen are forge-agnostic git and markdown. What GitHub supplies is the layer beneath them: an identity per role, a permission set per identity, and an enforcement surface only a human can change. This page is what GitHub gives the tools, control by control, and where each control’s authority actually sits.

What this page is

This is the shipped baseline. The Assay repository itself, this site, and the fleet that builds them run on this profile continuously; the desk verbs cover 85 percent of desk-role work on GitHub, measured on the layers page. The remainder is not a gap the tools will close: repository administration, App creation, ruleset changes, and the merge itself are a person’s acts on GitHub’s own surface, and the profile depends on that being so.

Identity: one GitHub App per role

Every role the fleet runs is a GitHub App of its own, and every write the fleet makes is signed by one of them. An App is an identity a tool mints a short-lived installation token for, not a service that listens: no webhook, no OAuth, no user authorization. The six desk roles (reviewer, worker, verifier, desk, issue-loop, intake-loop) share one floor, pull requests, issues and contents at read & write, and deskroster preflight refuses to run a role whose installation lacks any of the three. The reviewer needs one read on top of that floor, administration: read-only, and the preflight does not check it — see the permission table. Three more identities are infrastructure rather than desks: board-writer, the only identity on the ruleset bypass; promote, the only holder of the workflows permission; and auditor, which holds no write at all.

Assay roleGitHub identityMechanism
reviewerGitHub AppPRs, issues, contents read & write; checks, statuses, Actions read-only; administration read-only. Posts the verdict the author cannot post; GitHub itself rejects an author’s approval of their own PR. The administration read is what lets the ready-flip see the required checks on a protected branch whose required checks are not expressed in a ruleset (classic protection, or a ruleset with no required-status-checks rule); without it that read fails closed and no PR is flipped. Adding a required-status-checks rule to the ruleset clears it per branch; the permission clears it everywhere
workerGitHub Appsame floor; branches and draft PRs; reads CI logs while shepherding; never approves, so it needs no administration read
verifierGitHub AppPRs, issues, contents read & write; lands Evidence rows through the contents API as itself
deskGitHub Appsame floor plus administration read-only, the reviewer’s read again, so a coordinator shepherding a PR sees the same required-checks answer; coordination writes land through PRs
issue-loop, intake-loopGitHub Appthe two inbound lanes; PRs, issues, contents read & write
board-writerGitHub Appcontents read & write only, and the one entry on the main ruleset bypass: commits STATUS.md from main’s CI
promoteGitHub Appcontents plus workflows read & write, the one grant no desk App carries; its token is minted only by a human-dispatched job
auditorGitHub Appcontents read-only plus administration read-only; the hardening checker’s identity, GET-only by construction

The exact toggles, the manifest flow that fills them in one click each, and the install click-through are on GitHub Apps. The point of the split is attribution: a review, a verification row, or a board commit is posted by an identity the implementer’s token cannot produce. That is strictly more than a self-written checkmark and strictly less than proof of diligence; the rest of the method supplies the second.

Enforcement: rulesets and branch protection are the human-only surface

Every guard inside the desk tools is client-side: a verb’s refusal binds the session that calls it. What binds everyone is GitHub’s server-side layer, and the profile is built so that no fleet identity can reach it. A ruleset on main requires a pull request, a posted review from an identity other than the author, and green required checks before a merge; its bypass list names board-writer and nothing else. No App in the fleet holds administration at write, so none of them can create a rule, edit one, or add itself to a bypass. Two do hold the read half — the reviewer, because the ready-flip must be able to see which checks a branch requires, and the desk alongside it — and a read changes nothing: it is the same asymmetry the auditor identity is built on. Changing that surface is an owner’s act in a browser, in a separate human account, which is exactly why it can be trusted as a gate rather than a convention.

  1. Merge is always the human’s

    No App is granted a path to merge; the ruleset requires a review the author’s identity cannot post; the pr-review-desk flips a draft ready and a person presses merge.

  2. One bypass, one purpose

    board-writer commits the generated board past protection, from main’s CI, and can do nothing else with the grant.

  3. Read the settings, change nothing

    The auditor App reads visibility, rulesets, default workflow permissions and secret scanning against a checklist; fields GitHub shows only to a caller that could change them come back could-not-check, never assumed.

The workflows permission: why a workflow file is a human’s push

GitHub treats .github/workflows/ as its own permission class: a token without workflows: write cannot push a commit that adds or changes a workflow file, whatever its contents grant says. Assay leans on that. The six desk Apps do not hold it, so no desk can alter what CI runs against its own change, and a worker that tries gets GitHub’s refusal, not a policy warning. Workflow changes land as staged files outside the live directory, are reviewed like any other change, and are moved into place by the promote job, the one holder of the grant, which a human dispatches by hand. Nothing autonomous mints its token.

Two more of GitHub’s own controls sit on the same principle. Fork runs need approval: the profile sets Actions to require a maintainer’s approval for every workflow run from an outside contributor, and the desks never build an unblessed fork head, because a pull request from a fork is code that has not been read yet, running on a machine that holds secrets. And release is a tag a person pushes: the release workflow cross-compiles and checksums the binaries on a tag, and the tag push, like the merge, is not a verb any App holds. Where an adopter wants a deploy approval beyond that, GitHub’s Actions environments with required reviewers are the forge’s own gate for it; Assay’s pipeline today runs from intake to verified-on-main, and release, deploy and rollback are not in it.

Token custody: minted per action, expiring on its own

The fleet authenticates with one thing only: an installation token minted from a role’s private key for each action, valid for an hour, scoped to the repositories that App is installed on. The .pem lives in the config home at mode 0600, or as an Actions secret in CI; the App ID is a plain variable; tokens are written to files and printed as paths, never as values, and never placed in an environment variable or on a command line. No logged-in gh and no personal git credential is ever reachable from inside a desk: a human login in the fleet’s environment would let an agent author, push, or merge as the human, straight past every control on this page. How that boundary is kept, and what is and is not in an agent’s environment, is on GitHub Apps.

Other forges, measured against this one

This profile is the baseline column of the GitLab parity table: every GitHub control above has a GitLab mechanism that must be at least as strong or the GitLab profile is non-conforming. GitLab is a published design today, with a partial backend and no live pilot yet; GitHub is where the fleet runs. No third forge is built or claimed.