v2.0.0 — the Action Binding Protocol
[2.0.0] - 2026-08-25
The Action Binding Protocol. Declare an action once; bind it to every live
control that can perform it. Four identities with four cardinalities — a
definition is what a capability IS, a binding is where it is connected, an
offer is the facts it was exposed under, a transition is one invocation — and
no consumer ever recovers one from another by string surgery. This is the v2
contract: createActionBindingRuntime is now createActionRuntime, offers are
principal-scoped (runtime.forPrincipal(p).offers() — the principal belongs to
the reader, not the binding), progress reports declared/observed/unreported
at close, abandoned requires an explicit authority (never inferred from a
detach), and offered inputs are revision-exact: an invocation can never read a
newer committed input while carrying an older offer.
Fixed
- Late evidence is kept, marked late, and reopens nothing. A settlement
arriving after the terminal used to vanish into a return of the first one.
It now rides the transition aslateSettlements— a QUOTATION of what the
late caller claimed and the payload it brought, never validated as if
accepted and never allowed to reopen a terminal. Absent when none arrived,
because an empty list would claim "we watched and none came." - A stale offer is no longer called a forgery. Invalidation deletes a
retired offer from every map, so the old refusal accused a slow caller
replaying yesterday's offer and an attacker of the same crime. The refusal
now says what the runtime can still establish: a binding serving a current
offer names it as the next move; a registered but offerless binding says the
surface moved on; an unknown binding admits it cannot tell never-ours from
since-detached, and will not guess.
Added
- Action Binding now joins what was offered, invoked, and settled.
defineAction()declares one callable action, framework-neutral connections
bind its live instances, andruntime.available()returns self-describing
none,bound, oropenoffers.runtime.invoke()is the single broker for
offered execution: it accepts only the exact retained offer, captures bound
input when that offer is minted, validates caller input for open offers, and
carries a non-secret input receipt through the invocation, transition, and
settlement history. Definitions explicitly declareinvocation: 'inputless' | 'scalar' | 'host'; broker eligibility is never guessed from
Function.length. Open offers require one caller slot and are withheld when
no schema tells a UI/agent how to construct that input. - Channel declarations have a stable Layer 1 home. Callable definitions can
store typedneedsandproduceskind declarations. They are validated and
frozen but deliberately inert until the later channel/surface broker exists. - Input contracts have an enforcement port. Schemas with synchronous
.safeParse()or.parse()methods are enforced directly; applications can
supplyinputSchemaAdapterfor formats such as JSON Schema plus Ajv. Strict
activation refuses an unenforceable schema instead of presenting metadata as
an active gate. - Live fact generations are explicit.
connection.update()is inert when
facts do not change, whileconnection.touch()deliberately retires existing
offers without running application readers. React input bindings require an
inputKey; changing it atomically publishes the newly committed input reader
and invalidates the prior offer without changing binding identity. - Invocation outcomes distinguish refusal from failure.
refusedmeans the
application handler never started;failedmeans it started and then
threw/rejected. Preflight input refusals immediately settle their effect as
not attempted, and connection observers now receive direct, brokered, and
host-continuation invocations through one narrow settlement capability. - React availability generations are explicit.
availabilityKeynames the
committed facts read by adapter enabled/busy readers. Omitting it chooses a
conservative revision after every commit; simultaneous input and availability
changes still publish one revision.
This will be the first public callable Action Binding shape. Its cross-copy brand
is hcifootprint.action-definition.v1; that suffix versions the frozen branded
record, independently of the npm major version. The existing navigation-graph
ActionDef.input field remains unchanged; callable definitions deliberately use
inputSchema.