From 95d421d6211d7d470494d24fc5ebd49499cea961 Mon Sep 17 00:00:00 2001 From: "glm-5.1" Date: Mon, 11 May 2026 02:15:14 +0000 Subject: [PATCH] docs: update task status to completed for call-envelope-integration --- .../001-call-envelope-integration.md | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 tasks/call-protocol/001-call-envelope-integration.md diff --git a/tasks/call-protocol/001-call-envelope-integration.md b/tasks/call-protocol/001-call-envelope-integration.md new file mode 100644 index 0000000..3215013 --- /dev/null +++ b/tasks/call-protocol/001-call-envelope-integration.md @@ -0,0 +1,86 @@ +--- +id: call-envelope-integration +name: Update PendingRequestMap and CallHandler for ResponseEnvelope +status: completed +depends_on: [response-envelope-tests] +scope: broad +risk: medium +impact: project +level: implementation +--- + +## Description + +Update `src/call.ts` to integrate `ResponseEnvelope` throughout the call protocol. This covers all changes documented in call-protocol.md and api-surface.md's "Source vs. Spec Drift" sections for ADR-005. + +Changes needed: + +### `CallEventSchema["call.responded"]` +- Change `output` from `Type.Unknown()` to `ResponseEnvelopeSchema` + +### `PendingRequestMap.respond()` +- Add `isResponseEnvelope()` guard — throws if `output` is not a valid envelope +- This enforces the invariant that all call protocol responses carry source metadata + +### `PendingRequestMap.call()` +- Return type changes from `Promise` to `Promise` +- Resolution: the `call.responded` subscription handler resolves with the `ResponseEnvelope` from `output` field (which is already validated by `respond()`) + +### `CallHandler` +- **Handler model change**: Handler returns a value; `CallHandler` wraps and publishes. Handler does NOT publish `call.responded` itself. +- Capture handler return value +- Apply shared result pipeline: + 1. Detect: `isResponseEnvelope(result)` → pass through + 2. Wrap: `localEnvelope(result, operationId)` + 3. Normalize: `Value.Cast(spec.outputSchema, envelope.data)` when `outputSchema !== Type.Unknown()` + 4. Validate: `collectErrors` on `envelope.data` — warning-only +- Publish `call.responded` via `callMap.respond(requestId, envelope)` +- On handler exception: `mapError()` converts to `CallError`, publish `call.error` + +**Note**: Access control in `CallHandler` stays as-is (checking `identity` and `checkAccess`). ADR-006's change to make `CallHandler` call `execute()` is a separate task. + +## Acceptance Criteria + +- [x] `CallEventSchema["call.responded"].output` is `ResponseEnvelopeSchema` +- [x] `PendingRequestMap.respond()` validates `output` with `isResponseEnvelope()`, throws on raw values +- [x] `PendingRequestMap.call()` return type is `Promise` +- [x] `CallHandler` captures handler return value instead of discarding it +- [x] `CallHandler` applies result pipeline: detect → wrap → normalize → validate +- [x] `CallHandler` publishes `call.responded` via `callMap.respond()` with the envelope +- [x] `CallHandler` on handler exception publishes `call.error` (not re-throws) +- [x] Adapter handlers (pre-built envelopes via `mcpEnvelope`/`httpEnvelope`) pass through via `isResponseEnvelope()` +- [x] Existing `call.test.ts` tests updated for new return types and behavior +- [x] New tests for: envelope validation in `respond()`, envelope wrapping in `CallHandler`, envelope passthrough, Value.Cast normalization +- [x] `npm run build` passes +- [x] `npm run lint` passes +- [x] `npm test` passes + +## References + +- docs/architecture/call-protocol.md § Source vs. Spec Drift (ADR-005 items) +- docs/architecture/api-surface.md § Source vs. Spec Drift (ADR-005 items) +- docs/architecture/response-envelopes.md § CallHandler, § PendingRequestMap +- src/call.ts + +## Notes + +`CallHandlerConfig` changed from `{ registry, eventTarget? }` to `{ registry, callMap? }` to support publishing `call.responded` via `callMap.respond()`. When `callMap` is not provided, errors are thrown directly (backward-compatible for direct use without the call protocol). + +`Value.Cast` in TypeBox does not strip excess properties from objects — it only fills defaults and upcasts values. The test was updated to verify default-filling behavior rather than property stripping. + +## Summary + +Integrated ResponseEnvelope throughout the call protocol in `src/call.ts`. + +- Modified: `src/call.ts` (52 lines changed) + - `CallEventSchema["call.responded"].output` → `ResponseEnvelopeSchema` + - `PendingRequest.call()` → `Promise` + - `PendingRequestMap.respond()` → validates with `isResponseEnvelope()`, throws on raw values + - `CallHandler` → captures return value, applies detect→wrap→normalize→validate pipeline, publishes via `callMap` + - `CallHandlerConfig` → `{ registry, callMap? }` replacing `eventTarget?` + - Added imports: `KindGuard`, `Value`, `collectErrors`, `formatValueErrors`, `ResponseEnvelopeSchema`, `isResponseEnvelope`, `localEnvelope` +- Modified: `test/call.test.ts` (547 lines added) + - Updated existing tests to pass `ResponseEnvelope` to `respond()` + - Added 23 new tests: envelope validation in `respond()`, envelope wrapping in `CallHandler`, envelope passthrough (mcp/http), `Value.Cast` normalization, error publishing, and no-callMap fallback behavior +- All 189 tests passing +- Build and lint passing \ No newline at end of file