Skip to content

Rust #325: serve workflow-task cancellation scopes (open, prepare, deliver) - #771

Merged
rmcdaniel merged 9 commits into
mainfrom
rust-325-cancellation-scopes
Oct 26, 2026
Merged

rmcdaniel merged 9 commits into
mainfrom
rust-325-cancellation-scopes

Conversation

@rmcdaniel

@rmcdaniel rmcdaniel commented Oct 26, 2026 •

Copy link
Copy Markdown
Member

Part of #325. Serves the open, prepare and deliver routes for workflow-task cancellation scopes, fixture first. Security class: R2 (worker protocol surface).

Fixture (recorded from published 2.11.1)

cancellation-scopes (9b07c76, with identity labels independent of key order in 2a6ce03) records scope opening, nesting and shielding over raw worker HTTP. It covers a run cancellation request reaching every unshielded descendant of the root, prepare and deliver with their fences and refusals, and the activity worker's acknowledgement under the scope's request.

Native (e288d1d)

Native follows Workflow 2.10's portable scope opening and delivery, as published Server 2.11.1 does:

  • A scope is a node of a per-run tree rooted at the implicit root scope, kept only in history. Completion commands join it by cancellation_scope_id. A run cancellation request records CancellationScopeRequested for every unshielded descendant of the root.
  • A worker holding the claim prepares a delivery, which freezes its subtree inventory, and then delivers it. Every activity and timer member is fenced first, then the barrier is recorded. Scope delivery never closes the run, releases the claim or creates a task. The activity worker acknowledges a fenced attempt with the scope's request.
  • Scoped waits and children stay refused at completion. A scope holding one (possible only in history PHP wrote before a takeover) is refused with cancellation_scope_delivery_unavailable, as are parallel and selection boundaries below the next sequence.
  • Workflow #930's completion budget meters what it meters on Workflow main: completion commands that join a scope count toward it, while the open, prepare and deliver routes are their own unmetered transactions, as in DefaultWorkflowTaskBridge. Workflow main also meters the scope checkpoint route; native leaves that route unserved, and serving it later must meter it.
  • The three routes leave the unserved-route inventory; checkpoint stays unserved. Native serves 143 of the 175 published route methods on this branch.

History reads (bef9217)

Preparing or delivering a scope, and a run cancellation request, used to reread the run's whole history (up to 10000 rows) for every descendant scope and every fenced member, all under the transition lock. The transaction now keeps what scope_events read for one run, and a later read fetches only the rows past its last sequence. A delta that does not continue that sequence is never spliced in; the history is read again from the start. The in-place payload update (attribute_worker_terminal) and the history deletes (retention pruning, namespace deletion) clear the cache, and it ends with the transaction. Payloads are shared behind Arc and never mutated, so a snapshot already handed out does not change. Behavior and the 10000-row bound are unchanged.

A 40-scope tree with a timer in each (final history 165 rows), in history rows read:

Step Before After
Run cancellation request 4183 122
Prepare the parent scope 5413 124
Deliver it (40 timer fences) 12181 165
Deliver again (answered from history) 13860 165

history_cache lib tests cover append-after-read, a gapped delta, the in-place update, rollback and retry, another run, and that tree end to end. The tree's row bounds fail with the cache disabled.

Verification on this head

