@@ -53,7 +53,7 @@ source/capture pair** — applied with different payloads:
5353- **Scheduling matrix ** (see *Lifecycle Teardown * in :doc: `architecture ` for
5454 the invariant statement):
5555
56- - **Shared-output relays ** (reference outputs, service responses , adaptor
56+ - **Shared-output relays ** (reference and subscription outputs , adaptor
5757 ``from_graph ``/``to_graph ``, contexts) are **rank-correct and
5858 same-cycle **: pairs are declared with ``Wiring::add_same_cycle_pair ``
5959 (source rank-constrained after every capture); ``Wiring::finish `` re-ranks
@@ -66,8 +66,12 @@ source/capture pair** — applied with different payloads:
6666 **next cycle ** by design: the pairing is rank-free (no rank dependency),
6767 and the capture schedules the service source for
6868 ``evaluation_time + MIN_TD `` (current time during ``start ``). The temporal
69- break replaces a wiring edge, so a client's request may derive from the
70- service's own response — the same one-cycle rule as ``feedback ``.
69+ request mutation does not run the implementation in the capture cycle.
70+ - **Request/reply responses ** cross an explicit feedback source/sink pair in
71+ the graph that owns the implementation, then publish through the ordinary
72+ same-cycle shared-output relay. Request/reply clients are omitted from
73+ indirect service ranking, so recursive request/reply calls are legal. No
74+ nested client or higher-order operator constructs this feedback path.
7175- **Lifecycle: ** the source clears its captured state on ``stop ``. A restarted
7276 graph must republish through capture before the source can produce a live
7377 shared output.
@@ -107,7 +111,10 @@ The per-flavour payloads:
107111 before the source emits, so the final request delta is **cumulative **
108112 (``make_request_input_source_node `` / ``make_request_input_capture_node ``;
109113 proven by "request/reply source emits cumulative client requests" in
110- ``test_service_wiring.cpp ``).
114+ ``test_service_wiring.cpp ``). The implementation output is captured by an
115+ outer-graph feedback sink and replayed on the following cycle before the
116+ keyed shared response is published. A direct request therefore has Python's
117+ observable sequence ``[None, None, response] ``.
111118
112119Related decision recorded with this layer: real-time wall-clock scheduler
113120alarms use the normal graph schedule queue — ``NodeScheduler(...,
@@ -467,9 +474,9 @@ second implementation for the same service kind + path throws
467474**Semantics proven by tests ** (``test_service_wiring.cpp ``): a reference client
468475reads the implementation output by reference (no copy); paths keep shared
469476outputs separate; a subscription client's keys reach the implementation on the
470- next cycle and the response flows back keyed; request/reply replies are keyed
471- by the client's request id; two clients' requests reach the implementation as
472- one cumulative delta.
477+ next cycle and the response flows back keyed; request/reply replies cross the
478+ outer feedback edge and remain keyed by the client's request id; two clients'
479+ requests reach the implementation as one cumulative delta.
473480
474481
475482How a client expression lowers
0 commit comments