Route Contract Reference
Blueprint · Route contract · Route table lifecycle →
Bookmark this page when you need the field dictionary for a route row. Ops (version, promote, rollback) live in Route table lifecycle. Tool entry shape lives in Manifest registry. PEP payload shape lives in Policy contracts.
A route row is a governed execution contract. It points at versioned artifacts (manifest, policy, memory, workflow, prompt), at which agentic app to start (activation_target), and at which IdP client is acting (agent_client_id). It does not embed secrets, tokens, full chat history, or PDP prose.
Route row fields
| Field | Required | Example | Why it matters |
|---|---|---|---|
route_id | Yes | legal_contract_review | Stable id; tied to change records and audit |
activation_target | Optional | https://assistant-app.internal/v1/runs | Which agentic app the router starts. Omit to use the shared runtime. See Activation target |
agent_client_id | Yes | payments-agent-v2 | Which IdP-registered software principal acts on this route. Pointer only. See Agent client id |
intent / intent_label | Yes | contract_review | Classifier / eval fixture target |
description | Optional | Contract review for counsel | Human-readable scope |
tool_manifest | Yes | contract_review_staged_v3 or none | Tool schemas the LLM may propose. See Manifest registry |
policy_profile | Yes | read_only_standard | PEP rules, limits, attestation profile id |
model_profile | Yes | reasoning-standard | Capability and cost tier at the gateway |
retrieval | Optional | { "mode": "tool", "scope": ["accounts"] } | Who retrieves, and from which corpora |
memory_profile | Optional | See Memory profile | Session / working / loop policy; long-term is retrieve-only unless write tools are allowlisted |
workflow_id | Patterns 2-3 | contract_review_v3 | Fixed outer stages |
prompt_id | Usual | contract_review_v3 | Versioned prompt pack. One id on the row; multiple templates inside. See Prompt artifact |
output_schema_id | Usual | counsel_memo_v1 | Structured output validation |
eval_suite_id | Usual | contract_review_golden | Offline golden suite |
max_loop_steps | Pattern 1 | 12 | Bounds Observe → Decide → Tool (omit on Pattern 0) |
fallback | Optional | clarify / escalate_human | When entities missing or OOD |
required_claims | Optional | ["accounts:read"] | Entitlement filter for eligible routes |
Fields appear when the autonomy pattern needs them. Pattern matrix: Agents Blueprint.
Activation target
activation_target tells Plane ① which agentic app (runtime) to call after outcome=route. It is not a tool, not a model, and not a second classifier. The UI never uses this URL.
| Value | Meaning |
|---|---|
| Omit (default) | Shared agentic app. Hundreds of routes, one runtime. Prefer this. |
| HTTP(S) URL | POST /v1/runs on that app (async 202 + correlation_id) |
| Queue id | e.g. kafka:agent.commands.fraud for an event worker pool |
Pin it on the route pin with correlation_id. Resume ("yes") starts the same target. Do not re-resolve from active. How the POST looks: Wire agentic app.
A new use case is still a new row, not a new runtime. Set activation_target only when isolation cannot live in the pin (PCI enclave, separate release cadence). Same router either way.
Agent client id
agent_client_id is which robot this route is. It is an IdP client pointer on the catalogue row. It is not a secret, not a token, and not activation_target.
| Value | Meaning |
|---|---|
IdP client_id string | Register this agent in the IdP. After freeze, the agentic app requests a token for this client, downscoped to this route's manifest. |
Pin it on the route pin and the run pin with route_id and versions. Resume re-mints the same client. Route change mid-session: new downscoped token for the new row's agent_client_id. Do not derive the id from route_id. Do not put the client on NAR / activation_target config (shared runtime would become one fat identity). Do not inline scopes or client secrets on the row. The IdP holds registration, secret, allowed scopes, and status.
A new use case is a new row and usually a new agent client. Two rows may share a client only when they are the same software principal; prefer 1:1 so revoke does not take unrelated routes.
How the freeze carries it: Session custody · Wire agentic app. Token boundary: Token and session boundary.
Retrieval
mode | Who retrieves | Typical use |
|---|---|---|
omit / none | Nobody | Chat, handoff, no knowledge path |
deterministic_prefetch | Agentic app, before the LLM. Model never sees retrieve tools. | Pattern 0 Q&A; or Pattern 2 if retrieve → generate is a fixed workflow |
tool | A search tool runs inside scope. PEP still gates it. | Pattern 1: LLM proposes. Pattern 2: workflow names the stage. Pattern 3: LLM proposes inside the stage allowlist. |
scope is a list of corpus / index ids the route may touch. One retrieval object per row; multiple corpora go in that list, not a second retrieval block. The route pins mode and scope for the session. How the app branches on each mode, including more than one retrieve: RAG retrieval.
Memory Profile
Memory is route policy + session state, not a workflow step. The row declares what is allowed; the agentic app owns read, write, and assembly.
| Field | Values | Meaning |
|---|---|---|
conversation | none / session | User / assistant turns for this session |
working | none / session | Task slots, route entities, stage outputs |
loop | none / checkpoint | Step count, proposals, observations |
long_term | none / retrieve_only | Durable facts via retrieval or allowlisted tools; not stuffed into the system prompt. Permission only: who starts recall follows the pattern. See Autonomy shape · Memory |
ttl_hours | number | Soft lifetime for session-scoped stores |
isolation | e.g. ["tenant", "user", "session"] | Partition keys so Session A cannot read Session B |
Prompt artifact
prompt_id names one pack, not one string. The agentic app loads it after the route pin, then picks a template by llm_role (Patterns 2-3) or uses host alone (Pattern 0). Conversation and working memory are assembled at inference time. They are not extra prompt_ids.
| Pattern | What the pack holds |
|---|---|
| 0 | host only. One call. |
| 1 | host plus loop instructions. Same pack for every step. |
| 2-3 | host plus templates keyed by llm_role. none means no LLM call. |
Two stages may share one template. Key by role, not by stage_id. Do not put PEP limits in the pack.
Worked example: MSA risk review. This pack is prompt_id on route msa_risk_review. That row's manifest is msa_risk_review_v1. Full row and workflow: RAG retrieval. How llm_role is assigned: Autonomy shape. Assembly: Inference handoff. Manifest entry shape: Manifest registry.
| This pack | Used by |
|---|---|
prompt_id | msa_risk_review_v1 (this artifact) |
| Route | route_id: msa_risk_review |
| Manifest | tool_manifest: msa_risk_review_v1 |
| Workflow | workflow_id: msa_risk_review_v1 |
Route row: msa_risk_review (JSON)
Pattern 2. Shared runtime (no activation_target). No max_loop_steps: the workflow owns the budget. Same ids as the MSA row in RAG retrieval. The prompt pack does not list tools. The manifest does.
{
"route_id": "msa_risk_review",
"intent": "msa_risk_review",
"agent_client_id": "msa-risk-review-v1",
"description": "Fixed-stage MSA risk review for counsel: OCR, clause search, playbook, risk, memo",
"model_profile": "reasoning-standard",
"tool_manifest": "msa_risk_review_v1",
"policy_profile": "read_only_standard",
"retrieval": {
"mode": "tool",
"scope": ["clause-index", "legal-playbook"]
},
"memory_profile": {
"conversation": "session",
"working": "session",
"loop": "checkpoint",
"long_term": "retrieve_only",
"ttl_hours": 24,
"isolation": ["tenant", "user", "session"]
},
"workflow_id": "msa_risk_review_v1",
"prompt_id": "msa_risk_review_v1",
"output_schema_id": "msa_memo_v1",
"eval_suite_id": "msa_risk_review_golden",
"fallback": "clarify",
"required_claims": ["legal:msa_review"]
}
| Field | What it means on this route |
|---|---|
route_id / intent | This turn is MSA risk review. Classifier target and audit id. |
agent_client_id | IdP client msa-risk-review-v1. NAR requests a token for this robot after freeze. |
description | Human-readable scope. Not sent to the model. |
model_profile | Reasoning tier at the gateway. Not a vendor model id. |
tool_manifest | Load msa_risk_review_v1. Tool schemas and pdp_action live there. |
policy_profile | PEP uses read_only_standard. The registry does not grant permission. |
retrieval | Search tools may run, only against clause-index and legal-playbook. |
memory_profile | Session conversation, stage working slots, checkpointed loop. Long-term is retrieve-only. |
workflow_id | Pattern 2 stage order. The LLM does not pick the next stage. |
prompt_id | This pack: host plus query_formulation and synthesis. |
output_schema_id | Counsel memo shape (msa_memo_v1). App validates before return. |
eval_suite_id | Golden checks: stage order, required stages, memo grounded. |
fallback | Clarify when entities are missing or the turn is out of domain. |
required_claims | Ingress must carry legal:msa_review or this row is not eligible. |
| Stage | llm_role | Template from this pack |
|---|---|---|
ocr | none | None. No LLM call. |
clause_search | query_formulation | by_llm_role.query_formulation |
policy_search | query_formulation | Same template. Corpus comes from the workflow, not the prompt. |
risk_engine | none | None. No LLM call. |
memo | synthesis | by_llm_role.synthesis |
Prompt pack msa_risk_review_v1 (JSON)
One pack. The route msa_risk_review points here and at tool_manifest: msa_risk_review_v1. Five stages. Two templates. task_type is what Plane ③ sees (plan / synthesize). It is not a second prompt_id.
{
"prompt_id": "msa_risk_review_v1",
"prompt_version": "2026.08.1",
"host": "You are counsel's MSA risk-review worker. Do only the current stage. Do not choose the next stage. Do not invent tools.",
"by_llm_role": {
"query_formulation": {
"task_type": "plan",
"text": "Write the search query for this stage's corpus only. Do not pick a different index."
},
"synthesis": {
"task_type": "synthesize",
"text": "Draft the counsel memo from validated stage outputs only. Ground claims in retrieved clauses and playbook hits."
}
}
}
Changing prompt text under the same prompt_id does not bump route_table_version. Pointing the row at a new prompt_id does. Same rule as other artifact ids: When the row changes.
Related artifacts (not inlined on the row)
| Artifact | Owned by | Playbook |
|---|---|---|
Tool entries (name, schema, pdp_action, risk_tier) | Manifest store | Manifest registry · Manifest lifecycle |
| SARAC / PEP payload | Policy plane | Policy contracts |
| Workflow stages / stage allowlists | Workflow store | Agents Blueprint (Patterns 2-3) |
Prompt pack (host, by_llm_role) | Prompt store | Prompt artifact |
| Output schema / eval suite | Separate versioned stores | Loaded by id from the row |
| IdP agent registration (secret, allowed scopes, status) | IdP / agent security | Pointer on the row is agent_client_id only. See Agent client id |
Canonical single-route shape
Worked Pattern 2 row: MSA risk review. Same contract as Prompt artifact. Fields appear when the autonomy pattern needs them.
Canonical route row: msa_risk_review (JSON)
{
"route_id": "msa_risk_review",
"intent": "msa_risk_review",
"agent_client_id": "msa-risk-review-v1",
"description": "Fixed-stage MSA risk review for counsel: OCR, clause search, playbook, risk, memo",
"model_profile": "reasoning-standard",
"tool_manifest": "msa_risk_review_v1",
"policy_profile": "read_only_standard",
"retrieval": {
"mode": "tool",
"scope": ["clause-index", "legal-playbook"]
},
"memory_profile": {
"conversation": "session",
"working": "session",
"loop": "checkpoint",
"long_term": "retrieve_only",
"ttl_hours": 24,
"isolation": ["tenant", "user", "session"]
},
"workflow_id": "msa_risk_review_v1",
"prompt_id": "msa_risk_review_v1",
"output_schema_id": "msa_memo_v1",
"eval_suite_id": "msa_risk_review_golden",
"fallback": "clarify",
"required_claims": ["legal:msa_review"]
}
Full route table shape
One published artifact: table metadata plus one object per route row. Entitlements are enforced at runtime from ingress claims, not stored per user.
Route table (JSON): versioned route contracts
{
"route_table_version": "2026.07.1",
"product": "corporate-assistant",
"routes": [
{
"route_id": "agent-account-v3",
"intent_label": "account_history",
"agent_client_id": "accounts-agent-v3",
"description": "Read-only account and transaction history",
"tool_manifest": "accounts-readonly-v2",
"policy_profile": "read_only_standard",
"model_profile": "reasoning-standard",
"retrieval": { "mode": "tool", "scope": ["accounts"] },
"memory_profile": {
"conversation": "session",
"working": "session",
"loop": "checkpoint",
"long_term": "retrieve_only",
"ttl_hours": 24,
"isolation": ["tenant", "user", "session"]
},
"max_loop_steps": 6,
"fallback": "clarify",
"required_claims": ["accounts:read"]
},
{
"route_id": "agent-payments-v2",
"intent_label": "payment_initiate",
"agent_client_id": "payments-agent-v2",
"description": "Initiate outbound transfer",
"activation_target": "https://payments-app.internal/v1/runs",
"tool_manifest": "payments-readwrite-v3",
"policy_profile": "high_risk_step_up",
"model_profile": "reasoning-standard",
"retrieval": { "mode": "tool", "scope": ["policy-engine", "accounts"] },
"memory_profile": {
"conversation": "session",
"working": "session",
"loop": "checkpoint",
"long_term": "none",
"ttl_hours": 8,
"isolation": ["tenant", "user", "session"]
},
"max_loop_steps": 8,
"fallback": "escalate_human",
"required_claims": ["payments:initiate"]
},
{
"route_id": "agent-policy-qa-v1",
"intent_label": "policy_qa",
"agent_client_id": "policy-qa-agent-v1",
"description": "Policy and procedure Q&A via RAG",
"tool_manifest": "none",
"policy_profile": "read_only_standard",
"model_profile": "reasoning-standard",
"retrieval": {
"mode": "deterministic_prefetch",
"scope": ["policy-engine"]
},
"fallback": "clarify",
"required_claims": ["policy:read"]
},
{
"route_id": "agent-chat-v1",
"intent_label": "general_chat",
"agent_client_id": "chat-agent-v1",
"description": "Lightweight chat with no tools",
"tool_manifest": "none",
"policy_profile": "low_risk_chat",
"model_profile": "lightweight-chat",
"max_loop_steps": 2,
"fallback": "clarify",
"required_claims": []
},
{
"route_id": "agent-escalate-v1",
"intent_label": "escalate_human",
"agent_client_id": "escalate-agent-v1",
"description": "Hand off to human agent",
"tool_manifest": "handoff-v1",
"policy_profile": "read_only_standard",
"model_profile": "lightweight-chat",
"max_loop_steps": 3,
"fallback": "clarify",
"required_claims": []
}
]
}
Pattern cheat sheet
Every row carries agent_client_id. Patterns below are the extra refs.
| Pattern | Typical row refs |
|---|---|
| 0 | prompt_id, output_schema_id, eval_suite_id (tool_manifest: "none"); optional retrieval with mode: "deterministic_prefetch"; optional lean memory_profile |
| 1 | tool_manifest, prompt_id, max_loop_steps, memory_profile, eval / output ids; optional retrieval with mode: "tool" |
| 2 | workflow_id, tool_manifest, plus prompt / output / eval ids; memory_profile for stage working state; optional retrieval (prefetch as a named stage, or tool with fixed order) |
| 3 | workflow_id (stage allowlists), tool_manifest, memory_profile, plus prompt / output / eval ids; optional retrieval with mode: "tool" |
Worked JSON per pattern: Agents Blueprint.
When the row changes
Bump route_table_version when a route row changes (pointers or policy fields), not on every artifact publish behind a stable id.
| Change | Bump route_table_version? |
|---|---|
New or removed route_id | Yes |
Row points at a new workflow_id, tool_manifest, prompt_id, schema, or eval id | Yes |
model_profile, policy_profile, retrieval, memory_profile, max_loop_steps, activation_target, agent_client_id, or entitlements on the row | Yes |
New manifest_version under the same manifest_id | Often no (promote + pin on session) |
| Prompt / eval content bump while the route still references the same artifact id | Often no |
Full promote / rollback: Route table lifecycle.
Read next
Route table lifecycle → · Layered classifier · Manifest registry