Head bef921707 (current main 086ba7fa merged, which includes #768 and #766):

  • SQLite, all targets: lib 308 and execution 519 pass.
  • Native shared fixtures recorded over HTTP against a debug server: cancellation-scopes, activity-cancellation-acknowledgement, the three cooperative-* cleanup fixtures, cancel-leased-activity and cancel-pending-timer all pass on SQLite, PostgreSQL 17, MySQL 8 and MariaDB 10.11.
  • PostgreSQL 17 and MySQL 8: execution completion_budget, parallel_cancellation, cooperative*, activity_cancellation_ack, history_retention, namespaces, local_activities and child_cascade pass, 81/81 on each.
  • No PHP app/ source changes.

🤖 Generated with Claude Code

rmcdaniel and others added 9 commits October 24, 2026 15:04
….11.1

A raw-HTTP worker on protocol 1.20, registered as a cooperative Python
runtime in its own namespace, opens a scope tree (a shielded scope among
them), schedules a scoped activity and timers on completion and requests the
run's cancellation. The fixture records the scope requests that request
leaves, the scope-request claim, S1's preparation at its activity and its
delivery, which fences the activity and a descendant scope's timer, the
activity worker's view and its acknowledgement with the scope's request, and
completion admission by scope before and after preparation. A second run
prepares a descendant before its parent, so the parent's delivery waits and a
root command at its reserved boundary is refused; the delivered boundary
consumes its sequence. Each route is refused for its protocol, validation,
ownership, request and boundary faults, and for claims without the capability
or polled on 1.19.

The probe normalizes the observation: identities and digests become labels,
the run requests' instants named anchors and other instants their format, and
lease relations measured facts. Three recordings against published 2.11.1 on
SQLite, with CI's maintenance loop running, normalize identically.

Published 2.11.1 answers prepare and deliver with scope_id "root" with a bare
500: Workflow throws root_scope_requires_run_cancellation and the portable
delivery does not convert it. The fixture bounds that defect and expects the
route's 409 refusal.
… order

The probe labeled each ULID `{ulid:N}` by first appearance while walking
the observation in each response's own key order. Published PHP answers
in insertion order and native in sorted order, so two identities in one
delivery response (the delivery's event and a fenced timer's event)
traded labels between the servers although both were recorded in the
same order. The walk now visits object keys in sorted order, so labels no
longer depend on the order a server writes its keys.

The fixture was recorded again against published 2.11.1 with the new
labels, three times, identically. Against the previous expectation, 39
labels renumber one to one and nothing else changes. The two root-scope
defect steps keep their published answers from the new recording, and
their intended refusals take the same relabeling. The recorded
observation the contract test reads is replaced by the new pr1 recording.
The contract test passes 22/22.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Native serves the open, prepare and deliver routes for workflow-task
cancellation scopes, as published Server 2.11.1 does over Workflow 2.10's
portable scope opening and delivery:

- A scope is a node of a per-run tree rooted at the implicit root scope,
  kept only in history. Completion commands join it by
  cancellation_scope_id, and a run cancellation request records
  CancellationScopeRequested for every unshielded descendant of root.
- A worker holding the claim prepares a delivery, freezing its subtree
  inventory, and then delivers it: every activity and timer member is
  fenced first, then the barrier is recorded. Scope delivery never closes
  the run, releases the claim or creates a task. The activity worker
  acknowledges a fenced attempt with the scope's request.
- Scoped waits and children stay refused at completion. A scope holding
  one (possible only in history PHP wrote before a takeover) is refused
  with cancellation_scope_delivery_unavailable, as are parallel and
  selection boundaries below the next sequence.

Native passes the cancellation-scopes fixture recorded from published
2.11.1. The three routes leave the unserved route inventory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cancellation-scopes

The port log's route count is corrected to this branch's state: 133 of
175 served, 42 unserved as the route list records.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nto rust-325-cancellation-scopes

Workflow-task completion admits cancellation scopes before it guards
worker-session commands, in published 2.11.1's order. A scheduled
activity's options keep main's worker session and add its cancellation
scope, as PHP's bridge appends it. Native serves 141 route methods;
cancellation scope checkpoints stay missing. The probe timeout chain and
the comparator node test list keep both sides' entries.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ust-325-cancellation-scopes

The comparator node test list keeps both sides' tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…st-325-cancellation-scopes

# Conflicts:
#	.github/workflows/server-parity.yml
#	docs/rust-server-port.md
…llation-scopes

# Conflicts:
#	.github/workflows/server-parity.yml
#	rust/src/runtime/store.rs
…fence

Preparing or delivering a cancellation scope, and a run cancellation
request, reread the run's whole history (up to 10000 rows) for every
descendant scope and every fenced member, all under the transition lock.
A 40-scope tree with a timer in each read 4183 history rows to request,
5413 to prepare and 12181 to deliver, against a final history of 165.

The transaction now keeps what scope_events read for one run and later
reads fetch only the rows past its last sequence. A delta that does not
continue that sequence is never spliced in; the history is read again.
The in-place payload update and the history deletes clear the cache, and
it ends with the transaction. Payloads are shared and immutable, so a
snapshot already handed out never changes. The same tree now reads 122,
124 and 165 rows. Behavior is unchanged and the 10000-row bound holds.

Tests cover append-after-read, a gapped delta, the in-place update,
rollback and retry, another run, and the wide tree end to end, whose
row bounds fail with the cache disabled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@rmcdaniel

Copy link
Copy Markdown
Member Author

Security review package: #771 at bef921707

Head bef921707237bd186a2d92ca10b3622426158453. Tree c03efc956552e521fa9de955f53a5478e175412f. Parent 5bc3dd03ffd2c1f4cc71561b8e2c9b0cbfa0823f. Base main is 086ba7fafa9bf76eb10a28d537ef3b5b448dea02. Against main: 22 files, +18205/−59.

name-status against main
M	.github/workflows/server-parity.yml
M	docs/rust-server-port.md
M	rust/src/runtime/activity_cancellation_ack.rs
A	rust/src/runtime/cancellation_scopes.rs
M	rust/src/runtime/cooperative.rs
M	rust/src/runtime/execution.rs
M	rust/src/runtime/http.rs
M	rust/src/runtime/mod.rs
M	rust/src/runtime/namespace_cleanup.rs
M	rust/src/runtime/operator_metrics.rs
M	rust/src/runtime/store.rs
M	rust/src/runtime/transaction.rs
M	rust/src/runtime/wait_timeouts.rs
M	rust/tests/fixtures/unserved-php-routes.v2.11.1.txt
M	scripts/conformance/server-parity.mjs
A	scripts/conformance/server-parity/cancellation-scopes-contract.mjs
A	scripts/conformance/server-parity/cancellation-scopes-probe.php
M	scripts/conformance/server-parity/contract.mjs
M	scripts/conformance/server-parity/probe.php
A	tests/Fixtures/ServerParity/cancellation-scopes.json
A	tests/Fixtures/ServerParityExpected/cancellation-scopes-observation.json
A	tests/Unit/ServerParityCancellationScopesContractTest.mjs

Blobs at this head

41b7dcf0dbeb2e4a3bc4b139b134e60ab9f0c676 rust/src/runtime/cancellation_scopes.rs
de7d1b89c436cf25d8e056b6f7b53773f29f52d3 rust/src/runtime/http.rs
8af3a7def21482635642b0a873df0de8e1ab89c6 rust/src/runtime/store.rs
21bc4c85169a147f312e48a4f83e3a41d37675dd rust/src/runtime/transaction.rs
28e8fb869d9b0ab96405ab9e2a67f4384290b994 rust/src/runtime/backend.rs
c8d3c22ca037b468cd3824a9ba2fe1d111208707 rust/src/runtime/cooperative.rs
dfdebeee0aac31df1231708424212405cd6fad7d rust/src/runtime/activity_cancellation_ack.rs
8691494cd1579a8c52c81dc2fee511e5bb66301b rust/src/runtime/operator_metrics.rs
780382dc06557f1a413f11b40933a8ea6a69ef0f rust/src/runtime/namespace_cleanup.rs
db8c7e212fff10fba49ce13bde07a6e7a1472514 rust/src/runtime/execution.rs
77ab24aadf6c2dcfb3fc5068afb69983465e42e3 rust/src/runtime/payload_limits.rs
56da7a245a27fda4333225047edce20eb83b1955 rust/src/runtime/wait_timeouts.rs
2705cc90971e828b3fdfd4840f6777258f4e1a1d rust/tests/fixtures/unserved-php-routes.v2.11.1.txt
1243e0ca8c60c56c35f206d9015bf673d67d60f3 scripts/conformance/server-parity/cancellation-scopes-contract.mjs
813d5deef99eb834734f8691500205390e55d10f scripts/conformance/server-parity/cancellation-scopes-probe.php
c40e89805a9072fe81349a48d3058b9e3d7e32c9 scripts/conformance/server-parity/activity-cancellation-ack-contract.mjs
8085a2e100204a59f5702b999f0568d5b404f9ec scripts/conformance/server-parity/cooperative-contract.mjs
b96309fca7871e7acc2bfb7bf08bab5252cb8ae5 scripts/conformance/server-parity/cancellation-contract.mjs
ff9e5540e16907973f6c75f19a9693d0e431acc3 tests/Fixtures/ServerParity/cancellation-scopes.json
3acc7f6d7d90ec147f2a4e5bc298bb424ccf960d tests/Fixtures/ServerParity/activity-cancellation-acknowledgement.json
9d814fc9e2377e33c712a94b34174c551729fb88 tests/Fixtures/ServerParity/cooperative-before-claim-cleanup.json
29d6b90e55151c8d3a634ee57f86b4baf8ed28d2 tests/Fixtures/ServerParity/cooperative-cleanup-deadline.json
0c925bd2aa33d53c9130b36c5bd616ee39506159 tests/Fixtures/ServerParity/cooperative-pending-timer-cleanup.json
c44d81516bcd23628069da3f23623a48fdadb7ac tests/Fixtures/ServerParity/cancel-leased-activity.json
21a737a3d2e28e31f68298453526d7af43dd414c tests/Fixtures/ServerParity/cancel-pending-timer.json
bbe9adaadf3f6481f40a1154f80c110d81470c62 tests/Fixtures/ServerParityExpected/cancellation-scopes-observation.json
26321056ccdf0519e19f22b55b98638aee57c26e tests/Unit/ServerParityCancellationScopesContractTest.mjs

Where to read (lines at this head)

Area File:lines
Route registration rust/src/runtime/http.rs 265–276 (open, prepare, deliver); handlers 2250–2279
Body bound before parse http.rs 340 (payload_limits::enforce layer); payload_limits.rs 24–64
Field validation cancellation_scopes.rs 1095–1115 (OPEN_RULES, DELIVERY_RULES); 727–743 (boundary_syntax, spans ≤ 1000)
Refusal mapping cancellation_scopes.rs 1071–1089 (delivery_reason, open_reason)
Authority gate cancellation_scopes.rs 1245–1285 (scope_route_gate); 1222–1240 (scope_claim); cooperative.rs 212 (claim_supports_cancellation)
Open cancellation_scopes.rs 1288–1378
Prepare/deliver cancellation_scopes.rs 1397–1451 (route), 1458–1558 (engine), 1580–1684 (write_boundary), 1688–1700 (renew_hosting)
Fences cancellation_scopes.rs 1824–1887 (timer), 1891–2040 (activity, with its recorder)
Descendant requests cancellation_scopes.rs 2042–2111
Scope tree from history cancellation_scopes.rs 258–285 (scopes), 434–448 (descendants), 663–677 (admission_refusal), 806–838 (matches_membership)
Transition lock backend.rs 292 (SQLite BEGIN IMMEDIATE), 407 (PG pg_advisory_xact_lock), 607 (MySQL FOR UPDATE on dw_server_schema)
History cache cancellation_scopes.rs 218–227 (HistoryCache), 1163–1205 (scope_events); transaction.rs 16, 29
Cache invalidation store.rs 4077 (payload UPDATE); operator_metrics.rs 1363 (retention delete); namespace_cleanup.rs 210 (namespace delete)
Budget store.rs 2838 (completion meter)
Checkpoint unserved rust/tests/fixtures/unserved-php-routes.v2.11.1.txt 23
Cache tests cancellation_scopes.rs 2263 onward (history_cache)

Security gates

  1. Authority comes from the task, not from scope IDs. The gate loads the task by its path ID and the authenticated namespace. It loads the run from the task's workflow_run_id in that namespace, then checks the instance's namespace. lease_owner and workflow_task_attempt must equal the task's. The issued claim capability (cooperative.rs 212) binds task, run, namespace, worker, attempt and protocol. The worker's registration in the namespace must be fresh. Under the lock, scope_claim re-checks: leased, a workflow task, lease unexpired, same namespace as the run. A caller-supplied scope_id, parent_scope_id or request_id is only looked up in that run's own history. scopes() rejects any opened event whose schema or workflow_run_id differs, whose parent was not recorded earlier, or whose ID or sequence repeats. The request must be the scope's accepted request in this run.
  2. Foreign, cross-run and stale inputs fail closed, before any write. The fixture records each case:
    • unknown task, and a known task in another namespace: 404 task_not_found;
    • foreign owner: 409 lease_owner_mismatch;
    • wrong attempt: 409 workflow_task_attempt_mismatch;
    • a completed claim: claim mismatch;
    • unknown scope, unknown parent, wrong request: refused;
    • protocol below 1.20: refused.
      A stale registration answers stale_worker_registration. Engine refusals before any effect roll back.
  3. Idempotent under duplicate and concurrent calls. Each route is one transaction from begin(), which takes the server-wide transition lock first, so concurrent calls serialize.
    • A replayed open returns the recorded scope (duplicate: true); a different parent or shield answers cancellation_scope_replay_mismatch.
    • write_boundary returns the recorded marker for the same boundary and refuses any other (cancellation_scope_delivery_mismatch).
    • A repeated delivery answers from history and writes nothing.
      The fixture covers open_s1_again, open_s1_explicit_root, open_s1_shielded_replay, prepare_s1_again, prepare_s1_other_boundary, deliver_s1_again and deliver_s1_other_boundary. The wide-tree lib test asserts the history row count is unchanged after a redelivery.
  4. Cancellation stays inside its persisted scope.
    • Membership is read only from history, and descendants exclude shielded subtrees.
    • A boundary must touch the scope's unshielded subtree, and the scope must be recorded before it.
    • A shielded scope refuses a parent request (cancellation_scope_parent_shielded).
    • A preparation freezes the inventory, and admission_refusal blocks new members under a prepared scope.
    • Root needs run cancellation (root_scope_requires_run_cancellation). Wait and child members are refused (cancellation_scope_delivery_unavailable).
      The fixture covers the shielded scope, the root scope, and the shielded S3 timer staying unfenced.
  5. Bounded before allocation.
    • The body is refused from Content-Length, and at most max + 1 bytes are read before JSON parsing.
    • Strings are capped at 255. Integers must be at least 1. Spans are capped at 1000 before any span loop.
    • Each history read is LIMIT 10001, and a cumulative count above 10000 answers cancellation_inventory_exceeds_budget.
    • The scope tree is built iteratively, so its depth and width are bounded by the history.
    • With the cache, one request reads O(N + new rows) history rows instead of O((D+M)·N); figures are below.
  6. checkpoint stays unserved. It is not registered, it is listed in the unserved inventory, and the route-coverage execution test asserts its 501.
  7. Metering matches Workflow main. A workflow-task completion sets its meter (store.rs 2838) before recording commands, including commands that join a scope. The three scope routes open their own transactions without a meter, like DefaultWorkflowTaskBridge::openCancellationScope, prepareCancellationScopeDelivery and deliverCancellationScope on Workflow main (4eb49a20), which do not use meteredTransaction. Workflow main meters the scope checkpoint (checkpointCommands); native leaves that route unserved, and serving it later must meter it.

History cache, against your requirements

  • Transaction-local and run-keyed: the cache is Transaction.scope_history, keyed by run. Reading another run replaces it with a fresh full read.
  • Created after the lock and authority: begin() takes the lock when the transaction is created. In the routes, the first scope_events call runs after scope_route_gate and scope_claim.
  • Never shared: it lives in the per-request transaction, which commit and rollback consume. There is no static or global state, and a retry is a new transaction.
  • Strict deltas: a reread fetches sequence > last. The rows must continue last + 1, last + 2, …; any gap discards the cache and rereads from the start.
  • Invalidation: every in-place mutation of workflow_history_events clears it: the payload UPDATE (store.rs 4077), the retention delete (operator_metrics.rs 1363) and the namespace delete (namespace_cleanup.rs 210). Those are the only UPDATE and DELETE statements on that table. Inserts go through record (last + 1) or the two imports, whose transactions never read through the cache.
  • Bound kept: the cumulative 10000-row refusal is unchanged.
  • No mutable aliasing: Event.payload is Arc<Value> and is never mutated through get_mut or make_mut. The event list is copy-on-write (Arc::make_mut), so a snapshot already handed out keeps what it held; a test asserts this.

Measured on a 40-scope tree with a timer in each scope (final history 165 rows), in history rows read:

Step Cache disabled Cache
Run cancellation request 4183 122
Prepare the parent scope 5413 124
Deliver (40 timer fences) 12181 165
Deliver again 13860 165

Tests (history_cache):

  • a_reread_fetches_only_new_rows_and_sees_every_write covers append-after-read, an unchanged reread returning the same snapshot with no rows fetched, another run, the in-place update, a gapped delta forcing a full reread, and rollback and retry.
  • a_wide_scope_tree_is_requested_and_delivered_reading_history_about_once drives the router end to end: 40 scopes, run request, prepare, deliver with 40 timer fences, then an idempotent redelivery. Its row bounds fail with the cache disabled (the run request then reads 4183 rows).

Verification at this head

Local, at bef921707 with a clean tree:

  • SQLite, all targets: lib 308, including both history_cache tests; execution 519 (1 ignored); all other targets pass.
  • Native shared fixtures (recorded over HTTP against the debug server built from this head): cancellation-scopes, activity-cancellation-acknowledgement, cooperative-before-claim-cleanup, cooperative-cleanup-deadline, cooperative-pending-timer-cleanup, cancel-leased-activity and cancel-pending-timer pass on SQLite, PostgreSQL 17, MySQL 8 and MariaDB 10.11.
  • PostgreSQL 17 and MySQL 8: execution completion_budget, parallel_cancellation, cooperative*, activity_cancellation_ack, history_retention, namespaces, local_activities and child_cascade pass, 81/81 on each. These include the retention and namespace delete paths that clear the cache.
  • Counterfactual: with the cache disabled, the wide-tree test fails on its first row bound (the run request read 4183 rows).

Hosted CI on this exact head is running. I'll post its terminal state here. On the previous head (5bc3dd03), the only failure was a published-PHP 2.11.1 500 on update submit while recording update-validation on MariaDB (job 121384886683), the #726 lock-pressure class; every native check passed.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Hosted CI on exact head bef921707237bd186a2d92ca10b3622426158453 is terminal: 56 checks pass, 0 fail. One check is skipped by design: Candidate cancellation and prepared-local source qualification, which applies only to candidate-source runs. The workflow runs all succeeded: Shared Server fixtures 40267529807, PHPUnit Feature 40267529817, SonarQube 40267529696, Public Boundary 40267529804, and PR #771 40267527692. The merge state is clean against main 086ba7fa. This head is held for Security source clearance (package: comment 6408146718).

@rmcdaniel

Copy link
Copy Markdown
Member Author

Security source clearance received for exact head bef921707237bd186a2d92ca10b3622426158453, tree c03efc956552e521fa9de955f53a5478e175412f, and only that head. Source: sender security, session 01a16c9c-ac8d-7280-bed7-a647973bc7d1, server-325 chat cursor 2021, client message security-server771-bef92170-clearance-20261026-1. Security reviewed the complete 5bc3dd03f..bef921707 cache delta and the write-path inventory. Exact-head hosted CI is terminal: 56 pass, 0 fail, one path-inapplicable skip. Cleared to merge this head subject to normal repository gates; any source or tree change requires renewed review. This does not authorize deployment or broaden scope.

@rmcdaniel
rmcdaniel merged commit 5867f36 into main Oct 26, 2026
58 checks passed
@rmcdaniel
rmcdaniel deleted the rust-325-cancellation-scopes branch October 26, 2026 04:42
@rmcdaniel

Copy link
Copy Markdown
Member Author

Merged as 5867f369643c5a361739b4529e67528d085f2718. Parents are 086ba7fa (main) and bef92170 (the cleared head). The merge tree c03efc956552e521fa9de955f53a5478e175412f equals the Security-cleared tree. Branch and backup ref deleted. Next Rust activation: Nexus reads, requalified on this main.

rmcdaniel added a commit that referenced this pull request Oct 26, 2026
…-operations

The port guide counts 145 served route methods: Nexus reads add two and
cancellation scopes three to the base 140. The workflow test list carries
both contract tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
rmcdaniel added a commit that referenced this pull request Oct 26, 2026
…low-stream-writes

The port guide counts 145 served route methods: stream writes add two and
cancellation scopes three to the base 140. The workflow test list carries
both contract tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
rmcdaniel added a commit that referenced this pull request Oct 26, 2026
…e-adapters

The port guide counts 144 served route methods: bridge webhooks add one and
cancellation scopes three to the base 140. The workflow test list carries
both contract tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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