Research / Distribution and placement

P32 · full · draft

Seams as Synthesized Providers: Interception, Typed Remote Clients, A2A Transport and Safe Adapter Synthesis

partial full Distribution and placement

State of the work. Seam row, remote row, A2A binding and adapter predicate are shipped with tests; seam runtime behaviour, transport-failure withdrawal and the observer value are not established, and there is no evaluation.

This is a draft, not peer reviewed. Repository references link to revl at commit 45bec431f.

Pitch

A seam and a remote client are the same operation today, "synthesize a provider from a service declaration and put it in the row table", and a mock is designed to join it. Interception and remoteness therefore become admission facts that the unchanged checker already knows how to judge. Adapter synthesis is a separate path (src/revl/adapt.py) that the paper proposes to unify, not a caller of the same function.

Abstract

Middleware systems add interception, remote calls and interface adaptation as separate mechanisms: filter chains with a next() to forget, stub generators whose output is trusted, and adapters written by hand. Each has its own failure class and its own place in the trust story. We describe a composition language in which interception and remote providers are callers of one function, synthesize_provider(service, kind, params), over a table of named composition rows. The function emits ordinary source from a service declaration, and the ordinary compiler then checks it, so a synthesizer bug cannot make the checker admit ill-typed or out-of-bound code; it can still emit well-typed code with the wrong behaviour, which the checker does not catch. An interception row addresses a provision edge, is derived rather than written (no next()), has two kinds (observe, decide) and no rewrite. A remote row is a synthesized provider whose transport failure is declared to be provider withdrawal (the runtime cascade is not yet wired); it synthesizes no inverse, so the language's LIFO teardown guarantee is untouched, and its returns are untrusted by construction. A named wire, through a2a, binds A2A 1.0.0 and refuses every other transport by name. Safe adapter synthesis is a closed catalogue of total transformations under five safety clauses, proposed rather than applied silently, and admitted by the same gate as hand-written code. We report what is shipped (the seam row, the remote row, the A2A binding with its boundary gates, the adapter predicate with a 100-pair return-type dichotomy sweep), what is not (the full observer value, the widening of the emission bound, rename mapping), and where the documentation lags the code. We give no performance numbers.

Problem and claim

Interception, remote stubs and adapters each break a property that a checked composition language is trying to keep. A middleware chain lets a plugin forget to call the next handler or call it with other arguments. A generated remote client usually looks like a local call and is trusted as if it were one. An adapter written by hand, or inferred silently by a resolver, can change meaning without any reviewable artifact.

The claim is structural. If each of these is a synthesizer caller over a row table, then three consequences follow and can be checked in the tree. First, soundness is inherited: the synthesized text is compiled by the ordinary compiler, so G2 (one provider per key and realm), G3 and G4 (emission bounds) run unchanged (the function that returns the source is src/revl/synthesize.py:1652-1670; the gate code itself is not at that path and was not checked here). Second, the interesting decisions become small refusals with names: rewrite has no spelling, a remote method with no declared emission bound is not remotable, an address with userinfo is refused, an unknown transport is refused. Third, remoteness stops being a wiring fact. A consumer's source is identical whether its provider is local or remote.

The claim is not that revl has a unified synthesizer for four kinds today. The synthesizer function ships three kinds (remote, seam, host; src/revl/synthesize.py:117). The config overlay and the mock-requires driver are separate code paths. The paper must say "one synthesizer, three callers, with the overlay and mock designed to join" or fix the code first.

