Skip to content

QVAC-18183 feat[api]: non-inference migrations + decorated-promise requestId#2060

Merged
simon-iribarren merged 8 commits into
tetherto:mainfrom
simon-iribarren:feat/qvac-18183-non-inference-migrations-decorated-promise
May 15, 2026
Merged

QVAC-18183 feat[api]: non-inference migrations + decorated-promise requestId#2060
simon-iribarren merged 8 commits into
tetherto:mainfrom
simon-iribarren:feat/qvac-18183-non-inference-migrations-decorated-promise

Conversation

@simon-iribarren

@simon-iribarren simon-iribarren commented May 14, 2026

Copy link
Copy Markdown
Contributor

🎯 What problem does this PR solve?

  • loadModel, downloadAsset, and rag were the last three long-running server-side handlers without registry-routed cancellation. Cancelling them meant either reaching into cancelHandler.ts's legacy case "downloadAsset" / case "rag" arms (workspace-wide or download-key-wide blast radius) or waiting for shutdown — there was no cancel({ requestId }) parity with completion/embeddings/transcribe/etc.
  • Both kinds also carried their own out-of-band cancel-tracking maps: activeTransfers (download-manager.ts) and activeOperations (rag-operation-manager.ts). Two sources of truth for "what's running" meant cancel routing through registry.cancel(...) could disagree with the ad-hoc maps, especially during shutdown sweeps.
  • The Promise-shaped public-API calls (loadModel(...), downloadAsset(...)) didn't expose requestId synchronously, so a client UI couldn't wire a Stop button to a specific in-flight call before its await resolved — only completion(...) had that contract (via the CompletionRun object).

📝 How does it solve it?

  • rag (kind "rag")server/rpc/handlers/rag.ts now opens await using ctx = registry.begin({ kind: "rag" }) for every workspace-bound op (ingest, saveEmbeddings, reindex). rag-operation-manager.ts is rewritten around a small workspace → requestId map plus a shutdown sweep (cancelAllRagOperations). The old activeOperations / registerRagOperation / unregisterRagOperation / cancelRagOperation API is removed.
  • downloadAsset (kind "downloadAsset") — handler wraps the resolve flow in registry.begin(...) and threads ctx.signal plus a DownloadHooks.requestBinding into resolveModelPath(...). In download-manager.ts, startOrJoinDownload now accepts a per-subscriber binding ({ signal, scope, requestId }). Content-addressed dedup keyed on downloadKey is preserved (two callers requesting the same artifact still share one transfer) but each subscriber attaches an abort listener that rejects its promise alone, and the new maybeCancelTransfer(transfer) helper aborts the underlying transfer only when the last subscriber leaves. scope.defer(() => removeSubscriber(...)) is the safety-net cleanup for handler-exit paths that don't go through the abort listener.
  • loadModel (kind "loadModel") — handler wraps the download-then-load flow in registry.begin(...), threads ctx.signal into the resolver chain, and gates the addon-level plugin.createModel(...) / model.load(false) call on ctx.signal.aborted before and after. The load phase is soft-cancel only — the addon's load path doesn't accept a signal today; documented as a follow-up.
  • Design decision: loadModel opens its registry context with modelId: undefined. LoadModelRequest for a fresh load doesn't carry a modelId until the config-hash is computed inside the handler, so the registry slot is keyed by requestId alone. Implication: cancel({ modelId, kind: "loadModel" }) is a no-op for in-progress loads — cancel by requestId (via op.requestId on the decorated promise) is the only path. This is captured in the inline comment on the handler and the "what's on the registry today" dispatch-level table in request-lifecycle-primitives.mdc; flagging here so reviewers don't have to re-derive it.
  • Decorated-promise requestId — new helper packages/sdk/utils/decorate-promise.ts (Object.assign-based, intentionally not a prototype extension). loadModel(...) and downloadAsset(...) return Promise<string> & { requestId: string } so callers can grab op.requestId synchronously and fire cancel({ requestId: op.requestId }) before the network round-trip resolves. The unwrap chain is preserved: await loadModel(...) still returns the model id (backward-compat).
  • Cursor rules updatedrequest-lifecycle-primitives.mdc flips the migration table to mark M3b/M3c shipped, documents the decorated-promise pattern, and adds the "what's on the registry today" dispatch-level truth table. docs/request-lifecycle-system.mdc flips the M3c roadmap row.

