Skip to content

Chapter 17. Policy Layer and Capability Catalog

How to read this chapter

It helps to keep one practical question in mind rather than an abstract policy topic:

  • who decides whether the same support agent may start this run at all;
  • who decides whether it may read context, open a ticket, or write into memory;
  • where those decisions should live so they do not dissolve into orchestration code.

If those answers are hidden in random code paths, the runtime exists, but the contract core of the system is still missing.

1. Why a Reference Runtime Is Still Too Naive Without a Policy Layer

Even if you already have a clean runtime loop, that is still not enough. Without an explicit policy layer, the system remains too trusting:

That is the distinct promise of this chapter. It should help the reader see where the contract core of the runtime really lives, and why a runnable system still remains immature until admissibility, risk, and capability control stop hiding in orchestration code.

In the running support case, this appears immediately after Chapter 16. The runtime can already accept a request, assemble context, call the model, and reach the gateway. But the moment the agent is about to open an urgent ticket, write a summary into memory, or request another external step, the system needs more than a loop. It needs an explicit decision about admissibility and risk.

  • you cannot reliably distinguish allowed runs from forbidden ones;
  • tool calls are hard to control consistently;
  • memory writes live on scattered conventions;
  • product-specific restrictions leak into orchestration code very quickly.

That is why the next mandatory layer in the reference implementation is the policy layer.

Its job is not to "slow the system down". Its job is to make decisions about access, risk, and admissibility explicit instead of scattering them across random if branches.

That is also why this chapter should be read as a governed-decision chapter, not only as a policy-page chapter. The real question is not whether the team wrote some rules down. It is whether the runtime now has a reviewable contract core that can explain why a run was allowed, paused, denied, narrowed, or escalated.

If you want to see how that governed policy decision later stays connected to traces, approvals, eval judgments, incidents, and rollout, use the dedicated Evidence Spine page.

Need the contract layer?

For the more applied view, open the Policy Bundle Schema and Approval Contract, the Approval Request and Decision Record Schema, and the Reference Package.

2. A Policy Layer Should Answer Small and Clear Questions

A weak policy layer tries to become "the smart brain of the system". A strong policy layer does the opposite: it solves a limited set of clear questions.

For example:

  • can this run start at all;
  • can this context be read;
  • can this capability be invoked;
  • is approval required;
  • can this be written into memory;
  • can this result be returned outward;
  • which verifier contracts may be trusted when high-risk rollout or assurance decisions depend on graded evidence.

When those questions are explicit, the runtime becomes easier to explain, and guardrail changes stop being chaotic.

3. A Capability Catalog Is Not Just a Registry of Names

It is very easy to slide into a catalog that only stores a list of available tools. A good catalog does more:

  • it describes the capability contract;
  • stores the risk profile;
  • declares transport and execution mode;
  • captures idempotency expectations;
  • records ownership and lifecycle.

So the capability catalog is not "inventory for convenience". It is the central control point for platform capabilities.

Together, the policy layer and the capability catalog form the contract core of the reference implementation

flowchart LR
    A["Run request"] --> B["Runtime orchestrator"]
    B --> C["Policy layer"]
    B --> D["Capability catalog"]
    C --> E["Allow / deny / approve"]
    D --> F["Capability contract"]
    E --> G["Execution layer"]
    F --> G

Case thread: create_support_ticket as a capability

In the support-triage system, create_support_ticket should not be merely a tool name in the prompt. In the catalog it is a write capability with an owner, approval requirement, idempotency requirement, timeout/retry defaults, and brokered gateway transport. The policy layer can then say explicitly: this run may start, status reads are allowed, ticket creation requires an idempotency key and approval, and after side_effect_unknown continuation is denied until reconciliation.

Policy case-spine note: the capability catalog should encode the three canonical cases, not only ticket writing. Support triage needs write capabilities, approval requirements, and idempotency rules. Internal knowledge assistant needs read capabilities, corpus scope, memory-write permissions, and source-trust constraints. Incident coordination needs escalation capabilities, notification permissions, responder-role checks, and emergency-only policy overrides with explicit expiry.