Contributions

  1. Interception as an edge-addressed row: a seam row names a (key, realm) provision edge, its forwarder is derived from the service declaration so there is no next() to forget, the observer never receives the inner handle, observe and decide exist and rewrite has no spelling, with the refusal argument stated (src/revl/parser.py:6162, docs/design/424-dsh-language-gaps.md:369-420).
  2. The remote row as an admission fact: the provider is synthesized, transport failure is declared withdrawal by default (the runtime cascade is not yet wired, docs/composition-rows.md:341-346), every method must declare an emission bound, no inverse is synthesized so G7's LIFO teardown has nothing to walk, an address with userinfo or a path is refused, and the returned value is Untrusted[T] (docs/composition-rows.md:263-320, src/revl/synthesize.py:1-80).
  3. A named wire with a boundary: through a2a maps the canonical envelope onto A2A 1.0.0 message/send, refuses other transports by name, and joins gates that Untrusted[T] cannot give (correlation identity, envelope shape, scrubbing of peer-authored text from caller-visible failures) (docs/design/439-a2a-transport-binding.md:45-80, docs/design/439-a2a-transport-binding.md:199-300; src/revl/a2a_boundary.py:129, src/revl/a2a_boundary.py:197, src/revl/a2a_boundary.py:259, src/revl/a2a_boundary.py:413).
  4. Safe adapter synthesis: a decidable predicate over two service declarations and an opt-in map, a closed refusal enum, a catalogue-version-pinned derivation hash, adapters proposed by revl adapt and the resolver rather than applied silently, and a standing test that the predicate and the gate never disagree (src/revl/adapt.py:56-120, src/revl/adapt.py:704-743; tests/test_296_synthesis_catalogue.py:175-300).
  5. The unifying observation, with the caveat above: seam, remote and host providers are callers of one function over the row table, and config and mock are designed to join it (adapter synthesis is separate) (docs/design/424-dsh-language-gaps.md:681-700, src/revl/synthesize.py:1-30).

Evidence on main

Tree: revl main at commit 45bec431f. Counts are def test_ functions; no test was run for this extract.

