Technology · Discipline

Link-time plugin registration

A generated function is registered at link time so that it is fully wired or the build fails. There is no half-registered state.

FAFO Core and Function ForgeRust link-time distributed slicesGenerated composition root
One distributed slice, walked at startup
execution handlercontrol targetan entry missing its control half
  • 14function crates on the substrate
  • 7of them produced by the generator
  • 2registrations in every slice entry
  • 3actions per tool in the gateway

Registration as a property of the build

FAFO Core and Function Forge: a tool can be half-registered. I had seen hand-written registration routines drift that way. When agents started generating functions, I decided registration would be a property of the build, so discovery happens at link time and nothing can drift between what is built and what is registered.

Hand-written registration
GatewayResult pathControl

In the gateway, missing from the result path: the call hangs.

GatewayResult pathControl

Callable, not controllable: the job cannot be paused or drained.

Registered at link time
GatewayResult pathControl

Wired: executes and takes commands.

Slice entryLinkBinary

Not wired: the build fails, so nothing half-wired runs.

A hang is never an error, so the check moved to where an error is the only outcome.

Execution and control in one entry

Each function crate contributes an entry to a link-time distributed slice through the Rust linkme crate, a third-party crate I use. At startup the control plane walks that slice. Execution and control register in the same entry, which keeps every function operable by agents as well as executable.

The slice the control plane walks at startup
Execution handler: run by the generic conductor
Control target: takes pause, scale and drain on a job in flight

Generated lists that nobody edits

The control plane's composition root is generated too. It is the list of compiled-in functions plus the control-plane service, produced from registration files by a generated macro, so adding a function never means editing a startup routine. On the gateway side, the tool surface is generated at build time from per-function YAML and loaded at startup. This replaced an earlier design in which each function called a hand-written registration routine at startup.

Registration filesGenerated macroComposition root
Per-function YAMLBuildtools/list: execute, status, control

Deleting a function reverses every edit the generator made.

A human gate before a tool is callable

Function Forge depends on this. A new function needs a rebuild and a restart, and I chose that deliberately.

It puts a human gate at the moment a tool becomes callable, and I wanted the gate there.

An agent calls the generatorIt fills a sandboxed stepRebuild and restart, authorised by meCallable and controllable
The gate is the only way a generated function reaches agents.

Moving checks earlier, here from runtime into the linker, removes a class of failure, and the gate sits where it matters most for AI-generated code. The governance around that path is in Tools that build tools, under governance, and the build-time generation it rests on is on the Schema-driven build-time code generation page.