4. A Tool Surface Is Not the Same Thing as a Governed Capability Surface

Recent OpenAI tooling guidance is useful because it makes a distinction many teams blur in practice: a model may see tools, MCP servers, hosted capabilities, or local functions, but the runtime still has to decide what kind of control surface those things belong to.2

That distinction matters.

A weak implementation says:

  • here is a list of tools the model can call.

A stronger implementation says:

  • here is the governed capability surface;
  • here is which part of it is exposed to the model;
  • here is which transport carries execution;
  • here is which capabilities are callable directly, which are brokered through a gateway, and which are not exposed at all.

This is why a capability catalog should sit above raw tool definitions. It prevents the runtime from confusing:

  • what the model can mention;
  • what the runtime can route;
  • what the platform is actually willing to execute.

5. What Is Worth Storing in a Capability Catalog

Once the platform accepts stateful MCP-style capabilities, the catalog also has to describe more than transport and risk. It should help the runtime know whether a capability is sessionless, session-bound, interruptible, or resumable across multiple turns.

That means a mature catalog may also need fields such as:

  • capability session mode: stateless / stateful;
  • whether elicitation is allowed;
  • whether progress events are emitted;
  • session expiry behavior;
  • whether resume requires a fresh approval or may continue under the existing decision;
  • whether the capability may reinitialize its remote session automatically.

Without those fields, the policy layer can approve a capability in principle but still fail to govern how its live runtime session behaves in practice.

Anthropic's workflow taxonomy adds one more missing governance dimension here.1 The policy layer should not only decide whether a capability is allowed in isolation. It should also decide which orchestration patterns are allowed to invoke it.

Their later harness work adds a closely related lesson: once a system uses planner, generator, and evaluator roles over long-running work, policy has to govern not just a tool call, but the role contract around that tool call.8 If a generator proposes the sprint, an evaluator grades it, and a planner reshapes scope, the platform needs explicit rules about who may define done-ness, who may grade quality, who may trigger a reset, and which handoff artifact the runtime recognizes as continuity input after a context reset. That artifact remains a derived, untrusted view and cannot carry authority. The Context Continuity Envelope binds it to durable control state and requires a new policy decision before execution resumes.

For example, a policy contract may need to express whether a capability is:

  • safe inside prompt chaining but not inside unconstrained loops;
  • allowed in routing only for certain classes of requests;
  • usable inside parallelization only when branches are read-only;
  • callable by delegated workers in orchestrator-workers, or restricted to the parent runtime only.

That keeps capability governance tied to runtime shape instead of pretending the same tool contract is equally safe in every execution pattern.

A practical field set usually looks like this:

  • capability name;
  • owner;
  • mode: read / write / high_risk;
  • transport: mcp / gateway / sandboxed_exec;
  • exposure: direct / brokered / restricted;
  • input schema;
  • output shape;
  • approval requirement;
  • idempotency requirement;
  • timeout and retry defaults.

With that contract, the runtime can already behave predictably instead of adapting ad hoc to every capability.

5.1. Policy chooses not only the tool, but also the hands

Once brain / hands / session are split, the policy layer gets one more responsibility: it must decide not only whether a capability is allowed, but also which hands may execute it.9

The same logical action can have different profiles:

  • read-only through a brokered internal gateway;
  • write through a high-risk tool with approval and an idempotency key;
  • shell-like operation only inside an ephemeral sandbox with no external egress;
  • delegated subagent execution without inheriting secrets by default.

So the capability catalog is stronger when it includes execution_profile, sandbox_profile_id, egress_profile, credential_scope, debug_surface, and rollback_boundary. The policy decision then becomes not just allow, but a route: which harness may continue, which hands are available, which session evidence must be recorded, and where the blast-radius boundary sits.