claimpath:line or teststatusnote
synthesize_provider dispatches remote, seam, hostsrc/revl/synthesize.py:117 (KINDS), src/revl/synthesize.py:1652-1670; callers src/revl/composition.py:904 (remote), src/revl/composition.py:1001 (host), src/revl/composition.py:1244 (seam)shipped (3 kinds)the module docstring table (src/revl/synthesize.py:10-17) still lists seam as "designed" although it is shipped, and lists the config overlay as a kind and mock as "SHIPPED"; neither the overlay nor the mock driver calls it (a search for synthesize_provider under src finds only composition.py and docstrings)
Config overlay and mock are callers of the same functionnone found; mock-requires lives in src/revl/mocks.py:238designedthe biggest wording risk: the unification is a design, not code
Seam row parses, contextual keywords only, rewrite has no spellingsrc/revl/parser.py:1159-1190, src/revl/parser.py:6162; tests/test_424_seam_row.py (18): test_seam_row_parses_with_contextual_keywords_only, test_rewrite_has_no_spelling, test_seam_kind_must_be_observe_or_decideshipped
Forwarder is derived; observe and decide bodies; observer never holds inner handlesrc/revl/synthesize.py:1345-1470 (_seam_header, _seam_source); tests test_the_synthesized_forwarder_is_ordinary_source_on_no_disk, test_decide_seam_compiles_on_a_result_serviceshipped
decide admitted only on Result methodssrc/revl/synthesize.py:1317 (check_decidable); test test_decide_on_a_non_result_service_is_refused_naming_the_methodshipped
Seam through reach must stay inside the wrapped service's own emission boundsrc/revl/composition.py:1118 (_check_seam_through); tests test_a_seam_through_reach_outside_the_service_bound_is_refused, test_a_narrow_service_bound_refuses_the_forwardershipped as the fallbackthe widening (checking the forwarder against through instead of the service) is explicitly not made (src/revl/synthesize.py:1290-1293 for the decision note and src/revl/synthesize.py:1402-1405 for the emitted comment)
Observer value is Untrusted[Value]src/revl/synthesize.py:1413-1418 passes the operation name as a Str only, and says the full value is a later slicedesignedthe code does not yet pass arguments to the observer
Transitive capability attribution through a seam (seam:@label:key token)docs/design/424-dsh-language-gaps.md (decision D-424b.6); code comment src/revl/synthesize.py:1404-1405 says the token is not made heredesigned
Runtime half of the seam ("observer sees one call in order") executedtests/test_424_seam_row.py:1-30 docstring: "pinned at the source level" because the environment has no cordis-py runtimepartialthe claim is checked on generated source, not on a running composition
Same-key interposition is not expressible; distinct-key isdocs/composition-rows.md:702-748; tests/test_424_b1_interposition.py (5), tests/test_interposition_b1.py (4); src/revl/composition.py:1190-1212 hint textshipped (as a documented limit)docs/composition-rows.md:705-707 says the seam row "is not built" (line 707); it is built. Stale doc
Remote row: contextual keywords, needs an address, consumer unchanged, one provider resolvedtests/test_424_remote_row.py (23): test_consumer_compiles_unchanged_and_resolves_one_provider, test_a_remote_row_needs_a_peer_addressshipped
Remotable means every method declares an emission boundsrc/revl/synthesize.py:312 (check_remotable); test_a_plain_fn_service_is_refused_naming_the_methodshipped
No inverse synthesized; header says sosrc/revl/synthesize.py:1-80; test_no_inverse_is_synthesized_and_the_row_says_so (tests/test_424_remote_row.py:354)shippedthe argument that G7 stays intact is in prose; no mechanized statement in this tree
Userinfo, URL and path refused in the peer addresssrc/revl/synthesize.py:1720 (check_address); test_a_peer_address_carrying_userinfo_is_refused, test_a_url_is_refused_and_a_bad_authority_is_refusedshipped
Reach token folded from the host alonesrc/revl/synthesize.py:241 (cap_token); test_the_reach_token_is_folded_from_the_host_aloneshipped
on_failure(withdraw) is the default; fault raisedtests test_on_failure_withdraw_is_the_default_and_raises_a_fault, test_on_failure_result_is_refused_on_a_non_result_method; docs/composition-rows.md:320-348 (runtime half at line 342)partialthe doc says the runtime half (fault to provider withdrawal through the reactive path) is armed today by the placement bridge's monitor connection, which a synthesized row does not join, and that wiring it is the next step. The A2A design note cites src/revl/run.py:1870 as the withdrawal site; whether that closes the gap was not traced. Treat as open until checked
Returned values are untrustedsrc/revl/synthesize.py:823-841, src/revl/synthesize.py:1230; tests test_the_canonical_wire_taints_the_returned_value, test_a_local_provider_of_the_same_service_is_not_tainted (tests/test_424_remote_row.py:544, tests/test_424_remote_row.py:567), test_a_remote_a2a_result_cannot_reach_an_authority_sink, test_an_endorse_on_the_flow_path_admits_the_remote_a2a_result (tests/test_439_a2a_transport.py:311, tests/test_439_a2a_transport.py:325)shippeddocs/composition-rows.md:499 "Values are not tainted yet" is stale
Named transport: only the canonical wire and through a2a (and a REST variant); others refused by namesrc/revl/synthesize.py:1673 (check_transport), _A2A_TRANSPORTS; test_a_named_through_transport_is_refusedshipped
A2A message/send mapping, Str and Bytes modalities onlysrc/revl/synthesize.py:217 (_A2A_MODALITY), src/revl/synthesize.py:611 (_py_body_a2a); docs/design/439-a2a-transport-binding.md:45-80; tests/test_439_a2a_transport.py (112)shippedthe supported subset is text-in text-out plus inline file bytes; a non-terminal reply faults
Boundary gates: correlation identity, JSON-RPC shape, peer text scrubbed of call argumentssrc/revl/a2a_boundary.py:129 (py_correlation), src/revl/a2a_boundary.py:197 (py_funnel), src/revl/a2a_boundary.py:259 (py_envelope_gates), src/revl/a2a_boundary.py:413 (py_task_identity_gate), src/revl/a2a_boundary.py:456 (ts_funnel); tests/test_439_a2a_boundary.py (14)shippedthe ts importer bodies join it too (ts_funnel is called from src/revl/import_a2a.py:664 and src/revl/import_a2a.py:926). The remote row's synthesized A2A body is py only (src/revl/synthesize.py:611; there is no ts builder). docs/design/439-a2a-transport-binding.md:267 ("NOT joined yet" on the ts importer) is stale against lines 724-727
Version honesty: claim is exactly "A2A 1.0.0"; a card with another or no version is refusedsrc/revl/import_a2a.py:185 (A2A_VERSION), src/revl/import_a2a.py:321 (_version); tests/test_import_a2a.py:183, tests/test_import_a2a.py:189shipped for the importerfor a remote row there is no admission-time mismatch (the row states no version); a mismatch surfaces as a fault at first crossing (docs/design/439-a2a-transport-binding.md:351-392). Admission-time version refusal holds only on the importer path
A2A crossing is on the G8 audit surfacetest_the_a2a_crossing_is_on_the_g8_audit_surface, test_all_four_task_crossings_are_on_the_g8_audit_surface (tests/test_439_a2a_transport.py)shipped
Four-op Task lifecycle projection (_start, _poll, _reply, _cancel)src/revl/a2a_task.py; docs/design/439-a2a-task-lifecycle.md; tests/test_439_a2a_task.py (12)shipped (py)out of this paper's core; Stream[T] sugar, push notifications and durable task ids are open (docs/design/439-a2a-transport-binding.md:645-700)
Adapter predicate, five clauses S1 to S5, catalogue B1 to B6, closed clause enumsrc/revl/adapt.py:56 (CATALOGUE_VERSION), src/revl/adapt.py:59 (CLAUSES), src/revl/adapt.py:239 (compatible_total), src/revl/adapt.py:704 (bridge_plan); docs/design/296-adapter-synthesis.md:77-470shipped
Synthesis (render_adapter), derivation hash, adapter marking, chain depthsrc/revl/adapt.py:743, src/revl/adapt.py:971, src/revl/adapt.py:999-1050; tests/test_adapter_synthesis.py (3), tests/test_adapter_predicate.py (26), tests/test_adapter_carry.py (6)shipped
Resolver reports compatible-with-adapter, ranks below direct; revl adapt --checksrc/revl/__main__.py:1378; tests/test_296_resolve_adapter_ranking.py (17)shippedrevl diff showing the adapter as an added component remains a TODO in the module docstring (src/revl/adapt.py:36-38)
Predicate and gate never disagreetests/test_296_synthesis_catalogue.py:195 (test_dichotomy_auto_sweep_never_disagrees), tests/test_296_synthesis_catalogue.py:254 (test_dichotomy_optin_matrix_never_disagrees)partialthe sweep is 10 x 10 return types in a single-method service (_RETURNS, line 180); it does not generate parameter-position or arity cases
Synthesized body equals a hand-written wrappertest_synthesized_body_is_byte_identical_to_a_handwritten_wrapper (tests/test_296_synthesis_catalogue.py:159)shippedthe twin test for the "no gate learns what an adapter is" claim
Folds see through the adaptertests/test_296_folds_see_through.py (7); src/revl/adapt.py:30-34shipped
Rename mapping, auto-adapt modedocs/design/296-adapter-synthesis.md:877-880, docs/design/296-adapter-synthesis.md:905designed (explicitly deferred)

