Skip to content
EvalBook

MadeBook as a peer

How an EvalBook report becomes evidence on a MadeBook pull request — re-fetched and re-hashed, trusted only as far as its assurance — and how EvalBook asks MadeBook before it changes protected configuration.

EvalBook measures; MadeBook authorizes. When a MadeBook workspace is linked to an EvalBook project, two things happen and nothing else: EvalBook's reports become evidence on the workspace's pull requests, and EvalBook asks MadeBook before it changes its own protected configuration. An EvalBook pass never approves anything by itself. MadeBook's side shipped and is deployed; its own page is at madebook.ai/docs/governance/evalbook-evidence.

Status (25 October 2026). MadeBook's half is deployed and verified against an EvalBook stand-in. On EvalBook's side the decision flow (ask, re-read, resolved event, unlink governance) is built and tested against a MadeBook stand-in; serving reports for a subject is routed but not yet marked implemented in the requirements ledger and is being finished now. The two products have never talked to each other for real.

A report is a lead until MadeBook has re-fetched it

EvalBook sends evaluation.completed to linked MadeBook workspaces too. MadeBook never uses the verdict in that message. It asks EvalBook which reports exist for the pull request's head commit:

POST {evalbook}/project/api/peer/reports/forSubject
{ "organization_id": 12, "origin": { "app": "madebook", "workspace_id": "88" },
  "repository_full_name": "acme/api", "commit_sha": "<40 hex>" }

EvalBook answers the reports of projects linked to that workspace: { id, digest, suite_ref, result, evidence_complete, assurance, issued_at, valid_until, url }. MadeBook then fetches every one again by id (POST …/peer/report), recomputes the digest over the canonical body it received, and counts a report only when its content hashes to the digest named for it, it is about this organization, this repository and this exact head commit, it is in a schema MadeBook reads, inside its validity window and not superseded, and a pass does not contradict itself (required checks passed, evidence complete, nothing missing in the coverage). Anything else is refused and shown with its reason on the pull request. EvalBook not answering leaves the commit not established, which fails; never "no report".

The body a report must have for this: schema_version "1.0", the report id, organization_id, subject.repository_full_name, subject.commit_sha (40 hex), suite_ref.id, result in pass | fail | error | inconclusive, a boolean evidence_complete, valid_until, every check's status, required and assurance, and coverage.missing. Supersession is never inside the body; it is the envelope's status and superseded_by, so a digest never changes after issue. See Reports.

Trust comes from EvalBook's assurance

EvalBook's assurance Recorded in MadeBook as Counts toward "suite N must pass"
platform_verified — EvalBook ran the suite itself observed yes
anything else (reported, relayed, unspecified) reported no; shown, labelled as not counting

No signature promotes a report further, and nothing the report says about itself does either.

"EvalBook suite N must pass"

On the workspace's Compliance → EvalBook tab in MadeBook, a requirement EvalBook suite 5 must pass is a draft until somebody other than its proposer approves it. Each approved requirement is one item of MadeBook's compliance check: met by the newest current, verified, platform-verified pass on this commit; unmet by a failing, inconclusive, weaker, refused, superseded or expired report or by no report; unknown when there is no link or EvalBook did not answer, and unknown fails. An older pass never outvotes a newer failure on the same commit, and a push supersedes EvalBook evidence at once.

EvalBook asks before it changes protected configuration

Seven kinds of change are governed actions: activate_suite_version, change_threshold, activate_scorer_version, activate_judge_configuration, modify_protected_dataset, change_holdout, unlink_governance. In a linked project, EvalBook asks MadeBook for a decision bound to the exact change:

POST {madebook}/workspace/api/peer/evalbook/decision
{ "organization_id": 12, "sub": "7", "workspace_id": "88",
  "origin": { "app": "evalbook", "workspace_id": "31" },
  "idempotency_key": "evalbook:activation:suite-version:204",
  "action": { "kind": "activate_suite_version",
              "target": { "type": "suite_version", "id": 204, "suite_id": 5 },
              "digest": "sha256:…", "summary": "Lower pass threshold 0.9 → 0.85",
              "author_subs": ["9"], "requested_by_sub": "7" } }

MadeBook answers allow, deny, require_approval or defer, with reasons as sentences and parallel reason_codes, an action_digest, a valid_until, obligations on allow, and its policy. The rules on both sides:

  • Nothing defaults to allow. An unknown kind, a missing digest or a target that names nothing is denied. With no policy approved, every kind needs one independent approval.
  • The authors of a change, its requester and the person EvalBook acted for never approve it.
  • An allow is bound to the digest and to a window. A defer is answered but not stored, so EvalBook asks again and does not act meanwhile. An unreachable MadeBook is defer: fail closed.
  • On require_approval, MadeBook opens a decision settled by votes, then sends evalbook_decision.resolved to EvalBook's events receiver. EvalBook acts on only the decision id: it re-reads POST …/evalbook/decision/status and activates only on allow for the same action_digest before valid_until. An expired allow, a lapsed approval or an ended link reads as deny.

The owning component performs the activation only when called with an approved action whose digest still matches what it re-derives from the rows; it never activates from the event. See Suites and thresholds and Protected data and governance.

Unlinking is governed by EvalBook

Before EvalBook ends a link to a MadeBook workspace it asks for an unlink_governance decision; without allow the link stays, and the page says to unlink from MadeBook. After any unlink, actions that were pending a MadeBook decision stay blocked and are re-requested by a person under the standalone separation-of-duties rule, never silently activated.

What MadeBook does not get

MadeBook does not learn whether EvalBook actually executed an allow: the contract has no execution receipt yet, and MadeBook's decision page says "allowed, execution not reported". MadeBook has proposed an evalbook_decision.executed event; it is a contract change EvalBook has not adopted.