assay · methodology · the layer between the desks and the forge
Desk tools
The desk tools are the only sanctioned way an agent session writes to the forge. Each one is a single workflow verb with its policy compiled in: which role may run it, on which repositories, how often, and what it refuses. They are not a wrapper over the forge's CLI. A session holding a role's credential and calling the forge directly gets none of the guards below and can perform any write the credential allows; the same session going through a verb gets an audit line, a rate meter, a body scan, an enumerated operation set, and a refusal it must respect. The credential is a ceiling. The verbs are the control.
The verbs, by role
One binary per verb, named for what it does. Read the table by class: read-only verbs only query the forge and print; outward-write verbs can change something on the forge and are the only ones the write meters gate; local-only verbs touch state under your own configuration home. Every verb echoes its effective configuration before its output, so a widened repository scope shows up in run output rather than in someone's memory.
| Role | Verbs | What they do, and what they refuse |
|---|---|---|
| every desk | desktoken, deskboard, statusgen |
desktoken <role> mints or reuses a short-lived installation token for one role; deskboard reads the open pull requests, checks, and queue across the configured repositories and writes nothing; statusgen generates and lints the board and runs a brief's Verify table. |
| worker | deskpr, deskreply, deskfile |
deskpr create opens a pull request draft-only by construction: the draft flag is hardcoded into the command it builds and there is no flag to remove it, no force-push it can emit, and no ready, close, or merge verb anywhere in the toolset. deskpr update pushes follow-ups; deskpr edit corrects the body. deskreply comments only on the worker's own pull request. deskfile new files an issue after a dedupe search, as the role. |
| worker-desk | deskdispatch |
Claims one item through a durable, shared claim marker (a second dispatcher asking for the same item is refused), cuts an isolated checkout in the item's own repository, assembles the worker prompt, and records the model-tier stamp. A brief carrying a human gate is still dispatched: the gate binds approval, not implementation. |
| pr-review-desk | deskpost, deskflip, deskmerge, deskclose |
deskpost review posts the verdict under the reviewer identity, which the implementer cannot hold. deskflip flips draft to ready and refuses unless the reviewer approved at the current head, checks are green, the pull request is mergeable, a risk-classed change carries a security verdict at head, and the caller is the review role. deskmerge check reports merge-currency in three states; deskmerge merge merges main into a branch, never a branch into main. deskclose closes an issue only on a fetched human authorization or a two-role propose-and-confirm. |
| verify-desk | deskevidence, statusgen |
deskevidence commit lands an Evidence row through the forge's contents API as the verifier identity, so the row names who ran it and the author could not have written it. statusgen's execution witness re-runs the Verify table and records a three-state result per row. |
| intake-desk | deskfile, deskclose, scanloop |
Files exactly one tracked exit per inbound item, applies the trust gate before anything is queued, and batches the mechanical scan behind one branch and one pull request per pass. |
| the-desk | deskpr, deskfile, deskrelease |
Authors briefs and files findings through the same write verbs as everyone else. deskrelease cut creates a tag and can only create; it never moves or deletes one. |
The named verbs above are the everyday surface. The full toolset is larger (loop planners, the claim lock, worktree and fetch verbs, the guards installed as git and harness hooks), and the reference for all of it is the desk-tools README in the public repository, which is the authority when this page and it disagree.
The read path
The desk tools are the only sanctioned write path to the forge. Reads are a different question, because a read cannot change the record, so the rule is looser and there are three lanes:
| Lane | What reads through it | Credential |
|---|---|---|
| Desk read-only verbs | The board and queue reads a desk makes before it acts: deskboard, statusgen board reads, the verify loop's plan, the dispatcher's pre-claim reads. Through the Forge seam, so the same enumerated operations and the same refusals apply. |
The role's App token, read scopes only. |
| git itself | The canonical board read is git fetch followed by git show FETCH_HEAD:<path>. Briefs, registers and the board are read from the default branch, never from a worktree that may be stale or mid-edit. |
None on a public repository; the checkout's own fetch credential on a private one. |
| The operator's forge CLI, reads only | gh pr view, run logs, API GETs. Permitted to humans and sessions alike, because a read cannot change the record; forbidden for any write, because a write under a personal login is exactly the workaround the role identities exist to close. |
Whatever the shell already holds; never used to write. |
The rule in one sentence: writes go through a role identity and a policy verb; reads may use anything.
The honest wrinkle: the third lane holds capability the role identities do not carry today. CI run logs need an Actions read grant that the worker identity does not hold, so a session shepherding a red check reads the log through the operator's CLI rather than through a verb. That asymmetry is known and is why a read-path verb for run logs is on the roadmap: the target is that every read a desk depends on is available under its own identity, so the ambient lane becomes a convenience rather than a dependency.
The policy layer
Five mechanisms, each compiled into every outward-write verb rather than asked of the session running it.
-
Role identities
Every write is made as a role, never as a person and never as an ambient login.
desktokenmints a short-lived token from the role's App key and the verb acts as that identity; a verdict posted by the reviewer role names an identity the worker role cannot post as. That is separation of attribution, and it is the weaker claim the stronger ones rest on. On GitHub the roles are Apps; on GitLab, service accounts (see the GitLab profile). -
Three-state verdicts
Every check reports
checked-clean,checked-failed, orcould-not-check, and the third is never rounded to either of the other two. A verifier that cannot reach a sibling repository, a preflight whose token will not mint, a push guard that cannot determine the base branch: each says so, in those words, and the desks treat could-not-check as a stop, not a pass. -
Refusals, with exit codes
A verb that will not do what it was asked says why on one line and exits with a code that tells the caller what to do next. The distinction that matters is 5 against 6: a refusal is the tool saying the thing is not allowed, and reaching for the raw CLI to do it anyway is defeating a gate, not routing around a bug; unverifiable is the tool saying it could not tell, which is the one case where a documented fallback is authorised.
Exit Meaning What the caller does 0 ok, or an idempotent no-op nothing: it worked, or it had already been done 3 disabled: a kill switch or stop flag is armed stop; a human clears the flag 4 rate-limited: a write meter or the breaker is open read retry-after, sleep it once, attempt once; never poll5 refused: a deliberate safety stop stop and fix the input; never a fallback trigger 6 unverifiable: the tool could not confirm state the documented fallback for that verb, and only that -
Budgets and breakers
Every outward-write verb runs behind two write meters (a rolling hourly budget per verb, charged only on a write that lands) and a circuit breaker that opens on a run of failures. A session that loops on a failing write is stopped by the meter, not by its own good judgement, and the audit line records the attempt either way. The limits are the toolset's own, set below the forge's, so the forge's rate limit is never the first thing that trips.
-
Human gates
No verb merges a pull request into the default branch. No verb approves the author's own work; the forge refuses that too. No verb creates an App, changes a ruleset, or alters repository administration.
deskflipmakes a draft ready and stops;deskclosecloses only on a human authorization it can fetch;deskmerge mergeis gated on a fetched human sign-off and, unsigned, merges nothing. The tools are built so that the decisions which are yours cannot be taken for you, and a session that reaches for the forge's CLI to take one anyway is stepping outside the toolset, visibly.
The Forge seam
Every verb reaches the forge through one interface, and that interface is deliberately closed: fifteen enumerated operations, no generic call, no caller-supplied endpoint, and a continuous-integration check that refuses a shell-exec or passthrough shape anywhere in the tree. A new forge is a new backend behind the same interface; the verbs, their guards, and the desks above them do not change.
Forge GetPullRequest GetIssue OpenChangeForBranch SearchIssues ListLabels ListOpenChanges ListOpenIssues ListChangedFiles ReviewsAtHead ChecksAtHead PRTrustEvents IssueTrustEvents ... (writes: comment, review, flip, file, close, evidence) no Do(), no Raw(), no endpoint argument
The seam is what makes the coverage numbers on the layers page measurable at all: an operation is either on the interface with a backend for a given forge, on it with a partial backend, or not on it. There is no fourth state where a verb quietly does its own HTTP.
Limits, stated
GitHub first. The fleet that builds Assay runs on GitHub, so the GitHub backend is complete and continuously exercised, and the desk-role work the verbs cover there is measured at 85 percent. Everything on this page is proven on GitHub before it is claimed anywhere else.
GitLab is partial. A published profile maps every control onto GitLab's mechanisms, and the backend implements 62 percent of the Forge interface fully, 86 percent counting partial implementations, measured 2026-09-13. Until a live pilot has round-tripped a brief through the verbs rather than around them, GitLab is a design you can plan against, not an install you can run.
Human-only rows stay on the forge's CLI, by design. Across the whole software-delivery lifecycle the verbs cover 41 percent of actions on either forge. The remainder is not a backlog to be closed: repository administration, ruleset and branch-protection changes, App or service-account creation, token custody, and the merge itself are an administrator's or a reviewer's acts on the forge's own surface, and a toolset whose purpose is to bound what an agent may write has no business performing them. The repository-hardening checker reads those settings under an identity that can write nothing, and reports could-not-check on the ones it is not allowed to see.
The guards are client-side. A verb's refusal binds the session that calls it. What binds everyone is the server-side layer beneath: branch protection, rulesets, the per-role permission set, and the forge's own refusal to let an author approve their own change. The verbs are built to make the sanctioned path the easy one and every other path a visible step outside it; they are not, on their own, the thing that makes the forge safe.