Numbers that exist

Paper outline

  1. Introduction. Three failure classes (forgotten next(), trusted stubs, silent adapters) and the one-function answer.
  2. Background: services, providers, emission bounds, the row table and G2/G3/G4/G7 in the form this paper needs.
  3. The synthesizer: signature, why soundness is inherited, the kinds, what it refuses.
  4. Interception: edge unit, derived forwarder, observe and decide, why no rewrite, distinct-key cost, the through fallback and the widening not taken.
  5. Remote providers: admission facts, withdrawal, no inverse, untrusted returns, addresses.
  6. The A2A binding: envelope mapping, boundary gates, why Untrusted[T] is not enough, version honesty.
  7. Safe adapter synthesis: predicate, clauses, catalogue, proposed-not-silent, dichotomy test.
  8. Evaluation: refusal catalogue per construct, dichotomy sweep, expressiveness study (if produced).
  9. Related work, limits.

Threats to validity and limits

Fact-check

Claims in this draft were checked against revl main at commit 45bec431f: 33 checked, 11 corrected.

Cite this draft

@misc{revlp32,
  author       = {Thomas Moussajee},
  title        = {Seams as Synthesized Providers: Interception, Typed Remote Clients, A2A Transport and Safe Adapter Synthesis},
  year         = {2026},
  howpublished = {\url{https://inso1337.github.io/revl/research/P32.html}},
  note         = {Research draft, not peer reviewed. Written against revl commit 45bec431f. Licensed CC BY 4.0.}
}

Text licensed CC BY 4.0, author Thomas Moussajee.

← All research drafts