Rationale
Why an unprovided module is refused
A refusal in words against a silent no-op, and which one a reader can act on.
Why is an unprovided module refused rather than ignored?
Because the alternative is a program that runs, does nothing, and reports success. A silent no-op is indistinguishable from a working call at every point where somebody might notice, which makes it the most expensive kind of failure a capability system can have.
The refusal is the feature
It names the module, the target, and whether the capability exists in this host at all. That last part is the one a stub cannot give you: "this host has it and your manifest does not list it" and "nothing anywhere provides this yet" are different problems with different fixes.
// The same source against two targets.
//
// One provides `drift/animation` and one does not, and the difference is a refusal with the
// module and the target named in it. Nothing about this file changes between the two.
//
// **A script drives a blend tree; it does not build one.** The tree is built where the skeleton
// and the clips already are, and handed to a system through `uses` — so what a script does with
// it is set its parameters and evaluate it. Both kinds of parameter are set the same way: a blend
// weight, and a clock, which is the time a `clip` node was told to read instead of the frame's.
import { blendSet, blendAt, pose } from "drift/animation"
data GaitState {
// Seconds, for anything that carries on while the character stands still.
phase: f32 = 0
// The distance the character has covered, on the gait clips' own axis.
stride: f32 = 0
}
fn step(state: mut GaitState, dt: f32, speed: f32) {
state.phase += dt
state.stride += dt * speed
}
fn drive(tree: Blend, state: GaitState, joints: u32) -> Pose {
let out = animation.pose(joints)
animation.blendSet(tree, "speed", 1.0)
animation.blendSet(tree, "gait", state.stride)
animation.blendAt(tree, state.phase, out)
return out
}
And why mocks were declined
A mock is a second implementation of a contract with no first implementation to check it against, so anything it agrees with is itself. A surface that appeared to work would be one nobody could tell from a surface that did, which is worse than an absence anybody can read.
// Written before this host provided the capability. It links now, and not a character of it moved.
//
// This is the property the whole language is arranged around, and this file is the receipt. It was
// written when `drift/ai` had no provider here: it parsed, it type-checked, and only linking
// declined it, naming the module and the target and whether the capability existed at all, which is
// a different answer from "no". Then the engine bound the module, and the file linked that day,
// unedited.
//
// So press compile and watch it link. The point is not that it works; it is that nothing about the
// file had to change for it to start working.
//
// What the script does and does not do is worth reading twice. It tells an agent that something
// happened and asks what the agent is doing. It does not receive a plan, and it never runs one:
// a model's output is not an execution path, and a tool resolves by id to something registered
// long before the model was asked.
import { agent, wake, degraded, intentId } from "drift/ai"
data Sentry {
alarmed: bool = false
lastIntent: String = ""
}
fn alarm(sentry: mut Sentry, id: String, priority: i32) {
if let found = ai.agent(id) {
ai.wake(found, "heard something", priority)
sentry.alarmed = true
}
}
// `degraded` is how a script asks whether the agent is over budget and running on its policy floor
// alone. A floor is deterministic and synchronous, so an agent with no provider still behaves, and
// the answer here is a quality signal rather than a failure.
fn observe(sentry: mut Sentry, id: String) -> bool {
if let found = ai.agent(id) {
sentry.lastIntent = ai.intentId(found)
return ai.degraded(found)
} else {
return true
}
}
What it costs
You cannot run a file that names a capability you have not wired yet, even to test the parts that do not touch it. The file type-checks, which is most of the value, but there is no execution until the manifest is real.