5.2. An analytics agent is a governed query engine, not a free SQL autopilot

One capability class deserves special treatment because it often looks harmless: an analytics agent that answers business questions, writes SQL, chooses metrics, or works through a semantic layer. In practice, this is not merely query_database. It is a governed query engine with a contract for metric meaning, data scope, allowed aggregations, query cost, and result visibility.

Modern analytics platforms are already moving in that direction. Snowflake Cortex Analyst relies on semantic views, verified query suggestions, access roles, and generated SQL executed inside Snowflake's governance boundary.10 Databricks Genie Spaces build conversational analytics around trusted assets and space instructions, while Power BI Copilot operates over a semantic model and existing workspace permissions.1112

The book does not need the vendor-specific product detail. The architectural lesson is that an analytics agent should enter the capability catalog as analytics_query, metric_explain, or semantic_model_lookup, not as a general SQL hand. Its contract should specify:

  • semantic_model_id or the allowed semantic views;
  • dataset_scope and tenant boundary;
  • allowed metrics, dimensions, and grain;
  • whether the capability may emit raw SQL or only a parameterized query plan;
  • maximum cost, row limit, and timeout;
  • masking rules, suppressed columns, and small-cell suppression;
  • whether human review is required for new metrics, cross-domain joins, or external export;
  • which query plan, generated SQL, source tables, and policy decision land in the trace.

Otherwise a "read-only" agent can still reveal sensitive slices, join domains without an owner, invent unapproved KPIs, or run expensive queries. A mature policy layer should be able to say not just "this warehouse can be read", but "this analytics question may be answered through this semantic model, under this role, with these limits and this evidence record."

6. Approval Should Look Like an Interruptible Runtime Path, Not a Side Conversation

A policy layer becomes much more real when approval is modeled as part of runtime control flow rather than as a manual process outside the system. LangGraph's interrupt model is useful here because it makes pause, review, and resume explicit runtime primitives instead of ad hoc human workarounds.3

That is the right shape for policy-heavy agent systems too.

When a high-risk capability reaches an approval boundary, the runtime should be able to:

  • pause the run;
  • surface the pending action and its context;
  • wait for an external decision;
  • resume with a structured outcome.

That is much stronger than sending a message to an operator and hoping the surrounding code still remembers what it was doing.

A useful next step is to distinguish two approval patterns:

  • direct human approval for rare or very high-risk actions;
  • classifier-mediated approval paths for repetitive low-context decisions where manual review causes approval fatigue.

That second path should not be treated as “approval removed.” It should be treated as delegated control with stricter evidence requirements:

  • what classifier or gate made the decision;
  • what evidence was visible to it;
  • when it must escalate to a human;
  • how subagent or follow-on actions inherit or lose that delegated approval;
  • which verifier contracts are trusted to grade those same high-risk paths when rollout or assurance depends on verifier output.

A partial grant must not become an authority expansion

A successful OAuth flow does not mean every requested_scopes entry appears in granted_scopes: users may decline optional permissions. Cloudflare, From all-or-nothing to task-based OAuth consent describes this behavior. The catalog defines separate required_scopes for each operation; example scope names are illustrative, not a universal OAuth vocabulary.

Before execution, the gateway computes missing_scopes = required_scopes - granted_scopes from verified authorization context. A nonempty set denies that operation. An independent permitted part of the task may continue only if this preserves task meaning and atomicity; partial progress must not be reported as full completion. Action approval cannot create a missing OAuth scope. New access consent is a separate user decision, followed by fresh policy and task-boundary checks, without automatically widening the active run.

After credential refresh, revocation, or task resume, do not rely on the old grant snapshot. Unknown or unverified permissions must not be reconstructed from the original request. Evidence fields and failure scenarios are in the policy bundle contract.

Credential ownership and type are independent axes

