Skip to content

feat(context-api): supply a per-call final continuation - #241

Closed
taras wants to merge 1 commit into
mainfrom
agent/api-per-call-terminal
Closed

feat(context-api): supply a per-call final continuation#241
taras wants to merge 1 commit into
mainfrom
agent/api-per-call-terminal

Conversation

@taras

@taras taras commented Aug 10, 2026

Copy link
Copy Markdown
Member

Why

createApi() composes stable-name middleware around the handler it was built with, and that handler is the only base a call can end in. Api exposes operations and around and nothing else, so a caller that owns the authority for one invocation has nowhere to put it — the base is shared, and anything that reaches it is reachable by every other call through the same name.

This is a prerequisite for taras/executable.md#432, where the public Execution name has to keep composing replaceable policy while the final continuation carries authority for exactly one invocation. Without this the only routes are installing the terminal as at:"min" middleware — which is still replaceable — or reproducing collectMiddleware, $api:$name, contextWindow() and reducePrototypeChain() in the consumer, which couples it to internals of this package and of Effection.

What

api.terminating.hello(function* (name) {
  return `HELLO ${name}`;
})("world");

terminating mirrors operations for function members. It collects the same middleware, preserves max/min ordering exactly, and composes it around the continuation supplied for that call instead of the createApi() handler.

The continuation stays in the caller's lexical state: it is not stored, not placed in a context, not part of the operation's public arguments, not behind a stable name, and not reachable by the middleware composed around it. Middleware cannot tell which base is underneath it.

Value members have no arguments to intercept and no continuation to supply, so they are typed never.

Compatibility

operations and the module-level default are untouched — additive only. Loaded-copy composition is unchanged, because collection still goes through the same stable-name middleware context.

Tests

context-api/terminating.test.ts, 10 cases:

  1. existing operations still terminate in the createApi default
  2. a per-call continuation replaces the default for that call only
  3. public middleware observes the same max/min order with either terminal
  4. a non-delegating handler prevents the supplied continuation from running
  5. nested calls use their respective continuations — including one started from inside the outer call's own chain
  6. concurrent calls cannot exchange continuations
  7. middleware installed through another same-name descriptor composes
  8. later at:"min" and at:"max" registrations remain public layers outside the supplied continuation
  9. failure tears the call down normally
  10. cancellation tears the call down normally

cd context-api && npx vitest run → 49 passed (3 files), which includes the package's existing suites unchanged.

createApi() composes stable-name middleware around the handler it was
built with, and that handler is the only base a call can end in. A caller
that owns the authority for one invocation has nowhere to put it: the
base is shared, so anything reaching it is reachable by every other call.

`Api.terminating` collects the same middleware, in the same max/min
order, and composes it around a continuation supplied for that one call.
The continuation stays in the caller's lexical state — it is not stored,
not placed in a context, not part of the operation's public arguments,
and not reachable by the middleware composed around it. A handler that
returns without delegating never reaches it, and nested or concurrent
calls each reach their own.

`operations` and the module-level default are unchanged.
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 81955109-941b-4e52-8ac7-a0352413e2ad

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@taras taras closed this Aug 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant