The pull request check
The two checks Madebook posts on a pull request — madebook/supervised and madebook/compliance — what each means, how to require them, how Madebook verifies the host actually enforces them, the GitHub App, and how it confirms who installed it.
Madebook posts two checks on a pull request. They answer different questions, and it matters which one you require.
| Check | Question | Turns red when |
|---|---|---|
madebook/supervised |
Did this work go through Madebook? | Nobody reported it through the MCP, or a critical control fired. |
madebook/compliance |
Does this exact commit meet the rules this organization set? | Anything the rules require — evidence, independent approvals, owners, an open decision, an approved contract — is missing, stale, unverified or unresolved, and no valid exception covers it. |
madebook/supervised is deliberately narrow: it says work did not go around Madebook. madebook/compliance is the one to require when you want Madebook to block merges; what it evaluates is on The compliance check.
Both are verdicts about a pull request, not changes to it. Madebook never writes to your code — no pushes, no branches, no pull requests, no merges.
What turns madebook/supervised green
A session has to claim the pull request. That happens when an agent names it in a change report — pull_request: { number, url, title } on madebook_change_report.
If Madebook is unreachable, or the token is wrong, or the hooks never fire, edits go unreported and the status stays red until a session claims it. Silence is never green: a check that did not run reads as absent, not as passing.
It is switched on per repository with Require supervision on the workspace's Repositories page (see Connecting a repository).
What turns madebook/compliance green
Every item the rules require is met, or waived by an approved, unexpired exception, on the head commit. Evidence on an older commit, an approval by the author, an approval withdrawn by a later request for changes, or a check whose kind Madebook had to guess — none of it counts. When Madebook cannot establish something, the item is unknown and the verdict fails.
It is switched on per repository on the workspace's Compliance page: monitor posts a neutral check and blocks nothing; enforce posts a failure.
Making it binding
Require the check in GitHub branch protection or a ruleset: Settings → Branches → Require status checks to pass, and pick the check. Once required, a pull request that fails it cannot be merged. That is a setting on your repository, not on Madebook.
Two things are new about this:
Require it from the App. Anyone with write access to a repository can post a commit status with any name. A check is only proof when GitHub pins it to the Madebook GitHub App — which is why a repository connected with a personal access token can be monitored but never enforced.
Madebook checks that you did. For every repository in enforce mode, Madebook reads the host's branch protection and rulesets for the default branch and reports one of:
| Reading | Meaning |
|---|---|
| enforced | madebook/compliance is required, from the Madebook App, in an active rule. |
| misconfigured | Enforce is on here, but the host does not require the check, does not pin it to the App, or only evaluates the rule. The findings name the fix. |
| monitoring | The repository is in monitor mode. |
| unavailable | Madebook cannot read the configuration — a permission, a plan, or a host it cannot verify yet. GitLab and Azure DevOps read as unavailable today; their supervised status still posts, but Madebook cannot say whether it is required. |
Bypass — administrators exempt, ruleset bypass actors — is reported alongside. The reading refreshes every six hours, on protection-change webhooks and on demand; a change is audited and raised in Attention, and step 5 of the governance journey is not done until every enforced repository reads enforced.
Two ways to post it
| Personal access token | The Madebook GitHub App | |
|---|---|---|
| Who it belongs to | One engineer. It dies when they leave and expires when they set it to. | The organization. Installed by a GitHub org admin. |
| Webhooks | Pasted per repository, with a secret per repository. | One endpoint for every repository the installation covers. |
| The verdicts | Commit statuses, posted as that person. | Check runs posted as Madebook, each with a summary page explaining the colour. |
| Can be enforced | No — a status posted by a person cannot be pinned. | Yes. |
| Repositories Madebook can see | Whatever that person can see. | Exactly what the admin selected at install time. |
Personal tokens keep working with no App registered. See Connecting a repository.
The GitHub App
There is one App per Madebook deployment, registered once by a platform admin. Each customer organization then installs it.
Before you start
- Madebook must know its own public API address — an address GitHub can reach, such as
https://madebook.acme.example/api. It comes from Bookbag's app registry (the API base registered for Madebook under Platform admin → Apps at Bookbag).MADEBOOK_PUBLIC_URLon the host overrides it for local development, in either form:https://madebook.acme.example(/apiis appended) orhttps://madebook.acme.example/api(used as given). Admin → Integrations shows the URLs you paste into GitHub, and only once the address is known. Copy them from there. MADEBOOK_ENCRYPTION_KEYmust be set. The private key, webhook secret and client secret are stored encrypted and the store refuses plaintext.
The quick way
Admin → Integrations → The Madebook GitHub App → Create the App on GitHub does everything below in one press: Madebook sends GitHub a manifest with the URLs, permissions and events already filled in; you confirm on GitHub; and the App's id, slug, private key, webhook secret, client ID and client secret are stored on the way back. The manifest also switches on Request user authorization (OAuth) during installation, so an App created this way confirms its installers from the first install. Then Verify with GitHub. See Integrations.
Creating the App by hand
Your organization → Settings → Developer settings → GitHub Apps → New GitHub App. A personal account works too; an organization-owned App is the usual choice for a company deployment.
| Field | Value |
|---|---|
| GitHub App name | Madebook, or Madebook — Acme if the name is taken. Names are global. |
| Homepage URL | Your Madebook URL |
| Callback URL | <API base>/code/api/github/app/callback — for example https://madebook.acme.example/api/code/api/github/app/callback |
| Request user authorization (OAuth) during installation | Ticked — required; see Confirming who installs it below |
| Expire user authorization tokens | Either. Madebook revokes the user token as soon as it has used it. |
| Setup URL | Leave empty. GitHub disables it once OAuth during installation is ticked, and sends the browser to the Callback URL instead. |
| Webhook → Active | Ticked |
| Webhook URL | <API base>/code/api/github/app/webhook |
| Webhook secret | A long random string. Keep it — you paste it into Madebook next. |
Repository permissions:
| Permission | Access | Why |
|---|---|---|
| Contents | Read | Index the tree and read files |
| Metadata | Read | Mandatory. List repositories. |
| Pull requests | Read | Mirror pull requests and reviews |
| Checks | Write | Post the madebook/supervised and madebook/compliance check runs |
| Commit statuses | Write | Fallback where check runs are unavailable |
| Administration | Read | Read branch protection and rulesets, so Madebook can say whether madebook/compliance is actually required and who can bypass it |
Organization permissions:
| Permission | Access | Why |
|---|---|---|
| Members | Read | Resolve @org/team owners in approval rules and CODEOWNERS |
Subscribe to events: Pull request, Pull request review, Check run, Check suite, Status, Push, Branch protection rule, Repository ruleset. (Installation events are delivered to every App and cannot be selected.)
Where can this App be installed: Any account, so every customer organization can install it.
Then note the App ID and the Client ID (under About) and the slug — the last part of https://github.com/apps/<slug>. Under Client secrets, generate one and copy it (GitHub shows it once). Under Private keys, generate one; GitHub downloads a .pem file.
Registering it in Madebook
Admin → Integrations → The Madebook GitHub App. Paste the App ID, the slug, the whole .pem file, the webhook secret, the client ID and the client secret. Save, then Verify with GitHub: Madebook signs a JWT with the key, asks GitHub who the App is, fills in the client ID if it was left empty, and lists any permission or event still missing, by name. Fix those on GitHub before installing anywhere.
The private key, webhook secret and client secret are write-only. The page shows a fingerprint of the key and whether each secret is stored — never the values. Paste a new one to replace it.
GitHub Enterprise Server: open GitHub Enterprise Server? on the same panel and set the API base (https://github.example.com/api/v3) and the web base (https://github.example.com). Everything else is the same. If the server is on a private network, the operator lists it in MADEBOOK_PRIVATE_HOSTS.
Confirming who installs it
GitHub sends the browser back to Madebook with the installation id in the query string. Madebook's signed state proves which Madebook organization started the install, but says nothing about that id — and installation ids are small numbers that can be guessed. Checking the id with the App's own credentials only proves it is an installation of this App, anybody's. Left there, an admin of one Madebook organization could put another company's installation id into the callback and have Madebook read that company's repositories.
So Madebook does what GitHub recommends. With Request user authorization (OAuth) during installation on, GitHub asks the installer to authorize the App and adds a one-time code to the callback. Madebook exchanges it, with the App's client ID and secret, for a user token; asks GitHub which installations of this App that person can access; and connects the installation only if it is on that list. Whose installation it is comes from GitHub's answer, not the query string. The user token is revoked straight away and is never stored, logged, audited or shown.
Anything else is refused and nothing is connected: no code, no client credentials stored, a code GitHub will not exchange, a lookup that fails, or an installation the person cannot access. The browser comes back with the reason, and every refusal is written to the audit log as integration.app_claim_refused with the installation id and the reason. No token is ever in the entry.
An App registered before this check
Such an App has the OAuth setting off, and Madebook holds no client secret for it — so every new install is refused, deliberately, until a platform admin does this once. Installations already connected keep working; nothing about minting their tokens changes.
The App card on Admin → Integrations opens with a checklist: whether the client ID and secret are stored, what the last install showed about the OAuth setting (GitHub's API does not report it, so the card says on when the last install carried a code and off when it did not), and the exact Callback URL to enter, with a copy button. While the client credentials are missing, the organization's Install button is disabled and says why.
- On GitHub, open the App's settings. For an App owned by a personal account: Settings → Developer settings → GitHub Apps → the App → Edit.
- Under Identifying and authorizing users, tick Request user authorization (OAuth) during installation and set the Callback URL to
<API base>/code/api/github/app/callback(Admin → Integrations shows it with a copy button). Save. - On the same page under Client secrets, press Generate a new client secret and copy it. GitHub shows it only once.
- In Madebook, Admin → Integrations → The Madebook GitHub App: paste the Client ID (from the App's About section) and the client secret, Save, then Verify with GitHub.
The next install shows the OAuth setting as on.
Installing it for an organization
From the organization's Connections page (Repositories tab, Option A · Install the Madebook GitHub App), or a workspace's Repositories page.
Madebook sends the browser to GitHub with a signed state naming the organization and the person who clicked. A GitHub org admin chooses All repositories or Only select repositories, confirms, and authorizes the App. GitHub sends the browser back to the Callback URL with the one-time code; Madebook confirms that person can access the installation, confirms the installation id with the App's own credentials, records the installation against the organization, and returns to Connections.
Then pick the repositories the workspace reads from the list — the list is what the installation covers. Each is indexed immediately.
To change which repositories are covered, use Change which repositories on GitHub. That is GitHub's decision to keep. Repositories removed from the installation stop syncing and say so on the Repositories page.
What arrives by itself
- Webhooks — pull requests, reviews, check runs, statuses, pushes to the default branch, and branch-protection and ruleset changes, for every covered repository, signed with the App's webhook secret. Nothing to paste per repository.
- Both verdicts as check runs, each with a summary page explaining the colour.
- Enforcement readings — because the App can read branch protection.
- Installation lifecycle — uninstalling or suspending on GitHub marks the connection accordingly here. Nothing keeps claiming a connection GitHub has taken away.
What it never does
Reads only. Contents: read means Madebook cannot push, branch, or open pull requests, and the write half of the source-control interface stays off. Installation tokens are minted from the private key when needed, live an hour, are cached in memory, and are never written to the database.
CI results and approvals become evidence
Until a repository is wired for CI, every test result Madebook holds is the agent's own account of its own work — stored as reported, marked provisional by the evidence gate, and counted by the compliance check for nothing.
Two feeds change that.
Webhook (the fast path). With the App, it is already wired. With a token, the repository page's CI and approvals panel shows the payload URL and secret. Add it under the repository's Settings → Webhooks, content type application/json, events check_run, check_suite, pull_request, pull_request_review. Payloads are signed with HMAC-SHA256 over the raw bytes, and a wrong or missing signature answers 404 — deliberately. Until Madebook knows its own public API address (registered at Bookbag, or MADEBOOK_PUBLIC_URL as an override), the page cannot tell you the full URL to paste.
Pull (works anyway). A background sync fetches the same facts outbound, on the credential the repository already uses. Judged repositories re-sync every ten minutes regardless of webhooks, so a delivery that never arrived is found on the next pass.
How each becomes evidence
| What arrives | Becomes |
|---|---|
| A check run | observed evidence, tied to its repository, pull request, commit, issuer and attempt |
| A pull request approval | attested evidence, tied to the commit it approved |
An approval is the human review, so it is recorded as one — and a human_review requirement is satisfied by nothing else. A later request for changes, a dismissal, or (by default) a new commit withdraws it.
A check whose name Madebook does not recognise is recorded as kind build and satisfies no requirement. That is deliberate: a guessed kind closes a gate and reads exactly like proof. A scanner passing is shown, and it is still not a security review. A check mapping on the repository's Compliance settings says which check, from which issuer, is which kind.
Troubleshooting
| Symptom | Cause |
|---|---|
| The Install button is missing | No App is registered, or Madebook's public API address is not known (not registered at Bookbag, and no MADEBOOK_PUBLIC_URL override). Admin → Integrations says which. |
| The Install button is disabled | The App has no client ID and secret stored. See An App registered before this check. |
| GitHub returns with "did not complete" | The installation id could not be confirmed with the App's credentials — usually a wrong App ID, or a key from a different App. Verify on the admin panel. |
| "GitHub did not say who installed the App" | Request user authorization (OAuth) during installation is off on GitHub, or the Callback URL is not Madebook's. |
| "does not have access to that installation" | The GitHub account that authorized cannot manage that installation. Install again signed in to GitHub as an admin of that GitHub organization. |
| "would not confirm who installed the App (incorrect_client_credentials)" | The client ID or secret stored in Madebook is wrong or was regenerated on GitHub. Enter the current ones. |
Verify says checks: write, administration: read or members: read is missing |
The App was created without it. Edit the App's permissions on GitHub; existing installations are asked to accept. |
| Enforcement reads misconfigured | The host does not require madebook/compliance, or requires it without pinning it to the App. The reading's findings name the fix. |
| Enforcement reads unavailable on GitLab or Azure DevOps | Madebook cannot verify protection on those hosts yet. |
| Webhook deliveries show 404 on GitHub | Wrong or missing webhook secret. Madebook answers 404 to any unsigned delivery, deliberately. |
| A repository says the installation no longer includes it | It was removed from the installation on GitHub. Add it back there, or disconnect it here. |