LangChain Connections, in the Managed Deep Agents prerelease, separates who owns a credential from how access was obtained. agent-owned means a deployment credential shared by callers; user-owned means resolving a particular user's credential at run time. Neither is identical to the account the downstream service sees.

Owner Static secret OAuth grant
Agent / deployment Shared search-service key Grant for a dedicated shared account
User Personal secret selected by verified identity Grant for the individual caller

OAuth therefore does not by itself prove on-behalf-of-user execution, and a static secret need not be shared. Connections fixes ownership at creation; runtime lookup selects an existing credential rather than changing its owner. A connection name is a configuration reference, not evidence of authority.

For a production contract, bind the verified requester, tenant, deployment, connection, and effective downstream principal. If a user grant is missing, stop the dependent operation and request access through a protected flow; never silently substitute an agent or another user's credential. On resume, revalidate identity, resource, permissions, and separate action approval. This extends the partial OAuth grant contract above rather than replacing it.

Test reads as well: identical queries against private repositories must respect each caller's visibility. For shared accounts, audit must distinguish who requested an action from whose identity executed it, without presenting a shared-account action as a personal user action. The fields and scenarios are a proposed policy bundle extension, not a Connections integration implemented in the reference runtime.

Operation, resource, and access class are separate dimensions

Cloudflare Workers granular authorization and its role documentation provide a concrete matrix instead of a single “Worker access” permission. These are minimum permissions for the listed operations; broader roles may include them, but do not automatically expand resource scope.

Operation Resource Required access class
Read settings, metrics, logs, and traces A specific Worker Metadata Read-Only
Read source code That Worker Content Read-Only
Update an existing Worker's code without route changes That Worker Editor
Delete a Worker The Worker being deleted Admin at an applicable scope
Change a Route or Custom Domain Worker and every affected zone Editor for the Worker and Workers Routes Write for each zone

A command name does not determine authority: wrangler deploy may update an existing Worker, create one, or change routes. Creation requires Admin at the Workers product scope; ordinary deployment with unchanged routes does not require zone access. The gateway must derive actions and target resources from a trusted adapter and the actual configuration, not the model's claim that this is “just a deploy.” An unresolved target set cannot authorize execution.

Do not turn this into “deployment needs permissions on every connected resource.” Cloudflare documents that deploying a Worker with KV/R2/D1 bindings does not require separate permissions on those resources; direct access is checked separately. However, permission to change code may allow use of existing bindings through the Worker. Thus preventing deletion does not make deployment safe: code review, runtime access limits, and separate approval for high-risk changes still matter. Similarly, telemetry access does not make logs non-sensitive: secrets and personal data need filtering and access controls.

For a compound change, check every required action–resource pair; a Worker role is not zone-management authority. Evidence and negative checks appear in the proposed policy-bundle extension.

Implementation limit: the detailed Workers documentation explicitly says Custom Domains do not yet support per-Worker roles. The matrix describes necessary access classes, not support for every scope–operation combination. Check the target API capabilities; missing support is not grounds to automatically broaden access to product scope.

7. A Policy Decision Should Be an Object, Not Just a Bool

A very useful engineering habit: do not reduce policy decisions to True/False.

It is often much more useful to return something like:

  • allow
  • deny
  • approval_required
  • sanitize_and_continue
  • escalate

And additionally:

  • reason code;
  • policy id;
  • risk class;
  • optional constraints;
  • allowed orchestration patterns or explicit pattern restrictions;
  • trusted verifier contracts or verifier-contract requirements for high-risk paths;
  • optional approval or resume requirements.

That greatly improves explainability and makes telemetry far more useful.

7.1. Policy Should Govern Not Only Permission, But Also Control Response

DeepMind's AI Control Roadmap usefully names one more responsibility for the policy layer: a control system should not only decide whether an action is allowed, but also select the response mode.5 In their model, control is built on threat modeling, monitoring agent actions/plans, supervisor systems, prevention/response gates, and metrics such as coverage, recall, and time-to-response.

