On this page
Review agent proposals with bind hooks
Prerequisite: bind and runtime hooks. Expert 37 shows an application installing its own registration policy on a live normal Conduit, then passing it object references supplied by an agent workflow. Use the existing codegen lessons when the workflow must first produce those references.
Check the actual supplied reference
The example registers two ordered pre strategies:
Require an existing
ReportToolobject.Require its
schema_versionto be1.
The second check sees the same object as the first, including its current instance
state. An unrelated object and a tool with an unsupported schema both fail in
pre_bind, before a Spell is created or registered.
conduit.add_bind_hooks(
pre=[review.require_report_tool, review.require_supported_schema],
activation=[review.configure_spell],
post=[review.record_registration],
)
supplied = ReportTool()
spell_id = conduit.bind(
spell=supplied,
existence="unique",
submitted_by="report-agent",
)
assert conduit.meld("ReportTool") is supplied
The unique-only lifetime is Melder's existing rule for supplied objects. The hook pipeline checks and annotates that registration; resolution returns the same supplied reference. The example's type/schema checks are application policy, not new built-in restrictions on every Melder binding.
Modify the Spell at the right stage
Pre-bind receives only the target reference. Bind keyword metadata becomes available
on the new Spell in activation. The example checks its submitted_by label there,
then sets spell.metadata["review_policy"] and appends an agent-reviewed tag.
The label is application-provided metadata; trusted caller identity must come from
the application's own authenticated context if that distinction is required.
Post-bind receives the same Spell after registration. The example verifies that
book.find_spell_by_spell_id(spell.spell_id) returns it, sets
spell.metadata["post_checked"] = True, and records its ID in the application's audit.
The caller then inspects those changes through the public lookup.
These are live metadata changes. They do not replace source code, create a new version, recompute a Spell ID or automatically republish a recorded snapshot. Native identity is established before bind activation; identity-bearing changes need the normal binding/version workflow. Recording and Nexus publication can precede post-bind, so a post-hook metadata edit is not a promise that an earlier published record changed.
This example uses active bind registrations. bind_inactive also runs the lifecycle,
but its post callback receives a parked candidate. A callback for that path should
not assume ordinary selected-version lookup returns the parked Spell.
Handle failure according to its phase
Failure phase |
Demonstrated result |
|---|---|
|
An unsuitable reference is refused; activation and post do not run |
|
An unsupported submitter label is refused; the unpublished Spell is retired and the caller's object remains usable |
|
The notification fails after registration; the binding and earlier post metadata changes remain |
Inspect HookExecutionError.original_exception for the application's cause. Raising
stops the remaining callbacks in that stage; return values are ignored.
Expert 37 appends a simulated failing audit callback after its normal post callback.
It catches the resulting post_bind error and proves meld("BackupReport") still
returns the registered object. Inspect existing state before retrying such a bind;
a failed notification does not mean a second registration is needed.
Keep policy updates on the Book's existing lifecycle
add_bind_hooks appends and clear_bind_hooks clears all stages for future binds.
Each bind retains its captured callbacks, including when callbacks change the registry.
Use the Book or its normal-Conduit facade for updates. Configuration seeds are captured
when a Book is constructed; changing another Book's runtime registry is separate.
The example's audit is sequential and in memory. Concurrent callbacks must coordinate any application state they share, and external notifications keep their own failure and retry policy. The existing transaction admission and versioning workflow continue to govern structural changes.
The workflow in code
These are the core steps from the saved example. Use its complete linked script for all class definitions and setup.
1def main() -> None:
2 """Bind accepted agent proposals and assert the precise refusal boundaries.
3
4 Contract:
5 Uses ordinary public binding against a live normal Conduit. The
6 proposal references stand for objects supplied by an agent workflow;
7 acquiring or generating those references is a separate concern.
8 """
9 book = md.Spellbook(configuration=md.SpellbookConfiguration().with_defaults())
10 conduit = None
11 try:
12 conduit = book.conjure(dynamic=True, name="agent-bind-review")
13 review = BindingReview(book)
14 conduit.add_bind_hooks(
15 pre=[review.require_report_tool, review.require_supported_schema],
16 activation=[review.configure_spell],
17 post=[review.record_registration],
18 )
19
20 # A reference check rejects by raising, not by returning False.
21 for proposal, cause in ((object(), TypeError), (ReportTool(2), ValueError)):
22 try:
23 conduit.bind(spell=proposal, existence="unique", submitted_by="report-agent")
24 except md.HookExecutionError as error:
25 assert error.phase == "pre_bind"
26 assert isinstance(error.original_exception, cause)
27 print("reference refused:", error.phase, str(error.original_exception))
28 else:
29 raise AssertionError("The unsuitable proposal was registered.")
30 assert book.describe_spells_in_spellbook() == []
31 assert review.published_ids == []
32
33 supplied = ReportTool()
34 try:
35 conduit.bind(spell=supplied, existence="unique", submitted_by="unknown-agent")
36 except md.HookExecutionError as error:
37 assert error.phase == "bind_activation"
38 assert isinstance(error.original_exception, PermissionError)
39 print("agent metadata refused:", error.phase)
40 else:
41 raise AssertionError("The unapproved agent was admitted.")
42 assert book.describe_spells_in_spellbook() == []
43 assert supplied.render("still-owned-by-caller") == "report:still-owned-by-caller"
44
45 spell_id = conduit.bind(
46 spell=supplied, existence="unique", submitted_by="report-agent",
47 )
48 registered = book.find_spell_by_spell_id(spell_id)
49 assert registered is not None
50 assert registered.metadata == {
51 "submitted_by": "report-agent",
52 "review_policy": "report-schema-v1",
53 "post_checked": True,
54 }
55 assert "agent-reviewed" in registered.tags
56 assert conduit.meld("ReportTool") is supplied
57 assert supplied.render("weekly") == "report:weekly"
58 assert review.published_ids == [spell_id]
59 print("accepted existing object:", registered.metadata)
60
61 # add appends: record_registration runs before the later failing callback.
62 conduit.add_bind_hooks(post=[unavailable_audit_sink])
63 backup = BackupReport()
64 try:
65 conduit.bind(spell=backup, existence="unique", submitted_by="report-agent")
66 except md.HookExecutionError as error:
67 assert error.phase == "post_bind"
68 assert isinstance(error.original_exception, RuntimeError)
69 print("post notification failed; inspect the published binding before retrying")
70 else:
71 raise AssertionError("The audit failure was not propagated.")
72 assert len(review.published_ids) == 2
73 published_backup = book.find_spell_by_spell_id(review.published_ids[-1])
74 assert published_backup is not None
75 assert published_backup.spell_name == "BackupReport"
76 assert published_backup.metadata["post_checked"] is True
77 assert conduit.meld("BackupReport") is backup
78 print("binding survived post failure:", published_backup.spell_name)
79
80 conduit.clear_bind_hooks()
81 assert conduit.meld("ReportTool") is supplied
82 assert registered.metadata["post_checked"] is True
83 finally:
84 if conduit is not None:
85 conduit.cleanup()
86 book.cleanup()
Runnable examples
Review agent bindings — Expert 37