Adapter guide

What you implement

A minimal external adapter has three moving parts. That is true whether the target is a repository, a ticket system, a workflow tool, or something custom.

In any language, the shape is small:

text
adapter/
state handler
policy handler
worker loop

You can put those pieces in one process or several processes. The protocol does not require a specific language or framework.

StateProvider

The StateProvider is the endpoint the Adapter Host calls when it needs current target state before a decision can proceed.

You implement:

  • one HTTP callback endpoint
  • target binding checks
  • a bounded state response for the requested target and scope

You do not implement:

  • final decision logic
  • WorkOrder creation
  • host-side hashing rules beyond returning the required public fields

The point is not to mirror your whole backend. The point is to return the governed state view that the decision path actually needs.

Minimal shape:

text
POST /state
-> validate target binding and callback token
-> read one bounded slice of target state
-> return one governed StateView response

Minimal pseudo implementation:

text
handleState(request):
assert request.target_adapter == cfg.target_adapter
assert request.target_ref == cfg.target_ref
assert bearer token is valid
state = readTargetState(request.target_ref, request.target_scope)
return {
state_view_ref: "state-view-1",
target_adapter: request.target_adapter,
target_ref: request.target_ref,
target_scope: request.target_scope,
content: json(state),
content_hash: sha256_hex(json(state)),
returned_view_hash: sha256_hex(json(state))
}

PolicyBoundary

The PolicyBoundary is the endpoint the Adapter Host calls to ask for a candidate decision.

You implement:

  • one HTTP callback endpoint
  • your local policy rules
  • a candidate response that matches the public contract

You do not implement:

  • final admission
  • direct work creation
  • fail-open shortcuts that bypass the host

Policy participates, but it is not the final authority by itself. Core still validates the candidate before admitted work can exist.

Minimal shape:

text
POST /policy
-> validate target binding and callback token
-> evaluate one bounded policy rule
-> return one candidate such as admitted or blocked

Minimal pseudo implementation:

text
handlePolicy(request):
assert request.target_adapter == cfg.target_adapter
assert request.policy_identity matches cfg
assert bearer token is valid
if target state allows the action:
return candidate decision "admitted"
return candidate decision "blocked"

Worker

The worker is the code that claims admitted work, performs the bounded target-side operation, and posts the outcome.

You implement:

  • the claim loop
  • target-side materialization
  • outcome submission with the required bindings

You do not implement:

  • browser-facing intent ingestion
  • policy-only write authorization
  • a replacement for host-side claim and lease semantics

Minimal shape:

text
loop:
POST /v0/adapter/work-orders/claims
if no_work_available: wait and poll again
if claim received: apply one target-side effect
POST /v0/adapter/outcomes

Minimal pseudo implementation:

text
workerLoop():
while true:
claim = claimWork(target_adapter)
if claim.reason == "no_work_available":
sleep(5 seconds)
continue
result = applyTargetEffect(claim)
postOutcome(claim, result)

What this means in plain terms

If you can answer these three questions, you can build an adapter:

  • How do we read the current target state we care about?
  • How do we decide whether that action should proceed?
  • How do we perform the final target-side effect and report the result?

Everything else stays with the bundle. That is what makes the integration surface relatively small.

Three concrete examples

Adapter exampleStateProviderPolicyBoundaryWorker
Repository adapterread branch or pull-request stateallow or block one repository changeopen one pull request or post one bounded update
Ticket adapterread ticket workflow stateallow or block one transitionmove one ticket or add one note
ImpactRoom-style reference adapterread room or turn stateallow or block one room-side effectmaterialize one room-side effect after claim

The shipped minimal target adapter is the smallest inspectable version of these three parts. It is generic on purpose, so you can map it onto your own target instead of inheriting someone else's demo logic.