§4 Open decisions (resolved)

  • Decision A — cancel error class: A.1, reuse InferenceCancelledError (52419) across non-inference handlers. Zero new public-API surface. The naming oddity is filed as a potential rename follow-up.
  • Decision B — RAG workspace-level admission: B.2, dispatcher-level pre-emption in rag.ts. Before registry.begin({ kind: "rag" }), the dispatcher walks the workspace→requestId map and calls registry.cancel({ requestId: prev }) for any prior in-flight op on the same workspace. No new field on ConcurrencyPolicy. The cancel-prior → begin-new order is load-bearing.
  • resolveModelPath signal threading: signal: AbortSignal parameter, not a full ctx: RequestContext. The resolver doesn't need the context — just the signal — so the narrower parameter is the idiomatic shape across all call sites.

cancelHandler.ts's case "downloadAsset" and case "rag" arms stay intact (M1 compat contract). They now emit a deprecation log line and route through registry.cancel(...) rather than the legacy direct paths. M3d retires the dispatcher entirely.

🧪 How was it tested?

  • bun lint — clean
  • bun run typecheck — clean
  • bun test test/unit/ — all new tests green; pre-existing unrelated tts-multicast.test.ts failure on this worktree is a Cannot find package '#rpc' from missing dist/, present on a clean checkout of the base branch too (not introduced here)

New tests:

  • test/unit/utils/decorate-promise.test.ts — sync metadata access, unwrap-to-T (the backward-compat pin: await loadModel(...) → string), rejection propagation, in-place identity, .then / .catch / .finally chain integrity, multi-field decoration.
  • test/unit/download-manager-subscriber.test.ts — dedup preserved (two callers share one transfer), per-subscriber cancel doesn't affect siblings, last-subscriber leaving aborts the transfer, scope.defer safety net for handler-exit paths, cancel error carries requestId.
  • test/unit/rag-workspace-preempt.test.ts — workspace map get/set/clear semantics + the "still mine?" guard against stale scope-unwind clears stomping a newer op's mapping.
  • test/unit/request-id-wire.test.tsloadModel / downloadAsset / rag request schemas preserve requestId on the wire and treat it as optional for legacy clients (server falls back to a server-generated id).

Known follow-up

  • The loadModel load phase is soft-cancel: a cancel that arrives during plugin.createModel(...) / model.load(false) lands on the registry but the addon runs to completion. The handler still rejects the client's promise with InferenceCancelledError, but the model is now registered as loaded (orphan-model trade-off). A per-load cancel surface on the addon would close this; tracked as a separate follow-up.

🔌 API Changes

Decorated-promise return shape for loadModel / downloadAsset

Before:

const modelId: string = await sdk.loadModel({ modelSrc: "..." });
// No way to cancel this specific call without grabbing the modelId
// out-of-band and calling cancel({ operation: "inference", modelId })
// (which is broad-cancel — kills every in-flight request on that model).

After:

const op = sdk.loadModel({ modelSrc: "..." });
op.requestId;                              // ← synchronously available, before await
const modelId: string = await op;          // ← legacy unwrap still works

// Stop button → exactly this load, nothing else on the model:
stopButton.onclick = () => sdk.cancel({ requestId: op.requestId });

Same shape for downloadAsset:

const op = sdk.downloadAsset({ assetSrc: "https://example.com/big.gguf" });
setTimeout(() => sdk.cancel({ requestId: op.requestId }), 1000);
await op; // rejects with InferenceCancelledError if cancelled

The return type is Promise<string> & { requestId: string }. Existing call sites that use const id = await sdk.loadModel(...) keep working without modification — the decoration adds a property without touching the prototype chain.

Per-requestId cancel for rag

The rag handler now emits a requestId in the registry for every workspace-bound op (ingest / saveEmbeddings / reindex). Clients targeting a specific RAG op via the wire envelope can cancel it directly:

const requestId = crypto.randomUUID();
const result = sdk.rag({
  type: "rag",
  operation: "ingest",
  workspace: "ws-a",
  modelId: "m1",
  documents: [...],
  requestId, // optional on the wire; legacy clients omit it and the server generates one
});

await sdk.cancel({ requestId });

