Govern
Run manifest
Every run freezes a snapshot of exactly what it was authorized to do the instant it was created — not whatever your config says right now. That snapshot is stored on the run record (metadata.run_manifest) and, when the run is actually dispatched to a runner, copied onto the queued job too. Admin can pull it up for any run, forever.
What it is
At createRunCtx — the single function every run creation path (REST, streaming, cron, A2A delegation)
goes through — the plane resolves the agent, the runner it will dispatch to, its declared tool allowlist and
connector needs, whether connector policy is fail-closed, and who asked for the run. That resolved set of facts
is written once, as run.metadata.run_manifest, and copied verbatim onto the queued job the runner
receives. Neither copy changes after that point, even if you edit langgraph.json, rotate a grant,
or flip policy on a minute later.
Why it is here
"What was this agent even allowed to do when it ran?" is a question every audit, incident review, and support ticket eventually asks. Live config answers "what's allowed right now" — which is the wrong answer once anything has changed since. A frozen per-run record is the only honest answer to a question about the past.
What's in it (schema v1)
{
"schema_version": 1,
"captured_at": "2026-09-05T02:58:06Z",
"tenant_id": "acme",
"agent_id": "sales-bot",
"agent_version": 3,
"runner_kind": "python-langgraph",
"connector_needs": ["salesforce"],
"allowed_tools": ["lookup_account"],
"policy_fail_closed": true,
"principal": { "identity": "alice", "permissions": ["runs:create"] },
"parent_run_id": null,
"depth": 0
}
agent_version— the registry version dispatched, so "which build of the agent actually ran" survives a later redeploy.allowed_tools— absent means no in-graph tool allowlist was configured (unrestricted); present, including an empty list, means the runner was told to refuse every tool call not on it.policy_fail_closed— whether the connectorpolicysection was configured for this dispatch (true= a policy engine was active; it does not mean every call was denied). Specific allow/deny/pending decisions are still Grants & HITL and PDP / Cedar — see Audit for the decision log.principal— omitted entirely when no auth provider is configured (open local/dev mode), not a blank record.
In the product
What to expect
- Automatic, no config. Every run gets one in metadata — there's nothing to turn on, and nothing changes about how you write an agent. LLM cache hits get a manifest too (no runner dispatch, so nothing is copied onto a
RunAssignment). - A record, not a gate. This is v1: it captures intent, it does not evaluate sequences of calls or block anything on its own. Reasoning about a sequence of tool calls in flight and denying a suspicious one before it runs is a different, heavier control than a per-run snapshot. Runkite's existing fail-closed grants, mandatory HITL, and break-glass are the enforcement layer; the manifest is what you check afterward to see what any of them were actually working with.
- Lives in existing storage. No new database column or migration — it rides inside the
metadataJSON column every run already has, so it works on every state backend the same way. - API-visible from day one.
GET /admin-api/runs/{id}has always returned it (it's just a key inmetadata); the Admin UI card in this release is what makes it visible without reading raw JSON.
Reference: docs/trust-governance.md · Grants & HITL · Admin UI guide → Threads & Runs · Limitations