For a reference runtime, that means a policy decision should be able to return not only allow or deny, but also a control response:

  • allow_with_monitoring for low-risk and reversible actions;
  • pause_for_supervisor for actions that need human or classifier-mediated review;
  • block_synchronously for high-risk actions before they reach the outside world;
  • quarantine_session for behavior that looks like misinterpretation, overeagerness, or boundary probing;
  • escalate_incident for events where control has already detected possible harm.

This is an important boundary: not every danger looks like adversarial misuse. Sometimes an agent simply over-optimizes a local goal, misunderstands the task, or continues down a path that looks productively useful but is systemically destructive. The policy layer should therefore bind the decision to a response, owner, evidence, expected response time, and audit trail.

AWS AgentCore Gateway shows a practical version of this layer around MCP tools: policy and Lambda interceptors sit outside the model loop, so the control path does not depend on how the agent explains its next call.6 Cedar policy provides a deterministic allow/deny decision over principal, action, resource, and context, while request/response interceptors handle the dynamic part: bearer-token validation, act-on-behalf token exchange, context injection, tool authorization, payload transformation, and response filtering. For a reference runtime, the useful lesson is that a tool call should pass through a pre-call decision point, downstream call, and post-call response filter, while the trace stores the policy decision, denial reason, sanitized request/response, interceptor version, and audit event.

7.2. Policy must evaluate the trajectory, not only the action

Actions that are legitimate in isolation can form an unsafe sequence. Reading account details and making a transfer may each be allowed, but a transfer to details that do not match the earlier read must be denied. Several purchases may each remain below an approval threshold yet exceed the budget together; an otherwise valid retry becomes an unsafe loop once the cumulative allowance has been exhausted.7

Before the next external action, policy must therefore evaluate more than the current request. It needs value bindings and normalized fingerprints for significant fields, the cumulative budget, required predecessor steps and their order, approval state, time window, and policy version. The trusted decision history lives outside the model context and outside the compaction summary: the model may propose the next step, but it cannot rewrite the evidence of what has already happened.

The runtime records this check as a separate trajectory_policy_decision event containing the rule identifier, a safe representation of the observed sequence, counters, reason, and final decision. Rate limits complement this contract by constraining request frequency, tokens, or connection duration, but they do not replace semantic trajectory policy, which checks action order, value continuity, and cumulative effect.

The trajectory-policy scenarios provide a runnable teaching example and its expected decisions. The evaluator receives a verified snapshot from a trusted provider, but does not itself prove that snapshot's provenance, implement a distributed durable store, locking, compare-and-swap (CAS), an atomic action-and-counter commit, or crash recovery, and is not connected to AgentRuntime.

MCP catalog caching: freshness is not permission

LangChain describes tool-list caching with a server TTL; FastMCP documents ttlMs, cacheScope, and caller partitioning for shared stores. This is client-specific behavior: caching is opt-in and works with modern-era servers that advertise hints; legacy connections do not gain it automatically. Sources: LangChain: MCP in LangChain / FastMCP: Response caching.

Isolation requires one client per caller or a shared-store partition derived from verified identity. A tenant alone is insufficient when two users see different catalogs; caller-supplied user_id and the server URL alone are not trusted partitions. In FastMCP only server-marked public responses may cross partitions. Local policy may forbid even that sharing; never relabel a user-specific catalog as public.

A cache hit reuses a description, not an allow decision. Even before TTL expires, a revoked scope must block invocation through fresh authorization checks. The catalog is neither a credential nor a guarantee that listed actions are allowed. Create a new client when the principal changes; after grant or policy changes, make stale visibility unavailable until revalidated. Hidden tool names and schemas must not leak to another user either.

When a tool definition changes, the old plan and approval may no longer match the action. On detecting a new version or digest, refresh the catalog and recheck arguments, risk, and approval applicability; TTL alone cannot instantly detect a change. For sensitive operations, force refresh or use verifiable version binding. A client digest detects differences between snapshots but cannot force a server to execute the old version: that guarantee needs server-side version binding. See the policy bundle contract for fields and acceptance scenarios.