The legacy sdk.cancel({ operation: "rag", workspace }) arm still works (now routed through the registry with a deprecation log line; removed in M3d).

…questId

Migrate the last three pre-M3 long-running handlers onto the
`RequestRegistry` and expose `requestId` synchronously on the
Promise-shaped public-API calls.

Deliverables (per the M3c implementation brief):

1. **`rag`** (kind `"rag"`) — `server/rpc/handlers/rag.ts` now opens
   `await using ctx = registry.begin({ kind: "rag" })` for every
   workspace-bound op (`ingest`, `saveEmbeddings`, `reindex`).
   `rag-operation-manager.ts` is rewritten around a small
   workspace → requestId map (`get/set/clearActiveRagRequest`) plus a
   `cancelAllRagOperations` shutdown sweep. The pre-existing
   `activeOperations` / `registerRagOperation` /
   `unregisterRagOperation` / `cancelRagOperation` API is removed —
   nothing outside the manager touched it.

2. **`downloadAsset`** (kind `"downloadAsset"`) — handler wraps the
   resolve flow in `registry.begin(...)` and threads `ctx.signal` plus
   a `DownloadHooks.requestBinding` into `resolveModelPath(...)` /
   `resolveModelPathWithStats(...)`. In `download-manager.ts`,
   `startOrJoinDownload` now accepts a per-subscriber
   `SubscriberRequestBinding` ({ signal, scope, requestId }); the
   content-addressed dedup keyed on `downloadKey` is preserved (two
   callers requesting the same artifact still share one transfer) but
   each subscriber attaches an abort listener that rejects its
   promise alone, and the new `maybeCancelTransfer(transfer)` helper
   aborts the underlying transfer only when the **last** subscriber
   leaves. `scope.defer(() => removeSubscriber(...))` is the
   safety-net cleanup for handler-exit paths that don't go through
   the abort listener.

3. **`loadModel`** (kind `"loadModel"`) — handler wraps the
   download-then-load flow in `registry.begin(...)`, threads
   `ctx.signal` into the resolver chain, and gates the
   `plugin.createModel(...)` / `model.load(false)` call on
   `ctx.signal.aborted` before and after. Load phase is soft-cancel
   only — the addon's load path doesn't accept a signal today;
   documented as a follow-up.

4. **Decorated-promise `requestId`** — new helper
   `packages/sdk/utils/decorate-promise.ts` (`Object.assign`-based,
   intentionally not a prototype extension). `loadModel(...)` and
   `downloadAsset(...)` return `Promise<string> & { requestId: string }`
   so callers can grab `op.requestId` synchronously and fire
   `cancel({ requestId: op.requestId })` before the network round-trip
   resolves. The unwrap chain is preserved: `await loadModel(...)`
   still returns the model id, pinned by
   `test/unit/utils/decorate-promise.test.ts`.

5. **Cursor rules** — `request-lifecycle-primitives.mdc` flips the
   migration table to mark M3b/M3c shipped, documents the decorated-
   promise pattern, and adds the "what's on the registry today"
   dispatch-level truth table. `docs/request-lifecycle-system.mdc`
   flips the M3c roadmap row.

§4 Open decisions (resolved):

- **Decision A — cancel error class:** A.1, reuse
  `InferenceCancelledError` (52419) across all non-inference
  handlers. Zero new public-API surface. The naming oddity is filed
  as a potential rename follow-up.
- **Decision B — RAG workspace-level admission:** B.2, dispatcher-
  level pre-emption in `rag.ts`. Before `registry.begin({ kind: "rag" })`,
  the dispatcher walks the workspace→requestId map and calls
  `registry.cancel({ requestId: prev })` for any prior in-flight op
  on the same workspace. No new field on `ConcurrencyPolicy`. The
  cancel-prior → begin-new order is load-bearing — reversing it
  would cancel the just-installed context.
- **`resolveModelPath` signal threading:** `signal: AbortSignal`
  parameter, not a full `ctx: RequestContext`. The resolver doesn't
  need the context — just the signal — so the narrower parameter is
  the idiomatic shape across all call sites
  (`http.ts`, `hyperdrive.ts`, `registry.ts`, `resolve-session.ts`).

`cancelHandler.ts`'s `case "downloadAsset"` and `case "rag"` arms
stay intact (M1 compat contract). They now emit a deprecation log
line and route through `registry.cancel({ requestId })` rather than
the legacy direct paths. M3d retires the dispatcher entirely.

