#[derive(Clone, PartialEq, Eq, thiserror::Error)]
#[non_exhaustive]
pub enum BuildError {
#[error(
"this plane runs as tenant '{plane}' but its blob store serves '{store}'. \
Blobs are content-addressed, so a shared store means two tenants' \
identical bytes are one object — and erasing it for one destroys it for \
the other while reporting both requests discharged"
)]
BlobStoreTenant { plane: String, store: String },
#[error(
"this plane runs as tenant '{plane}' but its journal store is scoped to \
'{store}'. The mismatch does not surface at runtime: runs are written \
into the other tenant's keyspace while every erasure and every policy \
request names this one"
)]
JournalStoreTenant { plane: String, store: String },
#[error(
"tool server 'agent' is reserved: `tool://agent/<capability>` names an \
agent on this plane and dispatches through `commission`. A transport \
under that name would let a deployment change whether a grant means \
\"an agent here\" or \"somebody's server\" without changing any \
reviewed document — rename the server"
)]
ReservedToolServer,
#[error(
"agent '{agent}' declares `execution.kind: {kind}` with {grants}, but this \
plane has no tool catalogue, so every run would fail identically. Wire one \
with `RuntimeBuilder::toolbox(..)` — which derives it from this very \
declaration — or state it with `.tools(catalog, client)`. Grants of the \
form `tool://agent/<capability>` need neither, because they dispatch \
through `commission` rather than a transport"
)]
DeclarativeToolsUnreachable {
agent: String,
kind: &'static str,
grants: String,
},
#[error(
"this plane's journal store is shared between instances, and its \
governed memory is sealed with a **process-local** erasure lock — so \
the window between an erasure's legal-hold check and its key \
destruction is open to the other instance, which can write an item \
that ends up sealed under a scope about to stop existing. The erasure \
would report success. Wire a coordinator that spans instances: \
`EncryptedMemoryStore::new(..).coordinated_by(Arc::new(store.erasure_coordinator()))`"
)]
ErasureCoordinatorNotShared,
#[error(
"agent '{agent}' declares {declared}, so a run must be able to open a \
task and suspend until somebody decides it — but this plane has no \
{missing}. Wire it with `RuntimeBuilder::{remedy}(..)`, or drop the \
oversight declaration. A run admitted through plain `run(..)` also has \
no case, so give it correlation keys with `run_correlated(..)` or name \
one with `run_in_case(..)`"
)]
OversightUnreachable {
agent: String,
declared: String,
missing: &'static str,
remedy: &'static str,
},
#[error(
"agent '{agent}' declares `spec.memory_formation`, so every run ends by writing \
durable facts — but this plane has no memory store. Wire one with \
`RuntimeBuilder::memory(..)`, or drop the declaration. Formation runs after the \
answer, so left to run time this fails once the run has already paid for its \
model calls"
)]
FormationWithoutMemory { agent: String },
#[error(
"agent '{agent}' files memories under '{subject}', which resolves from the run's \
case — and this plane has no case store, so nothing could ever resolve it. Wire \
one with `RuntimeBuilder::cases(..)` and admit runs with `run_correlated(..)`, or \
declare a literal subject and accept that every subject's facts share one key"
)]
MemorySubjectUnbindable { agent: String, subject: String },
#[error(
"agent '{agent}' grants 'tool://agent/{capability}', and no agent on \
this plane provides '{capability}' — the model would be offered a \
consultation that fails when chosen"
)]
AgentToolUnknownCapability { agent: String, capability: String },
#[error(
"agent '{agent}' grants 'tool://agent/{capability}', which it provides \
itself — an agent consulting itself is a loop wearing a grant, and \
the delegation ceiling would only bound how long it spins"
)]
AgentToolSelfReference { agent: String, capability: String },
#[error(
"a lease of {ttl:?} cannot be renewed: the store keeps expiry in whole \
seconds and treats `expires_at <= now` as lapsed, so anything under \
{minimum:?} expires between renewals however often they run — and a \
run that cannot hold its lease can be taken over while it is still \
working"
)]
LeaseUnrenewable {
ttl: std::time::Duration,
minimum: std::time::Duration,
},
#[error(
"tool server '{server}' is registered twice — registration order would \
decide which transport carries a call"
)]
DuplicateToolServer { server: String },
#[error(
"this plane wires tools twice — `tools(..)` states the catalogue \
explicitly and `toolbox(..)` derives it from the agents, so one of them \
would silently replace the other's grants"
)]
ToolsWiredTwice,
#[error(
"the tools this binary implements and the manifest of agent '{agent}' \
disagree — the declaration a reviewer approved no longer describes the \
agent:\n {}", problems.join("\n ")
)]
ToolDrift {
agent: String,
problems: Vec<String>,
},
#[error(
"agents '{first}' and '{second}' both grant '{tool}' and declare it \
differently — a plane has one catalogue, so one of the two reviewed \
declarations would silently not be the one enforced"
)]
ToolDeclaredTwoWays {
tool: String,
first: String,
second: String,
},
#[error(
"tools were wired to a plane with no declared agent — a grant is an \
agent's declaration, so there is nothing here that admits them"
)]
ToolsWithoutDeclaration,
#[error(
"the stated tool catalogue is laxer than a reviewed manifest grant — a \
read-only entry exempts the tool from the whole-value taint gate and \
makes a timed-out call retryable:\n {}", problems.join("\n ")
)]
CatalogueLaxerThanGrant { problems: Vec<String> },
#[error(
"two skills on this plane are both named '{name}'. A skill name is how a \
capability resolves to an implementation and how a run names what it \
dispatched, so two of them make both answers arbitrary — rename one"
)]
DuplicateSkillName { name: String },
#[error(
"capability '{capability}' is claimed by two agents on this plane: \
'{first}' and '{second}'. Dispatch resolves a capability to one skill \
and to the manifest governing it, so the second claim would silently \
take the first's work out from under the first's budget and grants. \
Give them distinct capabilities, or put them on separate planes"
)]
CapabilityClaimedTwice {
capability: String,
first: String,
second: String,
},
#[error(
"agent '{agent}' declares execution but no privileged model — a \
declarative agent has nothing to call"
)]
DeclarativeWithoutModel { agent: String },
#[error(
"agent '{agent}' names provider '{provider}', which no driver is \
registered for. Call RuntimeBuilder::provider(\"{provider}\", ..)"
)]
UnknownProvider { agent: String, provider: String },
#[error(
"agent '{agent}' declares execution but provides no capability — a \
declarative agent nothing can call is a file that does nothing"
)]
DeclarativeProvidesNothing { agent: String },
#[error(
"agent '{agent}' advertises capabilities none of its own skills provide: \
{missing:?}. A skill wired with `RuntimeBuilder::skill` is not governed \
by any agent — it runs under the plane's budget and no manifest gate. \
Register it on the agent instead: \
`.agent(Agent::new(&manifest).skill(MySkill))`"
)]
AdvertisesWhatItCannotProvide { agent: String, missing: Vec<String> },
}
crate::core::error::debug_is_display!(BuildError);