Skip to content

Commit 0c9d966

Browse files
committed
ADR-0009: Explicit handler mediation of partial results across action boundaries
1 parent 7b1edb0 commit 0c9d966

2 files changed

Lines changed: 120 additions & 0 deletions

File tree

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# ADR-0009: Explicit handler mediation of partial results across action boundaries
2+
3+
- **Status:** accepted
4+
- **Date:** 2026-03-24
5+
- **Deciders:** @Aksem
6+
- **Tags:** actions, partial-results, architecture
7+
8+
## Context
9+
10+
FineCode supports **partial results**: incremental client-visible data that can
11+
be sent before an action fully completes. A direct action invocation can already
12+
emit partial results when the caller provides a `partial_result_token`.
13+
14+
The gap appears when one action delegates to another. Higher-level actions such
15+
as `lint` and `format` may orchestrate more specific subactions such as
16+
`lint_files` or `format_files`. Without an explicit cross-action rule, partial
17+
results produced inside a delegated action either stop at the delegation
18+
boundary or bypass the parent action's contract entirely.
19+
20+
That boundary matters for three reasons:
21+
22+
1. The parent action owns the client-facing contract and may need to reshape a
23+
delegated action's partial result before exposing it.
24+
2. Orchestrator handlers should use the same logic whether partial results are
25+
enabled or not.
26+
3. The rule must work for both sequential delegation and concurrent
27+
orchestration.
28+
29+
## Related ADRs Considered
30+
31+
- [ADR-0007](0007-single-registration-per-action-definition.md) - requires
32+
multi-language orchestration to use explicit composition rather than duplicate
33+
registrations. This ADR defines how partial results behave across that
34+
composition boundary.
35+
- [ADR-0008](0008-explicit-specialization-metadata-for-language-actions.md) -
36+
specialization metadata for language-specific actions. Related to dispatch and
37+
orchestration, but not to partial-result flow.
38+
39+
## Decision
40+
41+
When one action delegates work to another, **partial results do not implicitly
42+
propagate across the action boundary**.
43+
44+
If the parent action wants client-visible partial results while delegating, the
45+
parent handler must explicitly:
46+
47+
- consume partial results from the delegated action
48+
- decide whether to expose them at all
49+
- map them into the parent action's client-facing result shape before
50+
re-emitting them
51+
52+
The parent handler therefore owns the partial-result contract at its boundary in
53+
the same way it owns the final result contract.
54+
55+
The framework must support this rule with explicit handler-facing mechanisms
56+
that allow a handler to:
57+
58+
- consume delegated partial results incrementally
59+
- emit partial results from handler code without branching on whether partial
60+
results are active
61+
- use the same architectural rule in both sequential and concurrent
62+
orchestration patterns
63+
64+
## Consequences
65+
66+
- **Parent action boundaries stay explicit.** Nested actions cannot
67+
accidentally stream directly through a parent action without that parent's
68+
involvement.
69+
- **Result-shape ownership is clear.** A parent handler can translate,
70+
filter, enrich, or suppress delegated partial results before the client sees
71+
them.
72+
- **Orchestrator handlers stay mode-agnostic.** The same handler logic can run
73+
with or without partial-result support, with framework-provided no-op
74+
behavior when streaming is inactive.
75+
- **Sequential and concurrent orchestration are both supported.** The
76+
architectural rule is the same even when the concrete emission mechanism
77+
differs.
78+
- **Orchestrator APIs become slightly broader.** Handlers that compose other
79+
actions need explicit framework support for consuming and re-emitting partial
80+
results. Simple non-orchestrating handlers are unaffected.
81+
82+
### Alternatives Considered
83+
84+
**Implicit propagation through nested action calls.** Rejected because it lets a
85+
delegated action bypass the parent action's contract boundary. That makes
86+
result-shape mismatches and accidental exposure of subaction behavior much more
87+
likely.
88+
89+
**Forward the token but let the subaction send results directly.** Rejected
90+
because the parent handler would lose control over client-visible partial-result
91+
shape and timing. A parent action needs to own both.
92+
93+
**Only support manual handler-side emission.** Rejected because it makes the
94+
common sequential orchestration case more verbose than necessary.
95+
96+
**Only support yield-style emission.** Rejected because some concurrent
97+
orchestration patterns cannot emit from the place where results become
98+
available.
99+
100+
**Introduce a separate streaming-handler protocol now.** Deferred. Revisit if
101+
streaming handlers create recurring type-checking friction or require repeated
102+
runtime-only workarounds during handler authoring.
103+
104+
## Implementation Notes
105+
106+
The current implementation uses two handler-facing mechanisms:
107+
108+
- `IActionRunner.run_action_iter()` for consuming delegated partial results
109+
- `RunActionContext.send_partial_result()` for explicit emission from handler
110+
code
111+
112+
Async-generator handler `run()` methods are the preferred pattern for simple
113+
sequential mapping. Explicit `send_partial_result()` remains the escape hatch
114+
for concurrent patterns such as `TaskGroup`, where yielding from the point of
115+
completion is not practical.
116+
117+
Current runtime details such as async-generator detection, queue-based bridging,
118+
and how final return values are handled after explicit partial emission are
119+
implementation choices rather than the architectural decision itself.

docs/adr/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,4 @@ keep the same decision, would the ADR still read correctly?
5959
| 0006 | [Shared action types as the action identity contract](0006-shared-action-types-as-the-action-identity-contract.md) | accepted | 2026-03-21 | actions, api, architecture, environments |
6060
| 0007 | [Single registration per action definition](0007-single-registration-per-action-definition.md) | accepted | 2026-03-21 | actions, architecture |
6161
| 0008 | [Explicit specialization metadata for language-specific actions](0008-explicit-specialization-metadata-for-language-actions.md) | accepted | 2026-03-21 | actions, architecture, languages |
62+
| 0009 | [Explicit handler mediation of partial results across action boundaries](0009-explicit-partial-result-token-propagation.md) | accepted | 2026-03-24 | actions, partial-results, architecture |

0 commit comments

Comments
 (0)