Tests added:

- `test/unit/utils/decorate-promise.test.ts` — sync metadata access,
  unwrap-to-T, rejection propagation, in-place identity, chain
  integrity.
- `test/unit/download-manager-subscriber.test.ts` — dedup preserved,
  per-subscriber cancel doesn't affect siblings, last-subscriber
  leaving aborts the transfer, `scope.defer` safety net, cancel
  error carries `requestId`.
- `test/unit/rag-workspace-preempt.test.ts` — workspace map
  get/set/clear semantics + the "still mine?" guard against stale
  scope-unwind clears.
- `test/unit/request-id-wire.test.ts` — `loadModel` / `downloadAsset` /
  `rag` request schemas preserve `requestId` on the wire and treat
  it as optional for legacy clients.

`bun lint`, `bun run typecheck`, `bun test test/unit/` (additive
change — pre-existing unrelated `tts-multicast` test failure on this
worktree is from missing `dist/` for the `#rpc` package alias; not
introduced here).
- Soften M3b roadmap rows to "in review (tetherto#2058)" in request-lifecycle
  primitives + system docs until tetherto#2058 merges (avoids briefly advertising
  M3b as shipped while M3c lands first).
- Promote the "no in-flight match" log in cancelHandler.ts's
  `case "request"` arm from debug to info: the decorated-promise pattern
  makes post-settle Stop-button cancels a common, user-visible code path
  and needs to surface without lowering the log level.
- Route cancelTransfer's orphan-subscriber settle through
  removeSubscriber(...) so the last-subscriber teardown rule is enforced
  in one place (drops a redundant inline abort guard).
@simon-iribarren simon-iribarren added the verified Authorize secrets / label-gate in PR workflows label May 14, 2026
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

github-actions Bot commented May 14, 2026

Copy link
Copy Markdown
Contributor

QVAC E2E — windows — ✅ all tests passed (91/91, 428s)

Config: suite=smoke · filter=(none) · exclude=(none)
View run · Artifacts: reports

@github-actions

github-actions Bot commented May 14, 2026

Copy link
Copy Markdown
Contributor

QVAC E2E — linux — ❌ failed

Totals: 90/91 passed · 1 failed · 98.9% · 377s
Config: suite=smoke · filter=(none) · exclude=(none)
View run · Artifacts: reports

Results by section

  • addon-logging: 1/2 ❌

Failed tests

  • addon-logging-during-inference: Logging test error (addon-logging-during-inference): Request "6f5330eb-65b4-4925-99a7-1f10f04b01d0" (kind: completion, modelId: 4b7b9d71f364940c) was rejected by registry concurrency policy: another …

@github-actions

github-actions Bot commented May 14, 2026

Copy link
Copy Markdown
Contributor

QVAC E2E — android — ❌ failed

Totals: 82/91 passed · 1 failed · 98.8% · 2031s
Config: suite=smoke · filter=(none) · exclude=(none)
View run · Artifacts: reports · Device Farm logs

Results by section

  • addon-logging: 1/2 ❌

Failed tests

  • addon-logging-during-inference: Logging test error (addon-logging-during-inference): Request "d569e05f2386e82425bbc499e8ebacdb" (kind: completion, modelId: 4b7b9d71f364940c) was rejected by registry concurrency policy: another comp…

@github-actions

github-actions Bot commented May 14, 2026

Copy link
Copy Markdown
Contributor

QVAC E2E — macos — ✅ all tests passed (91/91, 335s)

Config: suite=smoke · filter=(none) · exclude=(none)
View run · Artifacts: reports

@simon-iribarren simon-iribarren removed test-e2e-smoke Triggers smoke e2e test suite [Currently SDK-only] verified Authorize secrets / label-gate in PR workflows labels May 14, 2026
Victor-Rodzko
Victor-Rodzko previously approved these changes May 14, 2026
Comment thread .cursor/rules/sdk/docs/request-lifecycle-system.mdc Outdated
…etherto#2060 review feedback)