8. Example Policy Contract

Here is a very simple but practical template:

policy:
  run_precheck:
    require_tenant: true
    deny_if_principal_missing: true
  capabilities:
    search_docs:
      decision: allow
    create_ticket:
      decision: approval_required
      approver: manager
    run_shell:
      decision: deny
  memory_write:
    allow_kinds:
      - validated_fact
      - session_summary

Its power is not completeness. Its power is explicitness. You can argue about a specific rule and understand where it applies.

As verifier-aware governance becomes part of the production model, the same explicitness should extend to verifier trust too. High-risk paths should not rely on whatever verifier happens to be present. The policy layer should be able to say which verifier contracts are trusted, when they are required, and what happens if an untrusted verifier contract appears in the release path.

9. Example Capability Catalog Contract

It helps to think about the catalog roughly like this:

capabilities:
  search_docs:
    owner: knowledge_platform
    mode: read
    transport: mcp
    exposure: direct
    timeout_seconds: 5
    approval: none
  create_ticket:
    owner: support_platform
    mode: write
    transport: gateway
    exposure: brokered
    timeout_seconds: 15
    approval: manager
    idempotency_key_required: true
  run_shell:
    owner: platform_runtime
    mode: high_risk
    transport: sandboxed_exec
    exposure: restricted
    timeout_seconds: 10
    approval: always

That kind of catalog already defines operational semantics, not just names. It also makes explicit whether a capability is model-facing, runtime-brokered, or operator-only.

10. Structured Outputs Matter Because Contracts Should Survive Contact With Code

Stateful capability flows make this even more important. If approval, pause/resume, session expiry, and re-init decisions are all encoded only in prose, the runtime cannot safely decide whether it is continuing the same governed session or accidentally opening a new one.

That is why policy artifacts should increasingly capture structured fields like:

  • capability_session_id
  • capability_session_mode
  • resume_policy
  • on_session_expiry
  • progress_event_policy
  • elicitation_policy

Those fields help the runtime keep approval control and capability session control aligned instead of letting them drift into separate implicit systems.

As approval systems mature, the contract may also need fields for classifier-backed approval control, such as:

  • approval_mode
  • approval_delegate
  • classifier_verdict
  • escalate_to_human_if
  • subagent_handoff_policy

Those additions help the runtime represent a delegated approval path explicitly instead of hiding it in product logic or UI behavior.

The same pattern discipline should extend to delegated workers. If the runtime supports an orchestrator-workers path, the policy layer should be able to say:

  • whether the worker inherits the parent approval context;
  • whether delegated approval expires at handoff boundaries;
  • whether the worker may request additional capabilities or only use a worker-safe subset;
  • whether worker output must be reviewed before any write-capability is exercised.

The same discipline should hold across the identity boundary too. If a capability uses delegated user authorization through MCP or another brokered transport, the policy layer should be able to state whether:

  • access is platform-owned or user-delegated;
  • delegated scopes may be reused across a paused run;
  • revoked authorization forces cancel, re-approval, or re-initialization;
  • subagents are allowed to inherit the same delegated authorization context.

That keeps delegated approval and delegated authorization inside one governed contract model instead of letting them evolve as unrelated exceptions.

The reference runtime now carries those assumptions directly in execution artifacts too: run_start, approval_requested, tool_execution, run_complete, approval records, and session export can all preserve the same delegated authorization context. That matters because rollout and investigation should not have to reconstruct delegated identity from side channels.

Recent OpenAI guidance on structured outputs is useful for the policy layer too.4

A contract is only half real if the runtime still has to guess whether a policy result, approval request, or capability payload came back in the expected shape.

That is why policy-heavy systems benefit from making key artifacts structurally explicit:

  • policy decisions;
  • approval requests;
  • approval outcomes;
  • capability inputs and outputs.

