behavior #
Behavior is the policy this runtime actually runs under: the hook slots that decide whether an input gets attention and whether a message is delivered. Changing it is a content-addressed publish plus a compare-and-swap activation, so the change is a revision you can name, verify, and roll back - not an edit that takes effect because it is on disk.
from klbr import behavior
draft = behavior.draft()
the trail #
Draft, test, publish, activate. Each step is a typestate: you cannot activate what did not validate, and you cannot validate what is not a draft.
draft.write(
"manifest.json",
'{"version":1,"hooks":{"attention.plan":"klbr_hooks.attention:plan",'
'"delivery.review":"klbr_hooks.delivery:review"}}',
)
draft.write("klbr_hooks/attention.py", attention_source)
candidate = draft.test()
test() is not a lint. It starts the hook role in a temporary process, hands it synthetic
requests, and validates the decisions that come back against the wire vocabulary
(wake/defer/ignore, allow/hold). A hook that answers something else fails here,
where nothing is live and nobody was reached.
release = candidate.publish("/path/to/releases")
print(release.id, release.verify())
A release is a directory named by the digest of its own bytes. verify() reloads it and
re-hashes what is on disk, so a release that was edited after publication fails rather than
being activated.
active = await release.activate(expected_epoch=epoch)
Activation is a compare-and-swap on the session's hook epoch. Pass the epoch you read; a stale one is refused instead of silently overwriting whoever won the race. Inside a cell this goes through the host, which re-verifies the bytes, re-runs the dry validation, and swaps the live policy. Going back is the same call with the previous release.
what it does not do #
There are two hook slots, and a manifest that names others is refused. Activation does not rewrite what already ran: it takes effect at the next cell's pin, and a cell that is already running keeps the revision it started with.
refusals #
CASConflictError is a lost race, not a bug - read the epoch again and decide whether you
still want the change. ValidationError at publish time means the dry run failed; fix the
hook rather than the manifest.