Removed the "Migration Roadmap" table, "M1/M2/M3a-d" milestone labels, planning-brief
decision references (Decision A/B.2, D1/D2), workspace-local paths
(`tasks/release-0.11.0-planning/...`, `pitch-3-decisions.md`), and "in review tetherto#2058"
forward-references from the request-lifecycle cursor rules and the matching code
comments. The dispatch-level "What's on the registry today" truth table remains and
now reflects only the kinds actually routed through the registry on this branch.
@simon-iribarren

Copy link
Copy Markdown
Contributor Author

/review

@github-actions

Copy link
Copy Markdown
Contributor

Tier-based Approval Status

**PR Tier:** TIER1

**Current Status:** ✅ APPROVED

**Requirements:**
- 1 Team Member approval ✅ (2/1)
- 1 Team Lead OR Management approval ✅ (1/1)



---
*This comment is automatically updated when reviews change.*

@simon-iribarren simon-iribarren merged commit 7db6e32 into tetherto:main May 15, 2026
18 checks passed
simon-iribarren added a commit to simon-iribarren/qvac that referenced this pull request May 15, 2026
Absorbed M3c (tetherto#2060) merge from main; reconciled rule files (union of
registry-routed kinds in 'What's on the registry today' table — kept
main's completion/loadModel/downloadAsset/rag rows and added M3b's
embeddings/transcribe/translate/finetune rows for 8 total), schemas /
plugin manifests (kept M3b's `requestId` field and `cancel: { scope, hard }`
declarations intact), and ops comment cleanup (took main's trim where
prose conflicted; M3b's implementation logic was identical on both sides).

The "Implementation Files" table in
.cursor/rules/sdk/docs/request-lifecycle-system.mdc is the union of both
branches' rows, with M3b's expanded descriptions kept (they're a strict
superset of main's). Concurrency-policy paragraphs in both rule files
keep M3b's wording since it's the more informative version covering the
actually-migrated kinds.

Verified: bun lint clean, bun run test:unit clean modulo the documented
pre-existing 'Cannot find package #rpc' from client/rpc/rpc-client.ts.
Proletter pushed a commit that referenced this pull request May 24, 2026
…questId (#2060)

* QVAC-18183 feat[api]: non-inference migrations + decorated-promise requestId

Migrate the last three pre-M3 long-running handlers onto the
`RequestRegistry` and expose `requestId` synchronously on the
Promise-shaped public-API calls.

Deliverables (per the M3c implementation brief):

1. **`rag`** (kind `"rag"`) — `server/rpc/handlers/rag.ts` now opens
   `await using ctx = registry.begin({ kind: "rag" })` for every
   workspace-bound op (`ingest`, `saveEmbeddings`, `reindex`).
   `rag-operation-manager.ts` is rewritten around a small
   workspace → requestId map (`get/set/clearActiveRagRequest`) plus a
   `cancelAllRagOperations` shutdown sweep. The pre-existing
   `activeOperations` / `registerRagOperation` /
   `unregisterRagOperation` / `cancelRagOperation` API is removed —
   nothing outside the manager touched it.

2. **`downloadAsset`** (kind `"downloadAsset"`) — handler wraps the
   resolve flow in `registry.begin(...)` and threads `ctx.signal` plus
   a `DownloadHooks.requestBinding` into `resolveModelPath(...)` /
   `resolveModelPathWithStats(...)`. In `download-manager.ts`,
   `startOrJoinDownload` now accepts a per-subscriber
   `SubscriberRequestBinding` ({ signal, scope, requestId }); the
   content-addressed dedup keyed on `downloadKey` is preserved (two
   callers requesting the same artifact still share one transfer) but
   each subscriber attaches an abort listener that rejects its
   promise alone, and the new `maybeCancelTransfer(transfer)` helper
   aborts the underlying transfer only when the **last** subscriber
   leaves. `scope.defer(() => removeSubscriber(...))` is the
   safety-net cleanup for handler-exit paths that don't go through
   the abort listener.

3. **`loadModel`** (kind `"loadModel"`) — handler wraps the
   download-then-load flow in `registry.begin(...)`, threads
   `ctx.signal` into the resolver chain, and gates the
   `plugin.createModel(...)` / `model.load(false)` call on
   `ctx.signal.aborted` before and after. Load phase is soft-cancel
   only — the addon's load path doesn't accept a signal today;
   documented as a follow-up.

4. **Decorated-promise `requestId`** — new helper
   `packages/sdk/utils/decorate-promise.ts` (`Object.assign`-based,
   intentionally not a prototype extension). `loadModel(...)` and
   `downloadAsset(...)` return `Promise<string> & { requestId: string }`
   so callers can grab `op.requestId` synchronously and fire
   `cancel({ requestId: op.requestId })` before the network round-trip
   resolves. The unwrap chain is preserved: `await loadModel(...)`
   still returns the model id, pinned by
   `test/unit/utils/decorate-promise.test.ts`.

5. **Cursor rules** — `request-lifecycle-primitives.mdc` flips the
   migration table to mark M3b/M3c shipped, documents the decorated-
   promise pattern, and adds the "what's on the registry today"
   dispatch-level truth table. `docs/request-lifecycle-system.mdc`
   flips the M3c roadmap row.

§4 Open decisions (resolved):

- **Decision A — cancel error class:** A.1, reuse
  `InferenceCancelledError` (52419) across all non-inference
  handlers. Zero new public-API surface. The naming oddity is filed
  as a potential rename follow-up.
- **Decision B — RAG workspace-level admission:** B.2, dispatcher-
  level pre-emption in `rag.ts`. Before `registry.begin({ kind: "rag" })`,
  the dispatcher walks the workspace→requestId map and calls
  `registry.cancel({ requestId: prev })` for any prior in-flight op
  on the same workspace. No new field on `ConcurrencyPolicy`. The
  cancel-prior → begin-new order is load-bearing — reversing it
  would cancel the just-installed context.
- **`resolveModelPath` signal threading:** `signal: AbortSignal`
  parameter, not a full `ctx: RequestContext`. The resolver doesn't
  need the context — just the signal — so the narrower parameter is
  the idiomatic shape across all call sites
  (`http.ts`, `hyperdrive.ts`, `registry.ts`, `resolve-session.ts`).

`cancelHandler.ts`'s `case "downloadAsset"` and `case "rag"` arms
stay intact (M1 compat contract). They now emit a deprecation log
line and route through `registry.cancel({ requestId })` rather than
the legacy direct paths. M3d retires the dispatcher entirely.

Tests added:

- `test/unit/utils/decorate-promise.test.ts` — sync metadata access,
  unwrap-to-T, rejection propagation, in-place identity, chain
  integrity.
- `test/unit/download-manager-subscriber.test.ts` — dedup preserved,
  per-subscriber cancel doesn't affect siblings, last-subscriber
  leaving aborts the transfer, `scope.defer` safety net, cancel
  error carries `requestId`.
- `test/unit/rag-workspace-preempt.test.ts` — workspace map
  get/set/clear semantics + the "still mine?" guard against stale
  scope-unwind clears.
- `test/unit/request-id-wire.test.ts` — `loadModel` / `downloadAsset` /
  `rag` request schemas preserve `requestId` on the wire and treat
  it as optional for legacy clients.

`bun lint`, `bun run typecheck`, `bun test test/unit/` (additive
change — pre-existing unrelated `tts-multicast` test failure on this
worktree is from missing `dist/` for the `#rpc` package alias; not
introduced here).

* QVAC-18183 chore: address PR #2060 review nits

- Soften M3b roadmap rows to "in review (#2058)" in request-lifecycle
  primitives + system docs until #2058 merges (avoids briefly advertising
  M3b as shipped while M3c lands first).
- Promote the "no in-flight match" log in cancelHandler.ts's
  `case "request"` arm from debug to info: the decorated-promise pattern
  makes post-settle Stop-button cancels a common, user-visible code path
  and needs to surface without lowering the log level.
- Route cancelTransfer's orphan-subscriber settle through
  removeSubscriber(...) so the last-subscriber teardown rule is enforced
  in one place (drops a redundant inline abort guard).

* QVAC-18183 doc: trim internal milestone references from cursor rules (#2060 review feedback)

Removed the "Migration Roadmap" table, "M1/M2/M3a-d" milestone labels, planning-brief
decision references (Decision A/B.2, D1/D2), workspace-local paths
(`tasks/release-0.11.0-planning/...`, `pitch-3-decisions.md`), and "in review #2058"
forward-references from the request-lifecycle cursor rules and the matching code
comments. The dispatch-level "What's on the registry today" truth table remains and
now reflects only the kinds actually routed through the registry on this branch.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants