Appearance
gh Adapter Contract — The gh Surface BoB Depends On
What a second forge backend behind
bin/ghwould have to reproduce (#1753). (Back to reference index.)
BoB talks to its forge through gh. A backend for another forge (#1746) does not have to be all of gh: it has to be the part of gh BoB uses. This page names that part, and the engine that reads it from the tree so it stays true.
The inventory
scripts/forge/gh-inventory.js reads every tracked file and lists each gh call it finds, by command:
bash
node scripts/forge/gh-inventory.js scan # one line per command: executable and prompt-text counts
node scripts/forge/gh-inventory.js scan --json # gh-inventory/1: totals, per-command flags / --json fields / files, every siteTwo kinds of call site are counted apart, and the split is the point:
| Kind | Where | Why it matters |
|---|---|---|
| executable | .sh / .js / .ts / workflow YAML, and extensionless scripts with a shell or node shebang | Code that runs gh. It could be ported by editing a function. |
| prompt-text | .md under commands/, skills/, agents/, registry/, runbooks/, docs/, templates/, and the root guides | Instructions an agent follows and then runs through Bash. They can only be ported by keeping gh working, which is why a gh-shaped shim beats a rewrite. |
Not read: tests/ (they stub gh), www/ (published pages), journal/, story/, wiki/, evals/, CHANGELOG.md and app test files.
Two forms are recognised:
- shell —
gh issue list --json number,titleon a line. In code, comment lines are skipped.<N>placeholders are words, and the command ends at the first pipe,;,&&, redirect or closing backtick. - argv — a JS/TS array literal whose first element is a
ghcommand and the second its subcommand:gh(['issue', 'list', …]),execFileSync('gh', ['api', …]),const args = ['pr', 'merge', …]. A command that takes no subcommand (status,browse) only counts withghin sight, so git's['status', '--porcelain']is not read asgh status.
A subcommand outside gh's own command tree is prose ("the gh issue family"), not a call. An argv array assembled piece by piece is not seen; the shim's runtime record (Forge Health → The shim) sees every call that actually runs, and is the cross-check for those.
Keys
A call is keyed <command> <subcommand> — issue list, pr merge. For gh api the key is the normalised endpoint: every interpolation (${x}, $X, <N>, {owner}) and every numeric path segment becomes {}, and the repo is always one {} whether it was written {owner}/{repo}, ${slug} or a literal owner and name — so api repos/{}/issues/{} is one endpoint. api graphql is its own key.
A --json field list held in a variable cannot be read statically; the command is marked json_dynamic.
The contract
scripts/forge/gh-contract.json (gh-contract/1) records one entry per command the tree calls:
| Field | Meaning |
|---|---|
behavior | What the command does for BoB — the part of gh's behaviour a backend must reproduce. |
output | The output shape callers read: a JSON array or object of the --json fields, a URL whose last segment is the new number, a REST body, or nothing but the exit code. |
portable | true — a backend must reproduce it. false — known-unsupported: no forge-neutral equivalent. |
reason | For an unsupported command, why. |
flags, json | The flags and --json fields BoB calls it with. json_dynamic: true when a field list is held in a variable. |
The behavior, output, portable and reason text is written by hand. The flags and fields are what the scan finds, recorded by record.
What every backend must also keep
- Transparency. Everything outside the contract passes through unchanged: stdin, stdout, stderr and the exit code (Forge Health → The shim).
- An honest failure. A read that cannot be answered exits non-zero; it is never an empty list. The cache, the queue and the callers rely on telling "nothing matched" from "the lookup failed".
The drift gate
bash
make check-gh-contract # the gate, part of make check
node scripts/forge/gh-inventory.js check --json # gh-contract-check/1
node scripts/forge/gh-inventory.js record # fold new usage into the contract, re-render this page
node scripts/forge/gh-inventory.js write # re-render this page's block onlycheck fails, naming the call sites, when:
| Problem | Meaning | Fix |
|---|---|---|
unrecorded-command | The tree calls a gh command the contract does not list. | record, then write its behavior, output and portable. |
unrecorded-flag / unrecorded-json | A known command is called with a new flag or --json field, or a field list in a variable. | record — and know a backend must now support it. |
unused-command | The contract lists a command nothing calls. | record drops it. |
undocumented | An entry has no behavior, output or portable, or is unsupported with no reason. | Write the text. |
doc-drift / doc-missing | The generated block below is not what the contract renders. | write. |
New coupling to the forge therefore cannot land silently: it lands with a contract edit in the same diff, where a reviewer sees it. Exit codes: 0 clean, 1 drift, 2 the tree or contract could not be read.
GraphQL
The cap behind this contract (#1746) measures "zero GraphQL in executable code". Today there are four executable callers of gh api graphql, and each is kept, and listed as known-unsupported, not ported:
| Caller | What one query reads |
|---|---|
scripts/brief/reconcile.js | The state and labels of up to 60 issues across repos, for the brief reconciler (#3100). |
scripts/brief/brief.js | The newest releases of every repo in the portfolio, for the briefing (#2243). |
scripts/forge/batch.js | A repo's issues 40 at a time, for the stale-todo sweep (#3219). |
scripts/release/notes.js | 40 issue titles, bodies and labels at a time, for the release notes. |
Each was batched on purpose: one query answers what dozens of REST calls (or gh issue views) would, and that is what keeps these readers inside the quota. A port would multiply quota spend by the batch size for no gain on GitHub, so a backend refuses them instead; every caller already treats a failed read as unknown, never as an empty answer. Each call site carries a one-line comment naming this contract.
Nothing passes the metric silently. The generated block names every executable file that calls an unsupported command, so a fifth GraphQL caller changes the block, and make check-gh-contract fails with doc-drift until the block is re-rendered in the same diff — where a reviewer sees the new row.
The surface
85 commands: 51 a backend must reproduce, 34 known-unsupported. GraphQL in executable code: 7 call site(s) in 4 file(s), listed as unsupported below.
Commands a backend must reproduce
| Command | Behavior | Output | Flags | --json fields |
|---|---|---|---|---|
api repos/{} | Read one repository's metadata (default branch, visibility, archived). | the REST response body as JSON (or the --jq projection of it) | --jq | — |
api repos/{}/collaborators | List the repo's collaborators. | the REST response body as JSON (or the --jq projection of it) | — | — |
api repos/{}/collaborators/{} | Add or remove one collaborator (-X PUT / DELETE). | the REST response body as JSON (or the --jq projection of it) | -X | — |
api repos/{}/compare/{}...{} | Compare two refs: ahead/behind counts and the commits between. | the REST response body as JSON (or the --jq projection of it) | — | — |
api repos/{}/contents/{} | Read one file's content at a ref. | the file's content (raw with -H Accept: raw, else the contents object) | -H | — |
api repos/{}/git/refs/heads/master | Move master to a SHA (-X PATCH -f sha=…). | the REST response body as JSON (or the --jq projection of it) | -X -f | — |
api repos/{}/git/refs/heads/{} | Create, move or delete a branch ref — merge-on-green's fast-forward (-X PATCH -f sha=… -F force=false). | the REST response body as JSON (or the --jq projection of it) | -F -X -f | — |
api repos/{}/issues | List or create issues through REST (--paginate; -X POST -f title=…). | the REST response body as JSON (or the --jq projection of it) | --jq --paginate -X -f | — |
api repos/{}/issues/comments/{} | Read, edit or delete one issue comment (-X PATCH --input). | the REST response body as JSON (or the --jq projection of it) | --input --jq -X | — |
api repos/{}/issues/{} | Read one issue, or patch its state, body or labels (-X PATCH --input). | the REST response body as JSON (or the --jq projection of it) | --input --jq -X | — |
api repos/{}/issues/{}/comments | List or add an issue's comments (--paginate; -X POST -f body=…). | the REST response body as JSON (or the --jq projection of it) | --jq --paginate -X -f | — |
api repos/{}/labels | List the repo's labels (--paginate). | the REST response body as JSON (or the --jq projection of it) | --jq --paginate | — |
api repos/{}/pulls/{}/merge | Merge one pull request (-X PUT). | the REST response body as JSON (or the --jq projection of it) | -X | — |
api user | Read the signed-in account (its login). | the REST response body as JSON (or the --jq projection of it) | -q | — |
api user/keys | List the account's SSH keys. | the REST response body as JSON (or the --jq projection of it) | --jq | — |
auth login | Sign the machine in to the forge. | nothing callers parse; the exit code is the answer | --git-protocol --hostname --web | — |
auth status | Is this machine signed in? Exit 0 = yes. | nothing callers parse; the exit code is the answer | — | — |
auth token | Print the current token, for a caller that talks HTTP itself. | the token on stdout | — | — |
issue close | Close an issue, optionally with a comment and a reason (completed / not planned). | nothing callers parse; the exit code is the answer | --comment --reason --repo -R | — |
issue comment | Add a comment to an issue. | the comment URL on stdout (not parsed) | --body --body-file --repo -R | — |
issue create | File an issue with a title, body, labels and assignee. | the URL of the new object on stdout — callers read the number from its last path segment | --assignee --body --body-file --label --repo --title -R | — |
issue edit | Change an issue's body, title or labels (--add-label / --remove-label). | the issue URL on stdout (not parsed) | --add-label --body --body-file --remove-label --repo | — |
issue list | List issues by state, label, assignee or a search string. --search takes GitHub query syntax, which a backend must translate or refuse. | a JSON array of objects carrying exactly the requested --json fields (or the --jq projection of it) | --assignee --jq --json --label --limit --repo --search --state -R -l -q -s | assignees body closedAt comments createdAt labels number state stateReason title updatedAt url + dynamic |
issue reopen | Reopen a closed issue. | nothing callers parse; the exit code is the answer | --comment --repo | — |
issue view | Read one issue: body, labels, state, comments. | one JSON object carrying the requested --json fields (or the --jq projection of it); without --json, human text callers do not parse | --comments --jq --json --repo -q | assignees body closedAt comments createdAt labels number state stateReason title url + dynamic |
label create | Create a label with a colour and description; --force updates one that exists. | nothing callers parse; the exit code is the answer | --color --description --force -R | — |
label delete | Delete a label. | nothing callers parse; the exit code is the answer | --repo --yes | — |
label edit | Rename a label or change its colour or description. | nothing callers parse; the exit code is the answer | --name --repo | — |
label list | List the repo's labels. | a JSON array of objects carrying exactly the requested --json fields (or the --jq projection of it) | --jq --json --limit --search | description name |
pr close | Close a pull request, optionally deleting its branch. | nothing callers parse; the exit code is the answer | --comment --delete-branch | — |
pr comment | Add a comment to a pull request. | the comment URL on stdout (not parsed) | --body --repo | — |
pr create | Open a pull request from a head branch to a base. | the URL of the new object on stdout — callers read the number from its last path segment | --base --body --body-file --fill --head --label --title | — |
pr diff | Print a pull request's diff. | a unified diff on stdout | — | — |
pr edit | Change a pull request's body or labels. | nothing callers parse; the exit code is the answer | --add-label --body | — |
pr list | List pull requests by state, base, head, author or label. statusCheckRollup is GitHub's checks model; a backend must synthesise it from its own commit statuses. | a JSON array of objects carrying exactly the requested --json fields (or the --jq projection of it) | --author --base --head --jq --json --label --limit --search --state -R -q | body createdAt headRefName isDraft labels mergedAt number state statusCheckRollup title url |
pr merge | Merge a pull request (--squash, --delete-branch). --admin bypasses branch protection, which a backend may not offer. | nothing callers parse; the exit code is the answer | --delete-branch --squash | — |
pr view | Read one pull request: body, head branch, commits, check rollup. | one JSON object carrying the requested --json fields (or the --jq projection of it); without --json, human text callers do not parse | --jq --json --repo -q | body commits headRefName number statusCheckRollup url + dynamic |
release create | Publish a release for a tag, with notes from a file or generated. | the URL of the new object on stdout — callers read the number from its last path segment | --generate-notes --notes --notes-file --title --verify-tag | — |
release download | Download a release's assets. | files written to the working directory | — | — |
release list | List releases. | a JSON array of objects carrying exactly the requested --json fields (or the --jq projection of it) | --json --limit --repo | isDraft name publishedAt tagName |
release view | Read one release (tag, URL). | one JSON object carrying the requested --json fields (or the --jq projection of it); without --json, human text callers do not parse | --jq --json | tagName url |
repo archive | Archive a repository. | nothing callers parse; the exit code is the answer | --yes | — |
repo clone | Clone a repository by owner/name. | a git checkout on disk | — | — |
repo create | Create a repository, optionally from a local source directory. | the repository URL on stdout | --private --source | — |
repo edit | Change repository settings (docs show --help). | nothing callers parse; the exit code is the answer | --help | — |
repo list | List an owner's repositories. | a JSON array of objects carrying exactly the requested --json fields (or the --jq projection of it) | --json --limit | isArchived isPrivate nameWithOwner pushedAt |
repo unarchive | Unarchive a repository. | nothing callers parse; the exit code is the answer | — | — |
repo view | Read the current repository: name, owner, default branch, visibility, archived. | one JSON object carrying the requested --json fields (or the --jq projection of it); without --json, human text callers do not parse | --jq --json -q | defaultBranchRef isArchived isPrivate name nameWithOwner + dynamic |
ssh-key add | Register a public SSH key on the account. | nothing callers parse; the exit code is the answer | --title | — |
ssh-key delete | Remove an SSH key from the account. | nothing callers parse; the exit code is the answer | — | — |
ssh-key list | List the account's SSH keys. | human text | — | — |
Known-unsupported
No portable equivalent: a backend refuses these, and the callers named here are what a port would have to change.
| Command | Behavior | Why it is unsupported | Executable call sites | Prompt-text call sites |
|---|---|---|---|---|
api graphql | One GraphQL query that reads many objects at once: the state and labels of up to 60 cross-repo issues for the brief reconciler (scripts/brief/reconcile.js, #3100), the newest releases of every repo for the briefing (scripts/brief/brief.js, #2243), a repo's issues in chunks for the stale-todo sweep (scripts/forge/batch.js, #3219) and 40 issue titles at a time for the release notes (scripts/release/notes.js). | GitHub's GraphQL schema. Each of these reads is batched on purpose: one query answers what dozens of REST calls would, which is what keeps the briefing, the reconciler and the stale-todo sweep inside the quota (#3100, #3219). A port to REST or gh issue view would multiply quota spend by the batch size, so they stay GraphQL and a backend refuses them; every caller already treats a failed read as unknown, never as an empty answer. A new executable caller changes this row, so it cannot land silently. | scripts/brief/brief.js scripts/brief/reconcile.js scripts/forge/batch.js scripts/release/notes.js | docs/reference/bob-asks.md docs/reference/briefing.md docs/reference/forge-health.md |
api rate_limit | Read the token's remaining quota per resource (the shim's budget, #3190). | GitHub's quota model; the shim's pacing (scripts/forge/budget.js) is built on it. A backend reports its own limits or none. | scripts/bootstrap-labels.sh scripts/forge/budget.js scripts/forge/health.js | docs/reference/forge-health.md |
api repos/{}/actions/permissions | Read the repo's Actions permission settings. | GitHub Actions: runners, runs, workflows, secrets and variables have no forge-neutral model — a backend with its own CI needs its own mapping, and BoB's CI locus (docs/reference/ci-locus.md) is GitHub-only today | — | registry/runbooks/bob-attack-surface-review.md |
api repos/{}/actions/runners | List the repo's self-hosted runners and their status. | GitHub Actions: runners, runs, workflows, secrets and variables have no forge-neutral model — a backend with its own CI needs its own mapping, and BoB's CI locus (docs/reference/ci-locus.md) is GitHub-only today | scripts/dashboard/server.js scripts/fleet/ci-locus.js scripts/fleet/ci-rollout.js scripts/fleet/runner.sh | — |
api repos/{}/actions/runners/registration-token | Mint a runner registration token. | GitHub Actions: runners, runs, workflows, secrets and variables have no forge-neutral model — a backend with its own CI needs its own mapping, and BoB's CI locus (docs/reference/ci-locus.md) is GitHub-only today | scripts/fleet/runner.sh | — |
api repos/{}/actions/runners/remove-token | Mint a runner removal token. | GitHub Actions: runners, runs, workflows, secrets and variables have no forge-neutral model — a backend with its own CI needs its own mapping, and BoB's CI locus (docs/reference/ci-locus.md) is GitHub-only today | scripts/fleet/runner.sh | — |
api repos/{}/actions/runs | List workflow runs. | GitHub Actions: runners, runs, workflows, secrets and variables have no forge-neutral model — a backend with its own CI needs its own mapping, and BoB's CI locus (docs/reference/ci-locus.md) is GitHub-only today | scripts/fleet/ci-locus.js | — |
api repos/{}/actions/runs/{}/jobs | List the jobs of one workflow run. | GitHub Actions: runners, runs, workflows, secrets and variables have no forge-neutral model — a backend with its own CI needs its own mapping, and BoB's CI locus (docs/reference/ci-locus.md) is GitHub-only today | scripts/dashboard/server.js scripts/fleet/ci-locus.js | runbooks/self-hosted-runner.md |
api repos/{}/actions/variables/{} | Read one Actions variable (CI_RUNNER_LABEL). | GitHub Actions: runners, runs, workflows, secrets and variables have no forge-neutral model — a backend with its own CI needs its own mapping, and BoB's CI locus (docs/reference/ci-locus.md) is GitHub-only today | scripts/fleet/ci-locus.js scripts/fleet/ci-rollout.js scripts/fleet/runner.sh | — |
api repos/{}/actions/workflows | List the repo's workflows. | GitHub Actions: runners, runs, workflows, secrets and variables have no forge-neutral model — a backend with its own CI needs its own mapping, and BoB's CI locus (docs/reference/ci-locus.md) is GitHub-only today | scripts/fleet/ci-rollout.js | — |
api repos/{}/branches/master/protection | Read master's branch protection. | GitHub's branch-protection model; a backend's protected-branch settings do not share its shape. | — | registry/runbooks/bob-attack-surface-review.md |
api repos/{}/branches/{}/protection | Read or replace a branch's protection (-X PUT --input). | GitHub's branch-protection model; a backend's protected-branch settings do not share its shape. | scripts/fleet/branch-protection.js | — |
api repos/{}/branches/{}/protection/required_status_checks | Read a branch's required status checks (merge-on-green's base-side read). | GitHub's branch-protection model; merge-on-green falls back to .github/required-checks.json when it cannot read this. | scripts/promotion/merge-on-green.sh | — |
api repos/{}/commits/{}/check-runs | List the check runs reported on one commit. | GitHub Checks: check runs come from GitHub Apps and Actions and have no forge-neutral model — a backend with its own CI needs its own mapping | — | docs/how-to/promotion-pipeline-setup.md |
api repos/{}/hooks | List the repo's webhooks. | GitHub's webhook model and event names; a backend's hooks carry different events and payloads. | — | skills/todo-writing/references/platform-recipes.md |
api repos/{}/pages | Read the repo's GitHub Pages site. | GitHub Pages; BoB publishes its sites through Cloudflare, and this read is a docs example. | — | skills/todo-writing/references/platform-recipes.md |
api repos/{}/rulesets | List the repo's rulesets. | GitHub rulesets have no forge-neutral equivalent. | — | skills/todo-writing/references/platform-recipes.md |
api user/installations | List the GitHub App installations the token can see. | GitHub Apps. | — | registry/runbooks/bob-attack-surface-review.md registry/runbooks/drill-farm-compromise.md |
api {} | A call whose endpoint is held in a variable (a docs example of gh api <path>). | The endpoint cannot be read from the tree, so the contract cannot promise it. | — | docs/reference/forge-health.md |
api {}/{}/settings/billing/actions | Read an owner's Actions minutes against the included quota (#1695). | GitHub billing for Actions minutes. | scripts/fleet/actions-budget.js | — |
api {}/{}/settings/billing/usage | Read an owner's billing usage (#1695). | GitHub billing. | scripts/fleet/actions-budget.js | — |
auth refresh | Add OAuth scopes to the token (-h host -s user,…). | GitHub OAuth scopes; a backend's token permissions are named differently. | scripts/fleet/actions-budget.js | commands/what-next.md docs/reference/actions-budget.md |
run list | List workflow runs for a branch, commit or workflow. | GitHub Actions: runners, runs, workflows, secrets and variables have no forge-neutral model — a backend with its own CI needs its own mapping, and BoB's CI locus (docs/reference/ci-locus.md) is GitHub-only today | .github/workflows/auto-release.yml scripts/asks/done-check.js scripts/brief/completion.js scripts/dashboard/project-detail.js scripts/dashboard/series.js scripts/dashboard/server.js scripts/fleet/ci-health.js scripts/fleet/ci-locus.js scripts/fleet/ci-rollout.js scripts/release/release.sh scripts/rituals/evidence.js | docs/explanation/bob-viz.md docs/explanation/warp-drive-internals.md docs/how-to/promotion-pipeline-setup.md docs/how-to/versioning.md docs/reference/ci-health.md docs/reference/project-detail.md docs/reference/series.md runbooks/local-delivery.md runbooks/self-hosted-runner.md |
run rerun | Re-run a workflow run (--failed). | GitHub Actions: runners, runs, workflows, secrets and variables have no forge-neutral model — a backend with its own CI needs its own mapping, and BoB's CI locus (docs/reference/ci-locus.md) is GitHub-only today | scripts/asks/done-check.js scripts/fleet/ci-locus.js | docs/reference/ci-locus.md docs/reference/todo-format-contract.md runbooks/self-hosted-runner.md |
run view | Read one workflow run, its jobs or its log. | GitHub Actions: runners, runs, workflows, secrets and variables have no forge-neutral model — a backend with its own CI needs its own mapping, and BoB's CI locus (docs/reference/ci-locus.md) is GitHub-only today | scripts/asks/done-check.js scripts/fleet/ci-locus.js | docs/reference/ci-locus.md registry/runbooks/github-pat-rotation.md runbooks/self-hosted-runner.md |
run watch | Wait for a workflow run to finish (--exit-status). | GitHub Actions: runners, runs, workflows, secrets and variables have no forge-neutral model — a backend with its own CI needs its own mapping, and BoB's CI locus (docs/reference/ci-locus.md) is GitHub-only today | — | registry/runbooks/cloudflare-api-token-rotation.md registry/runbooks/github-pat-rotation.md |
search issues | Search issues across an owner or repos with GitHub query syntax (the briefing's sources). | GitHub's search API and query syntax (is:, label:, repo:, owner-wide) — a backend has no equivalent; callers would have to move to per-repo issue/pr list filters | scripts/brief/brief.js scripts/dashboard/briefings.js scripts/dashboard/notices.js scripts/dashboard/reports.js scripts/dashboard/series.js scripts/forge/cache.js scripts/warp-drive/preflight-todo-cleanup.js | docs/explanation/bob-viz.md docs/reference/briefing.md docs/reference/notices.md docs/reference/reports.md docs/reference/series.md |
search prs | Search pull requests across an owner (the briefing's merged and open PRs). | GitHub's search API and query syntax (is:, label:, repo:, owner-wide) — a backend has no equivalent; callers would have to move to per-repo issue/pr list filters | scripts/brief/brief.js | docs/reference/briefing.md |
secret list | List a repo's or org's Actions secrets by name (never values). | GitHub Actions: runners, runs, workflows, secrets and variables have no forge-neutral model — a backend with its own CI needs its own mapping, and BoB's CI locus (docs/reference/ci-locus.md) is GitHub-only today | scripts/asks/done-check.js scripts/docs-site/init.sh scripts/security/cred-check.js scripts/security/secrets.js | docs/reference/bob-secrets.md docs/reference/secrets-inventory.md registry/runbooks/bob-attack-surface-review.md registry/runbooks/bridge-secret-rotation.md registry/runbooks/cloudflare-api-token-rotation.md registry/runbooks/drill-lost-laptop.md registry/runbooks/github-pat-rotation.md registry/skills/secrets-management/SKILL.md registry/skills/tailscale-remote-ops/SKILL.md skills/todo-writing/assets/todo-template.md skills/todo-writing/references/platform-recipes.md |
secret set | Set an Actions secret. | GitHub Actions: runners, runs, workflows, secrets and variables have no forge-neutral model — a backend with its own CI needs its own mapping, and BoB's CI locus (docs/reference/ci-locus.md) is GitHub-only today | scripts/docs-site/init.sh scripts/fleet/distribute-secret.sh scripts/security/cred-check.js scripts/security/secrets.js | docs/how-to/promotion-pipeline-setup.md docs/how-to/secrets-check.md docs/reference/bob-secrets.md docs/reference/branch-protection.md docs/reference/secrets-inventory.md registry/runbooks/bridge-secret-rotation.md registry/runbooks/cloudflare-api-token-rotation.md registry/runbooks/github-pat-rotation.md registry/skills/docs-site/SKILL.md registry/skills/secrets-management/SKILL.md skills/todo-writing/references/platform-recipes.md |
variable delete | Delete an Actions variable. | GitHub Actions: runners, runs, workflows, secrets and variables have no forge-neutral model — a backend with its own CI needs its own mapping, and BoB's CI locus (docs/reference/ci-locus.md) is GitHub-only today | scripts/fleet/runner.sh | — |
variable list | List the repo's Actions variables. | GitHub Actions: runners, runs, workflows, secrets and variables have no forge-neutral model — a backend with its own CI needs its own mapping, and BoB's CI locus (docs/reference/ci-locus.md) is GitHub-only today | — | registry/runbooks/bob-attack-surface-review.md skills/todo-writing/references/platform-recipes.md |
variable set | Set an Actions variable (CI_RUNNER_LABEL, #1728). | GitHub Actions: runners, runs, workflows, secrets and variables have no forge-neutral model — a backend with its own CI needs its own mapping, and BoB's CI locus (docs/reference/ci-locus.md) is GitHub-only today | scripts/fleet/runner.sh | docs/reference/branch-protection.md skills/todo-writing/references/platform-recipes.md |
workflow run | Dispatch a workflow. | GitHub Actions: runners, runs, workflows, secrets and variables have no forge-neutral model — a backend with its own CI needs its own mapping, and BoB's CI locus (docs/reference/ci-locus.md) is GitHub-only today | — | docs/how-to/skill-authoring.md registry/runbooks/cloudflare-api-token-rotation.md registry/runbooks/github-pat-rotation.md |
Related
- Forge Health — the shim every call goes through, and the cache, queue and budget behind it.