The goal is not elegance for its own sake. The goal is to reduce silent drift between runtime logic, audit records, and the surrounding control surface.

11. A Simple Policy Decision Skeleton

The point here is that the runtime receives not only permission, but a structured decision.

from dataclasses import dataclass


@dataclass
class PolicyDecision:
    action: str
    reason: str
    policy_id: str
    approval_mode: str = "human"
    escalate_to_human: bool = False
    requires_approval: bool = False


def evaluate_capability(name: str) -> PolicyDecision:
    if name == "search_docs":
        return PolicyDecision(action="allow", reason="low_risk_read", policy_id="cap_001")
    if name == "create_ticket":
        return PolicyDecision(action="approval_required", reason="write_action", policy_id="cap_014", requires_approval=True)
    return PolicyDecision(action="deny", reason="unsupported_capability", policy_id="cap_999")

Even code this small already gives the right shape for telemetry, approval UI flows, and investigations.

12. A Simple Capability Lookup Skeleton

And one more practical piece: the runtime should not know capability details directly, it should fetch them from the catalog.

from dataclasses import dataclass


@dataclass
class CapabilitySpec:
    name: str
    mode: str
    transport: str
    exposure: str
    timeout_seconds: int


def get_capability(name: str) -> CapabilitySpec | None:
    registry = {
        "search_docs": CapabilitySpec("search_docs", "read", "mcp", "direct", 5),
        "create_ticket": CapabilitySpec("create_ticket", "write", "gateway", "brokered", 15),
    }
    return registry.get(name)

This also looks boring. Good. The catalog layer should be boring, stable, and inspectable.

13. Common Mistakes

These problems are very typical:

  • policy rules are scattered across runtime code;
  • the capability contract is incomplete;
  • tools exposed to the model are treated as if they were automatically approved capabilities;
  • capability ownership is unclear;
  • approval logic is embedded directly into orchestration;
  • approval exists as a human process, but not as an explicit pause/resume path in the runtime;
  • structured contracts are missing, so policy and approval payloads drift in shape;
  • memory policy and execution policy behave as if they were unrelated;
  • the catalog and real adapters drift apart in behavior.

When that happens, the reference implementation stops being a reference and becomes a bundle of conventions again.

14. A Fast Maturity Test for the Policy Layer and Capability Catalog

A team should not think it has assembled the contract core of its agent system only because it has a few policy checks and a list of tools.

A stronger bar is this:

  • policy decisions are explicit objects rather than scattered booleans;
  • capability contracts carry ownership, transport, exposure, risk, and approval semantics;
  • runtime code depends on the catalog instead of direct calls and ad hoc exceptions;
  • approval is modeled as an explicit interruptible path rather than as an off-path manual workaround;
  • memory policy, execution policy, and approval policy belong to one visible control surface;
  • telemetry can expose not only what happened, but which policy and capability contract governed it.

If most of those conditions are missing, the runtime may exist, but the contract core is still not assembled.

15. What to Do Right Away

Start with this short list and mark every "no" explicitly:

  • Do you have a separate policy layer instead of a pile of if branches?
  • Does policy return a structured decision?
  • Is there a single capability catalog?
  • Do capabilities have owner, transport, exposure, and risk semantics?
  • Does the runtime use the catalog rather than direct calls?
  • Can approval pause and resume a run explicitly?
  • Are policy decisions visible in telemetry?

If the answer is "no" several times in a row, the skeleton exists, but the contract core is not assembled yet.

16. What to Do Next

First make policy decisions and capability contracts explicit, then check whether that same system is ready for its first rollout.

The next logical step in the reference implementation is to assemble a production rollout checklist, so you move from blueprint and contract core into a practical go-live framework.

17. Useful Reference Pages

This chapter is the contract hinge for the rest of the runtime-control cluster. The most useful next steps are Chapter 18 for rollout gates and Chapter 21 for assurance response built on the same approval and policy paths.