@moltzap/protocol 2026.503.3 → 2026.504.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/brands.d.ts +11 -0
- package/dist/brands.d.ts.map +1 -0
- package/dist/brands.js +14 -0
- package/dist/brands.js.map +1 -0
- package/dist/helpers.d.ts +29 -26
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +34 -16
- package/dist/helpers.js.map +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/dist/internal/ajv.d.ts +5 -0
- package/dist/internal/ajv.d.ts.map +1 -0
- package/dist/internal/ajv.js +22 -0
- package/dist/internal/ajv.js.map +1 -0
- package/dist/notification.d.ts +14 -0
- package/dist/notification.d.ts.map +1 -0
- package/dist/notification.js +11 -0
- package/dist/notification.js.map +1 -0
- package/dist/rpc-errors.d.ts +30 -0
- package/dist/rpc-errors.d.ts.map +1 -0
- package/dist/rpc-errors.js +23 -0
- package/dist/rpc-errors.js.map +1 -0
- package/dist/rpc-groups.d.ts +120 -0
- package/dist/rpc-groups.d.ts.map +1 -0
- package/dist/rpc-groups.js +131 -0
- package/dist/rpc-groups.js.map +1 -0
- package/dist/rpc-registry.d.ts +1238 -278
- package/dist/rpc-registry.d.ts.map +1 -1
- package/dist/rpc-registry.js +11 -22
- package/dist/rpc-registry.js.map +1 -1
- package/dist/rpc.d.ts +26 -5
- package/dist/rpc.d.ts.map +1 -1
- package/dist/rpc.js +21 -11
- package/dist/rpc.js.map +1 -1
- package/dist/schema/apps.d.ts +13 -23
- package/dist/schema/apps.d.ts.map +1 -1
- package/dist/schema/apps.js +2 -12
- package/dist/schema/apps.js.map +1 -1
- package/dist/schema/contacts.d.ts +6 -2
- package/dist/schema/contacts.d.ts.map +1 -1
- package/dist/schema/conversations.d.ts +15 -5
- package/dist/schema/conversations.d.ts.map +1 -1
- package/dist/schema/delivery.d.ts +9 -3
- package/dist/schema/delivery.d.ts.map +1 -1
- package/dist/schema/errors.d.ts +0 -4
- package/dist/schema/errors.d.ts.map +1 -1
- package/dist/schema/errors.js +0 -4
- package/dist/schema/errors.js.map +1 -1
- package/dist/schema/frames.d.ts +48 -51
- package/dist/schema/frames.d.ts.map +1 -1
- package/dist/schema/frames.js +28 -50
- package/dist/schema/frames.js.map +1 -1
- package/dist/schema/identity.d.ts +17 -9
- package/dist/schema/identity.d.ts.map +1 -1
- package/dist/schema/index.d.ts +3 -4
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +3 -4
- package/dist/schema/index.js.map +1 -1
- package/dist/schema/internal-frames.d.ts +10 -0
- package/dist/schema/internal-frames.d.ts.map +1 -0
- package/dist/schema/internal-frames.js +5 -0
- package/dist/schema/internal-frames.js.map +1 -0
- package/dist/schema/invites.d.ts +6 -2
- package/dist/schema/invites.d.ts.map +1 -1
- package/dist/schema/json-rpc.d.ts +27 -0
- package/dist/schema/json-rpc.d.ts.map +1 -0
- package/dist/schema/json-rpc.js +32 -0
- package/dist/schema/json-rpc.js.map +1 -0
- package/dist/schema/messages.d.ts +15 -5
- package/dist/schema/messages.d.ts.map +1 -1
- package/dist/schema/methods/apps.d.ts +138 -85
- package/dist/schema/methods/apps.d.ts.map +1 -1
- package/dist/schema/methods/apps.js +4 -45
- package/dist/schema/methods/apps.js.map +1 -1
- package/dist/schema/methods/auth.d.ts +62 -34
- package/dist/schema/methods/auth.d.ts.map +1 -1
- package/dist/schema/methods/contacts.d.ts +27 -9
- package/dist/schema/methods/contacts.d.ts.map +1 -1
- package/dist/schema/methods/conversations.d.ts +91 -18
- package/dist/schema/methods/conversations.d.ts.map +1 -1
- package/dist/schema/methods/conversations.js +2 -2
- package/dist/schema/methods/conversations.js.map +1 -1
- package/dist/schema/methods/messages.d.ts +39 -13
- package/dist/schema/methods/messages.d.ts.map +1 -1
- package/dist/schema/methods/presence.d.ts +6 -2
- package/dist/schema/methods/presence.d.ts.map +1 -1
- package/dist/schema/notifications.d.ts +791 -0
- package/dist/schema/notifications.d.ts.map +1 -0
- package/dist/schema/notifications.js +173 -0
- package/dist/schema/notifications.js.map +1 -0
- package/dist/schema/presence.d.ts +3 -1
- package/dist/schema/presence.d.ts.map +1 -1
- package/dist/schema/primitives.d.ts +21 -5
- package/dist/schema/primitives.d.ts.map +1 -1
- package/dist/schema/primitives.js +6 -0
- package/dist/schema/primitives.js.map +1 -1
- package/dist/test-fixtures/seed-data.d.ts +0 -1388
- package/dist/test-fixtures/seed-data.d.ts.map +1 -1
- package/dist/test-fixtures/seed-data.js +34 -990
- package/dist/test-fixtures/seed-data.js.map +1 -1
- package/dist/testing/agent-registration.d.ts +3 -1
- package/dist/testing/agent-registration.d.ts.map +1 -1
- package/dist/testing/agent-registration.js +6 -15
- package/dist/testing/agent-registration.js.map +1 -1
- package/dist/testing/arbitraries/frames.d.ts +2 -2
- package/dist/testing/arbitraries/frames.d.ts.map +1 -1
- package/dist/testing/arbitraries/frames.js +9 -7
- package/dist/testing/arbitraries/frames.js.map +1 -1
- package/dist/testing/arbitraries/from-typebox.d.ts.map +1 -1
- package/dist/testing/arbitraries/from-typebox.js +19 -10
- package/dist/testing/arbitraries/from-typebox.js.map +1 -1
- package/dist/testing/arbitraries/index.d.ts +1 -1
- package/dist/testing/arbitraries/index.d.ts.map +1 -1
- package/dist/testing/arbitraries/index.js +1 -1
- package/dist/testing/arbitraries/index.js.map +1 -1
- package/dist/testing/arbitraries/rpc.d.ts +3 -2
- package/dist/testing/arbitraries/rpc.d.ts.map +1 -1
- package/dist/testing/arbitraries/rpc.js +13 -3
- package/dist/testing/arbitraries/rpc.js.map +1 -1
- package/dist/testing/captures.d.ts.map +1 -1
- package/dist/testing/captures.js +4 -3
- package/dist/testing/captures.js.map +1 -1
- package/dist/testing/codec.d.ts +13 -11
- package/dist/testing/codec.d.ts.map +1 -1
- package/dist/testing/codec.js +43 -50
- package/dist/testing/codec.js.map +1 -1
- package/dist/testing/conformance/__divergence_proofs__/executable-proof-helpers.d.ts.map +1 -1
- package/dist/testing/conformance/__divergence_proofs__/executable-proof-helpers.js +15 -5
- package/dist/testing/conformance/__divergence_proofs__/executable-proof-helpers.js.map +1 -1
- package/dist/testing/conformance/_helpers.d.ts +12 -7
- package/dist/testing/conformance/_helpers.d.ts.map +1 -1
- package/dist/testing/conformance/_helpers.js +30 -7
- package/dist/testing/conformance/_helpers.js.map +1 -1
- package/dist/testing/conformance/adversity.d.ts.map +1 -1
- package/dist/testing/conformance/adversity.js +83 -90
- package/dist/testing/conformance/adversity.js.map +1 -1
- package/dist/testing/conformance/boundary.d.ts +1 -1
- package/dist/testing/conformance/boundary.d.ts.map +1 -1
- package/dist/testing/conformance/boundary.js +55 -50
- package/dist/testing/conformance/boundary.js.map +1 -1
- package/dist/testing/conformance/client/_fixtures.d.ts +7 -5
- package/dist/testing/conformance/client/_fixtures.d.ts.map +1 -1
- package/dist/testing/conformance/client/_fixtures.js +17 -8
- package/dist/testing/conformance/client/_fixtures.js.map +1 -1
- package/dist/testing/conformance/client/adversity.d.ts.map +1 -1
- package/dist/testing/conformance/client/adversity.js +28 -22
- package/dist/testing/conformance/client/adversity.js.map +1 -1
- package/dist/testing/conformance/client/boundary.d.ts +2 -2
- package/dist/testing/conformance/client/boundary.d.ts.map +1 -1
- package/dist/testing/conformance/client/boundary.js +19 -16
- package/dist/testing/conformance/client/boundary.js.map +1 -1
- package/dist/testing/conformance/client/delivery.d.ts +5 -5
- package/dist/testing/conformance/client/delivery.d.ts.map +1 -1
- package/dist/testing/conformance/client/delivery.js +77 -73
- package/dist/testing/conformance/client/delivery.js.map +1 -1
- package/dist/testing/conformance/client/index.d.ts +2 -2
- package/dist/testing/conformance/client/index.d.ts.map +1 -1
- package/dist/testing/conformance/client/index.js +1 -1
- package/dist/testing/conformance/client/index.js.map +1 -1
- package/dist/testing/conformance/client/rpc-semantics.d.ts.map +1 -1
- package/dist/testing/conformance/client/rpc-semantics.js +31 -37
- package/dist/testing/conformance/client/rpc-semantics.js.map +1 -1
- package/dist/testing/conformance/client/runner.d.ts +38 -32
- package/dist/testing/conformance/client/runner.d.ts.map +1 -1
- package/dist/testing/conformance/client/runner.js +36 -45
- package/dist/testing/conformance/client/runner.js.map +1 -1
- package/dist/testing/conformance/client/schema-conformance.d.ts +8 -8
- package/dist/testing/conformance/client/schema-conformance.d.ts.map +1 -1
- package/dist/testing/conformance/client/schema-conformance.js +37 -35
- package/dist/testing/conformance/client/schema-conformance.js.map +1 -1
- package/dist/testing/conformance/client/suite.d.ts.map +1 -1
- package/dist/testing/conformance/client/suite.js +4 -3
- package/dist/testing/conformance/client/suite.js.map +1 -1
- package/dist/testing/conformance/delivery.d.ts.map +1 -1
- package/dist/testing/conformance/delivery.js +127 -140
- package/dist/testing/conformance/delivery.js.map +1 -1
- package/dist/testing/conformance/dispatcher-concurrency.js +12 -12
- package/dist/testing/conformance/dispatcher-concurrency.js.map +1 -1
- package/dist/testing/conformance/env.d.ts +9 -0
- package/dist/testing/conformance/env.d.ts.map +1 -1
- package/dist/testing/conformance/env.js +16 -3
- package/dist/testing/conformance/env.js.map +1 -1
- package/dist/testing/conformance/presence.d.ts.map +1 -1
- package/dist/testing/conformance/presence.js +35 -24
- package/dist/testing/conformance/presence.js.map +1 -1
- package/dist/testing/conformance/registry.d.ts +1 -1
- package/dist/testing/conformance/registry.d.ts.map +1 -1
- package/dist/testing/conformance/registry.js +1 -1
- package/dist/testing/conformance/registry.js.map +1 -1
- package/dist/testing/conformance/rpc-semantics.d.ts +12 -12
- package/dist/testing/conformance/rpc-semantics.d.ts.map +1 -1
- package/dist/testing/conformance/rpc-semantics.js +131 -90
- package/dist/testing/conformance/rpc-semantics.js.map +1 -1
- package/dist/testing/conformance/runner.d.ts +2 -2
- package/dist/testing/conformance/runner.d.ts.map +1 -1
- package/dist/testing/conformance/runner.js +5 -10
- package/dist/testing/conformance/runner.js.map +1 -1
- package/dist/testing/conformance/schema-conformance.d.ts +3 -40
- package/dist/testing/conformance/schema-conformance.d.ts.map +1 -1
- package/dist/testing/conformance/schema-conformance.js +75 -235
- package/dist/testing/conformance/schema-conformance.js.map +1 -1
- package/dist/testing/conformance/suite.d.ts +1 -1
- package/dist/testing/conformance/suite.d.ts.map +1 -1
- package/dist/testing/conformance/suite.js +17 -18
- package/dist/testing/conformance/suite.js.map +1 -1
- package/dist/testing/index.d.ts +1 -1
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/models/dispatch.d.ts +5 -5
- package/dist/testing/models/dispatch.d.ts.map +1 -1
- package/dist/testing/models/dispatch.js +50 -59
- package/dist/testing/models/dispatch.js.map +1 -1
- package/dist/testing/models/state.d.ts +5 -5
- package/dist/testing/models/state.d.ts.map +1 -1
- package/dist/testing/models/state.js +14 -1
- package/dist/testing/models/state.js.map +1 -1
- package/dist/testing/test-client.d.ts +51 -40
- package/dist/testing/test-client.d.ts.map +1 -1
- package/dist/testing/test-client.js +154 -122
- package/dist/testing/test-client.js.map +1 -1
- package/dist/testing/test-server.d.ts +5 -5
- package/dist/testing/test-server.d.ts.map +1 -1
- package/dist/testing/test-server.js +11 -15
- package/dist/testing/test-server.js.map +1 -1
- package/dist/testing/toxics/client.d.ts.map +1 -1
- package/dist/testing/toxics/client.js +9 -3
- package/dist/testing/toxics/client.js.map +1 -1
- package/dist/testing/toxics/profile.d.ts.map +1 -1
- package/dist/testing/toxics/profile.js +4 -1
- package/dist/testing/toxics/profile.js.map +1 -1
- package/dist/types.d.ts +3 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/validators.d.ts +366 -101
- package/dist/validators.d.ts.map +1 -1
- package/dist/validators.js +23 -30
- package/dist/validators.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
- package/dist/schema/events.d.ts +0 -213
- package/dist/schema/events.d.ts.map +0 -1
- package/dist/schema/events.js +0 -122
- package/dist/schema/events.js.map +0 -1
- package/dist/schema/methods/push.d.ts +0 -21
- package/dist/schema/methods/push.d.ts.map +0 -1
- package/dist/schema/methods/push.js +0 -28
- package/dist/schema/methods/push.js.map +0 -1
- package/dist/schema/surfaces.d.ts +0 -55
- package/dist/schema/surfaces.d.ts.map +0 -1
- package/dist/schema/surfaces.js +0 -55
- package/dist/schema/surfaces.js.map +0 -1
|
@@ -26,10 +26,17 @@
|
|
|
26
26
|
* server-side adversity module's degradation contract.
|
|
27
27
|
*/
|
|
28
28
|
import { Clock, Effect } from "effect";
|
|
29
|
+
import { MessageDeliveredNotificationDefinition } from "../../../schema/notifications.js";
|
|
30
|
+
import { brandNotificationFrame } from "../../../schema/internal-frames.js";
|
|
29
31
|
import { PropertyUnavailable, registerProperty } from "../registry.js";
|
|
30
32
|
import { acquireFixture, collectTagged, invariant, subscribeAll, } from "./_fixtures.js";
|
|
33
|
+
import { AgentsList } from "../../../schema/methods/auth.js";
|
|
31
34
|
const CATEGORY = "adversity";
|
|
32
35
|
const PROPERTY_BUDGET_MS = 10_000;
|
|
36
|
+
const TIMEOUT_ERROR_PREVIEW_CHARS = 200;
|
|
37
|
+
const PROPERTY_LATENCY_RESILIENCE_CLIENT = "latency-resilience-client";
|
|
38
|
+
const PROPERTY_TIMEOUT_SURFACE_CLIENT = "timeout-surface-client";
|
|
39
|
+
const PROPERTY_SLOW_CLOSE_CLEANUP_CLIENT = "slow-close-cleanup-client";
|
|
33
40
|
function unavailable(name, reason) {
|
|
34
41
|
return new PropertyUnavailable({ category: CATEGORY, name, reason });
|
|
35
42
|
}
|
|
@@ -40,33 +47,32 @@ function unavailable(name, reason) {
|
|
|
40
47
|
* discriminating against drops/dups.
|
|
41
48
|
*/
|
|
42
49
|
export function registerLatencyResilienceClient(ctx) {
|
|
43
|
-
registerProperty(ctx, CATEGORY,
|
|
50
|
+
registerProperty(ctx, CATEGORY, PROPERTY_LATENCY_RESILIENCE_CLIENT, "fan-out survives latency (Toxiproxy) or degrades to cardinality check", Effect.scoped(Effect.gen(function* () {
|
|
44
51
|
if (ctx.toxiproxy === null) {
|
|
45
|
-
return yield* Effect.fail(unavailable(
|
|
52
|
+
return yield* Effect.fail(unavailable(PROPERTY_LATENCY_RESILIENCE_CLIENT, "Toxiproxy not provisioned; client-side latency toxic unavailable in this run"));
|
|
46
53
|
}
|
|
47
|
-
const fx = yield* acquireFixture(ctx, CATEGORY,
|
|
54
|
+
const fx = yield* acquireFixture(ctx, CATEGORY, PROPERTY_LATENCY_RESILIENCE_CLIENT);
|
|
48
55
|
yield* subscribeAll(fx.handle);
|
|
49
|
-
const base = {
|
|
56
|
+
const base = brandNotificationFrame({
|
|
50
57
|
jsonrpc: "2.0",
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
};
|
|
58
|
+
method: MessageDeliveredNotificationDefinition.name,
|
|
59
|
+
params: {},
|
|
60
|
+
});
|
|
55
61
|
const N = 3;
|
|
56
62
|
const campaign = yield* fx.window.freshEmissionTag;
|
|
57
63
|
for (let i = 0; i < N; i++) {
|
|
58
|
-
yield* fx.window.
|
|
64
|
+
yield* fx.window.emitTaggedNotification({
|
|
59
65
|
connection: fx.connection,
|
|
60
66
|
base: {
|
|
61
67
|
...base,
|
|
62
|
-
|
|
68
|
+
params: { positionIndex: i },
|
|
63
69
|
},
|
|
64
70
|
emissionTag: campaign,
|
|
65
71
|
});
|
|
66
72
|
}
|
|
67
73
|
const observed = yield* collectTagged(fx.handle, (t) => t === campaign, { expected: N, budgetMs: PROPERTY_BUDGET_MS });
|
|
68
74
|
if (observed.length !== N) {
|
|
69
|
-
return yield* Effect.fail(invariant(CATEGORY,
|
|
75
|
+
return yield* Effect.fail(invariant(CATEGORY, PROPERTY_LATENCY_RESILIENCE_CLIENT, `expected ${N} under latency, got ${observed.length}`));
|
|
70
76
|
}
|
|
71
77
|
})));
|
|
72
78
|
}
|
|
@@ -99,8 +105,8 @@ export function registerResetPeerRecoveryClient(ctx) {
|
|
|
99
105
|
* - `RealClientRpcError.kind === "timeout"`
|
|
100
106
|
*/
|
|
101
107
|
export function registerTimeoutSurfaceClient(ctx) {
|
|
102
|
-
registerProperty(ctx, CATEGORY,
|
|
103
|
-
const fx = yield* acquireFixture(ctx, CATEGORY,
|
|
108
|
+
registerProperty(ctx, CATEGORY, PROPERTY_TIMEOUT_SURFACE_CLIENT, "never-responded RPC surfaces typed RpcTimeoutError on the real client", Effect.scoped(Effect.gen(function* () {
|
|
109
|
+
const fx = yield* acquireFixture(ctx, CATEGORY, PROPERTY_TIMEOUT_SURFACE_CLIENT);
|
|
104
110
|
// Do NOT start a responder — TestServer silently absorbs the
|
|
105
111
|
// request. The real client's internal timeout must fire.
|
|
106
112
|
//
|
|
@@ -109,24 +115,24 @@ export function registerTimeoutSurfaceClient(ctx) {
|
|
|
109
115
|
// exceeds the budget, this property reports unavailable rather
|
|
110
116
|
// than pretending to assert the client-internal deadline.
|
|
111
117
|
const start = yield* Clock.currentTimeMillis;
|
|
112
|
-
const outcome = yield* Effect.exit(fx.handle.call.call(
|
|
118
|
+
const outcome = yield* Effect.exit(fx.handle.call.call(AgentsList.name, {}).pipe(Effect.timeoutFail({
|
|
113
119
|
duration: `${PROPERTY_BUDGET_MS} millis`,
|
|
114
|
-
onTimeout: () => unavailable(
|
|
120
|
+
onTimeout: () => unavailable(PROPERTY_TIMEOUT_SURFACE_CLIENT, `client timeout > ${PROPERTY_BUDGET_MS}ms suite budget`),
|
|
115
121
|
})));
|
|
116
122
|
const elapsed = (yield* Clock.currentTimeMillis) - start;
|
|
117
123
|
if (outcome._tag === "Success") {
|
|
118
|
-
return yield* Effect.fail(invariant(CATEGORY,
|
|
124
|
+
return yield* Effect.fail(invariant(CATEGORY, PROPERTY_TIMEOUT_SURFACE_CLIENT, "RPC unexpectedly resolved without a response"));
|
|
119
125
|
}
|
|
120
126
|
// Walk the cause chain for a RealClientRpcError matching the
|
|
121
127
|
// typed-timeout contract. `PropertyUnavailable` from the suite
|
|
122
128
|
// budget is a different branch.
|
|
123
129
|
const causeStr = String(outcome.cause);
|
|
124
130
|
if (causeStr.includes("PropertyUnavailable")) {
|
|
125
|
-
return yield* Effect.fail(unavailable(
|
|
131
|
+
return yield* Effect.fail(unavailable(PROPERTY_TIMEOUT_SURFACE_CLIENT, `client timeout exceeded suite budget (${elapsed}ms)`));
|
|
126
132
|
}
|
|
127
133
|
if (!causeStr.includes("RpcTimeoutError") &&
|
|
128
134
|
!causeStr.includes("timeout")) {
|
|
129
|
-
return yield* Effect.fail(invariant(CATEGORY,
|
|
135
|
+
return yield* Effect.fail(invariant(CATEGORY, PROPERTY_TIMEOUT_SURFACE_CLIENT, `expected timeout-shape rejection, got: ${causeStr.slice(0, TIMEOUT_ERROR_PREVIEW_CHARS)}`));
|
|
130
136
|
}
|
|
131
137
|
})));
|
|
132
138
|
}
|
|
@@ -139,8 +145,8 @@ export function registerTimeoutSurfaceClient(ctx) {
|
|
|
139
145
|
* resolves within budget; Scope teardown completes.
|
|
140
146
|
*/
|
|
141
147
|
export function registerSlowCloseCleanupClient(ctx) {
|
|
142
|
-
registerProperty(ctx, CATEGORY,
|
|
143
|
-
const fx = yield* acquireFixture(ctx, CATEGORY,
|
|
148
|
+
registerProperty(ctx, CATEGORY, PROPERTY_SLOW_CLOSE_CLEANUP_CLIENT, "slow close completes; real client's closeSignal resolves and Scope releases", Effect.scoped(Effect.gen(function* () {
|
|
149
|
+
const fx = yield* acquireFixture(ctx, CATEGORY, PROPERTY_SLOW_CLOSE_CLEANUP_CLIENT);
|
|
144
150
|
// Initiate a close from the TestServer side.
|
|
145
151
|
yield* fx.connection
|
|
146
152
|
.close({ code: 1001, reason: "slow-close-test" })
|
|
@@ -149,12 +155,12 @@ export function registerSlowCloseCleanupClient(ctx) {
|
|
|
149
155
|
const closeBudget = 3_000;
|
|
150
156
|
const settled = yield* Effect.exit(fx.handle.closeSignal.pipe(Effect.timeoutFail({
|
|
151
157
|
duration: `${closeBudget} millis`,
|
|
152
|
-
onTimeout: () => invariant(CATEGORY,
|
|
158
|
+
onTimeout: () => invariant(CATEGORY, PROPERTY_SLOW_CLOSE_CLEANUP_CLIENT, `closeSignal did not resolve within ${closeBudget}ms`),
|
|
153
159
|
})));
|
|
154
160
|
if (settled._tag === "Failure") {
|
|
155
161
|
const causeStr = String(settled.cause);
|
|
156
162
|
if (causeStr.includes("ConformancePropertyInvariantViolation")) {
|
|
157
|
-
return yield* Effect.fail(invariant(CATEGORY,
|
|
163
|
+
return yield* Effect.fail(invariant(CATEGORY, PROPERTY_SLOW_CLOSE_CLEANUP_CLIENT, `closeSignal timed out (${closeBudget}ms budget)`));
|
|
158
164
|
}
|
|
159
165
|
}
|
|
160
166
|
})));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adversity.js","sourceRoot":"","sources":["../../../../src/testing/conformance/client/adversity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"adversity.js","sourceRoot":"","sources":["../../../../src/testing/conformance/client/adversity.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { ClientConformanceRunContext } from "./runner.js";
|
|
2
2
|
/**
|
|
3
|
-
* E2 client half — TestServer emits arbitrary `
|
|
3
|
+
* E2 client half — TestServer emits arbitrary `NotificationFrame`s across
|
|
4
4
|
* many shapes to a real client. Properties interleave with a tagged
|
|
5
5
|
* liveness probe and a task-boundary assertion.
|
|
6
6
|
*
|
|
7
7
|
* Predicate (both must hold):
|
|
8
8
|
* 1. No crash — real client stays `ready`; no spurious closeSignal.
|
|
9
|
-
* 2. Liveness probe — a valid tagged
|
|
9
|
+
* 2. Liveness probe — a valid tagged notification emitted post-fuzz surfaces.
|
|
10
10
|
*/
|
|
11
11
|
export declare function registerSchemaExhaustiveFuzzClient(ctx: ClientConformanceRunContext): void;
|
|
12
12
|
//# sourceMappingURL=boundary.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"boundary.d.ts","sourceRoot":"","sources":["../../../../src/testing/conformance/client/boundary.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"boundary.d.ts","sourceRoot":"","sources":["../../../../src/testing/conformance/client/boundary.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAC;AAc/D;;;;;;;;GAQG;AACH,wBAAgB,kCAAkC,CAChD,GAAG,EAAE,2BAA2B,GAC/B,IAAI,CAmEN"}
|
|
@@ -9,51 +9,54 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { Effect } from "effect";
|
|
11
11
|
import * as fc from "fast-check";
|
|
12
|
-
import {
|
|
12
|
+
import { MessageDeliveredNotificationDefinition } from "../../../schema/notifications.js";
|
|
13
|
+
import { brandNotificationFrame } from "../../../schema/internal-frames.js";
|
|
14
|
+
import { arbitraryNotificationFrame } from "../../arbitraries/frames.js";
|
|
13
15
|
import { registerProperty } from "../registry.js";
|
|
14
16
|
import { acquireFixture, collectTagged, invariant, subscribeAll, } from "./_fixtures.js";
|
|
15
17
|
const CATEGORY = "boundary";
|
|
16
18
|
const PROPERTY_BUDGET_MS = 12_000;
|
|
19
|
+
const DEFAULT_FUZZ_BURST_RUNS = 10;
|
|
20
|
+
const PROPERTY_SCHEMA_EXHAUSTIVE_FUZZ_CLIENT = "schema-exhaustive-fuzz-client";
|
|
17
21
|
/**
|
|
18
|
-
* E2 client half — TestServer emits arbitrary `
|
|
22
|
+
* E2 client half — TestServer emits arbitrary `NotificationFrame`s across
|
|
19
23
|
* many shapes to a real client. Properties interleave with a tagged
|
|
20
24
|
* liveness probe and a task-boundary assertion.
|
|
21
25
|
*
|
|
22
26
|
* Predicate (both must hold):
|
|
23
27
|
* 1. No crash — real client stays `ready`; no spurious closeSignal.
|
|
24
|
-
* 2. Liveness probe — a valid tagged
|
|
28
|
+
* 2. Liveness probe — a valid tagged notification emitted post-fuzz surfaces.
|
|
25
29
|
*/
|
|
26
30
|
export function registerSchemaExhaustiveFuzzClient(ctx) {
|
|
27
|
-
registerProperty(ctx, CATEGORY,
|
|
28
|
-
const fx = yield* acquireFixture(ctx, CATEGORY,
|
|
31
|
+
registerProperty(ctx, CATEGORY, PROPERTY_SCHEMA_EXHAUSTIVE_FUZZ_CLIENT, "real client absorbs arbitrary NotificationFrames; liveness and boundary hold", Effect.scoped(Effect.gen(function* () {
|
|
32
|
+
const fx = yield* acquireFixture(ctx, CATEGORY, PROPERTY_SCHEMA_EXHAUSTIVE_FUZZ_CLIENT);
|
|
29
33
|
yield* subscribeAll(fx.handle);
|
|
30
34
|
// Fuzz burst: default 10 frames; stress mode scales with
|
|
31
35
|
// CONFORMANCE_NUM_RUNS through ctx.opts.numRuns.
|
|
32
|
-
const fuzzRuns = ctx.opts.numRuns ??
|
|
33
|
-
const burst = fc.sample(
|
|
36
|
+
const fuzzRuns = ctx.opts.numRuns ?? DEFAULT_FUZZ_BURST_RUNS;
|
|
37
|
+
const burst = fc.sample(arbitraryNotificationFrame(), {
|
|
34
38
|
numRuns: fuzzRuns,
|
|
35
39
|
seed: ctx.seed,
|
|
36
40
|
});
|
|
37
41
|
for (const frame of burst) {
|
|
38
42
|
yield* fx.connection
|
|
39
|
-
.
|
|
43
|
+
.emitNotification(frame)
|
|
40
44
|
.pipe(Effect.orElseSucceed(() => undefined));
|
|
41
45
|
}
|
|
42
46
|
// (1) Real client still alive — closeSignal not fired.
|
|
43
47
|
const closeRace = yield* Effect.race(fx.handle.closeSignal.pipe(Effect.as("closed")), Effect.sleep("100 millis").pipe(Effect.as("alive")));
|
|
44
48
|
if (closeRace === "closed") {
|
|
45
|
-
return yield* Effect.fail(invariant(CATEGORY,
|
|
49
|
+
return yield* Effect.fail(invariant(CATEGORY, PROPERTY_SCHEMA_EXHAUSTIVE_FUZZ_CLIENT, "real client closed during fuzz burst"));
|
|
46
50
|
}
|
|
47
51
|
// (2) Liveness probe.
|
|
48
52
|
const tag = yield* fx.window.freshEmissionTag;
|
|
49
|
-
yield* fx.window.
|
|
53
|
+
yield* fx.window.emitTaggedNotification({
|
|
50
54
|
connection: fx.connection,
|
|
51
|
-
base: {
|
|
55
|
+
base: brandNotificationFrame({
|
|
52
56
|
jsonrpc: "2.0",
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
},
|
|
57
|
+
method: MessageDeliveredNotificationDefinition.name,
|
|
58
|
+
params: {},
|
|
59
|
+
}),
|
|
57
60
|
emissionTag: tag,
|
|
58
61
|
});
|
|
59
62
|
const observed = yield* collectTagged(fx.handle, (t) => t === tag, {
|
|
@@ -61,7 +64,7 @@ export function registerSchemaExhaustiveFuzzClient(ctx) {
|
|
|
61
64
|
budgetMs: PROPERTY_BUDGET_MS,
|
|
62
65
|
});
|
|
63
66
|
if (observed.length === 0) {
|
|
64
|
-
return yield* Effect.fail(invariant(CATEGORY,
|
|
67
|
+
return yield* Effect.fail(invariant(CATEGORY, PROPERTY_SCHEMA_EXHAUSTIVE_FUZZ_CLIENT, "liveness probe never surfaced after fuzz burst"));
|
|
65
68
|
}
|
|
66
69
|
})));
|
|
67
70
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"boundary.js","sourceRoot":"","sources":["../../../../src/testing/conformance/client/boundary.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AACjC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"boundary.js","sourceRoot":"","sources":["../../../../src/testing/conformance/client/boundary.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ClientConformanceRunContext } from "./runner.js";
|
|
2
2
|
/**
|
|
3
|
-
* C1 client-side — TestServer emits N fan-out `
|
|
3
|
+
* C1 client-side — TestServer emits N fan-out `NotificationFrame`s (one
|
|
4
4
|
* per conversation participant position) to a real client subscribed
|
|
5
5
|
* to the conversation. All N carry the same `emissionTag` campaign;
|
|
6
6
|
* each carries a per-position `positionIndex` in the payload.
|
|
@@ -15,20 +15,20 @@ import type { ClientConformanceRunContext } from "./runner.js";
|
|
|
15
15
|
*/
|
|
16
16
|
export declare function registerFanOutCardinalityClient(ctx: ClientConformanceRunContext): void;
|
|
17
17
|
/**
|
|
18
|
-
* C3 client-side — TestServer emits a single `
|
|
18
|
+
* C3 client-side — TestServer emits a single `NotificationFrame` whose
|
|
19
19
|
* payload contains a distinct byte-sequence token; real client's
|
|
20
20
|
* subscriber surfaces a frame whose raw bytes still contain that
|
|
21
21
|
* token.
|
|
22
22
|
*
|
|
23
|
-
* Predicate (strict): the raw-bytes view of the surfaced
|
|
23
|
+
* Predicate (strict): the raw-bytes view of the surfaced notification
|
|
24
24
|
* includes the emitted token byte-for-byte. A client that routes
|
|
25
25
|
* payloads through a lossy re-serialization (e.g., key-reorder JSON
|
|
26
26
|
* stringify) fails.
|
|
27
27
|
*/
|
|
28
28
|
export declare function registerPayloadOpacityClient(ctx: ClientConformanceRunContext): void;
|
|
29
29
|
/**
|
|
30
|
-
* C4 client half — TestServer emits N task-A
|
|
31
|
-
* and M task-B
|
|
30
|
+
* C4 client half — TestServer emits N task-A notifications (tagged campaignA)
|
|
31
|
+
* and M task-B notifications (tagged campaignB) to a real client subscribed
|
|
32
32
|
* with a `conversationId` filter set to task-A. The client's task-A
|
|
33
33
|
* subscriber surfaces zero campaignB events.
|
|
34
34
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delivery.d.ts","sourceRoot":"","sources":["../../../../src/testing/conformance/client/delivery.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"delivery.d.ts","sourceRoot":"","sources":["../../../../src/testing/conformance/client/delivery.ts"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAC;AAoB/D;;;;;;;;;;;;;GAaG;AACH,wBAAgB,+BAA+B,CAC7C,GAAG,EAAE,2BAA2B,GAC/B,IAAI,CAuEN;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE,2BAA2B,GAC/B,IAAI,CAuDN;AAED;;;;;;;GAOG;AACH,wBAAgB,mCAAmC,CACjD,GAAG,EAAE,2BAA2B,GAC/B,IAAI,CA+GN;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,CAC5C,GAAG,EAAE,2BAA2B,GAC/B,IAAI,CAoEN"}
|
|
@@ -20,21 +20,23 @@
|
|
|
20
20
|
*/
|
|
21
21
|
import { Effect } from "effect";
|
|
22
22
|
import * as fc from "fast-check";
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
23
|
+
import { notificationFrame } from "../../../helpers.js";
|
|
24
|
+
import { ConversationArchivedNotificationDefinition, ConversationUnarchivedNotificationDefinition, MessageDeliveredNotificationDefinition, } from "../../../schema/notifications.js";
|
|
25
|
+
import { agentId, conversationId as toConversationId, } from "../../../schema/primitives.js";
|
|
26
|
+
import { brandNotificationFrame } from "../../../schema/internal-frames.js";
|
|
27
|
+
import { arbitraryNotificationFrame } from "../../arbitraries/frames.js";
|
|
26
28
|
import { registerProperty } from "../registry.js";
|
|
27
|
-
import { acquireFixture, collectTagged, invariant, subscribeAll, } from "./_fixtures.js";
|
|
29
|
+
import { acquireFixture, collectTagged, invariant, notificationParamsRecord, subscribeAll, } from "./_fixtures.js";
|
|
28
30
|
const CATEGORY = "delivery";
|
|
29
31
|
const PROPERTY_BUDGET_MS = 8_000;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
const PROPERTY_FAN_OUT_CARDINALITY_CLIENT = "fan-out-cardinality-client";
|
|
33
|
+
const PROPERTY_PAYLOAD_OPACITY_CLIENT = "payload-opacity-client";
|
|
34
|
+
const PROPERTY_TASK_BOUNDARY_ISOLATION_CLIENT = "task-boundary-isolation-client";
|
|
35
|
+
const PAYLOAD_TOKEN_RADIX = 36;
|
|
36
|
+
const TASK_BOUNDARY_EMISSION_COUNT = 3;
|
|
37
|
+
const LIFECYCLE_OBSERVATION_COUNT = 2;
|
|
36
38
|
/**
|
|
37
|
-
* C1 client-side — TestServer emits N fan-out `
|
|
39
|
+
* C1 client-side — TestServer emits N fan-out `NotificationFrame`s (one
|
|
38
40
|
* per conversation participant position) to a real client subscribed
|
|
39
41
|
* to the conversation. All N carry the same `emissionTag` campaign;
|
|
40
42
|
* each carries a per-position `positionIndex` in the payload.
|
|
@@ -48,25 +50,25 @@ function eventDataRecord(data) {
|
|
|
48
50
|
* drops one, or reorders the sequence fails.
|
|
49
51
|
*/
|
|
50
52
|
export function registerFanOutCardinalityClient(ctx) {
|
|
51
|
-
registerProperty(ctx, CATEGORY,
|
|
52
|
-
const fx = yield* acquireFixture(ctx, CATEGORY,
|
|
53
|
+
registerProperty(ctx, CATEGORY, PROPERTY_FAN_OUT_CARDINALITY_CLIENT, "N fan-out notifications surface on real client in emission order, no drops, no dups", Effect.scoped(Effect.gen(function* () {
|
|
54
|
+
const fx = yield* acquireFixture(ctx, CATEGORY, PROPERTY_FAN_OUT_CARDINALITY_CLIENT);
|
|
53
55
|
yield* subscribeAll(fx.handle);
|
|
54
|
-
const base = fc.sample(
|
|
56
|
+
const base = fc.sample(arbitraryNotificationFrame(), {
|
|
55
57
|
numRuns: 1,
|
|
56
58
|
seed: ctx.seed,
|
|
57
59
|
})[0];
|
|
58
60
|
if (base === undefined) {
|
|
59
|
-
return yield* Effect.fail(invariant(CATEGORY,
|
|
61
|
+
return yield* Effect.fail(invariant(CATEGORY, PROPERTY_FAN_OUT_CARDINALITY_CLIENT, "sample failed"));
|
|
60
62
|
}
|
|
61
63
|
const N = 5;
|
|
62
64
|
const campaign = yield* fx.window.freshEmissionTag;
|
|
63
|
-
const
|
|
65
|
+
const baseParams = notificationParamsRecord(base.params);
|
|
64
66
|
for (let i = 0; i < N; i++) {
|
|
65
|
-
const positional = {
|
|
67
|
+
const positional = brandNotificationFrame({
|
|
66
68
|
...base,
|
|
67
|
-
|
|
68
|
-
};
|
|
69
|
-
yield* fx.window.
|
|
69
|
+
params: { ...baseParams, positionIndex: i },
|
|
70
|
+
});
|
|
71
|
+
yield* fx.window.emitTaggedNotification({
|
|
70
72
|
connection: fx.connection,
|
|
71
73
|
base: positional,
|
|
72
74
|
emissionTag: campaign,
|
|
@@ -74,41 +76,42 @@ export function registerFanOutCardinalityClient(ctx) {
|
|
|
74
76
|
}
|
|
75
77
|
const observed = yield* collectTagged(fx.handle, (t) => t === campaign, { expected: N, budgetMs: PROPERTY_BUDGET_MS });
|
|
76
78
|
if (observed.length !== N) {
|
|
77
|
-
return yield* Effect.fail(invariant(CATEGORY,
|
|
79
|
+
return yield* Effect.fail(invariant(CATEGORY, PROPERTY_FAN_OUT_CARDINALITY_CLIENT, `expected ${N} observations, got ${observed.length}`));
|
|
78
80
|
}
|
|
79
|
-
const indices = observed.map((o) => o.
|
|
81
|
+
const indices = observed.map((o) => o.params.positionIndex);
|
|
80
82
|
// Strict ordering check against `[0..N)`.
|
|
81
83
|
for (let i = 0; i < N; i++) {
|
|
82
84
|
if (indices[i] !== i) {
|
|
83
|
-
return yield* Effect.fail(invariant(CATEGORY,
|
|
85
|
+
return yield* Effect.fail(invariant(CATEGORY, PROPERTY_FAN_OUT_CARDINALITY_CLIENT, `order mismatch at slot ${i}: got ${String(indices[i])}`));
|
|
84
86
|
}
|
|
85
87
|
}
|
|
86
88
|
})));
|
|
87
89
|
}
|
|
88
90
|
/**
|
|
89
|
-
* C3 client-side — TestServer emits a single `
|
|
91
|
+
* C3 client-side — TestServer emits a single `NotificationFrame` whose
|
|
90
92
|
* payload contains a distinct byte-sequence token; real client's
|
|
91
93
|
* subscriber surfaces a frame whose raw bytes still contain that
|
|
92
94
|
* token.
|
|
93
95
|
*
|
|
94
|
-
* Predicate (strict): the raw-bytes view of the surfaced
|
|
96
|
+
* Predicate (strict): the raw-bytes view of the surfaced notification
|
|
95
97
|
* includes the emitted token byte-for-byte. A client that routes
|
|
96
98
|
* payloads through a lossy re-serialization (e.g., key-reorder JSON
|
|
97
99
|
* stringify) fails.
|
|
98
100
|
*/
|
|
99
101
|
export function registerPayloadOpacityClient(ctx) {
|
|
100
|
-
registerProperty(ctx, CATEGORY,
|
|
101
|
-
const fx = yield* acquireFixture(ctx, CATEGORY,
|
|
102
|
+
registerProperty(ctx, CATEGORY, PROPERTY_PAYLOAD_OPACITY_CLIENT, "opaque payload token round-trips byte-identical through the real client", Effect.scoped(Effect.gen(function* () {
|
|
103
|
+
const fx = yield* acquireFixture(ctx, CATEGORY, PROPERTY_PAYLOAD_OPACITY_CLIENT);
|
|
102
104
|
yield* subscribeAll(fx.handle);
|
|
103
|
-
const token = `opq-${ctx.seed.toString(
|
|
104
|
-
const base = {
|
|
105
|
+
const token = `opq-${ctx.seed.toString(PAYLOAD_TOKEN_RADIX)}-${Date.now().toString(PAYLOAD_TOKEN_RADIX)}`;
|
|
106
|
+
const base = brandNotificationFrame({
|
|
105
107
|
jsonrpc: "2.0",
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
method: MessageDeliveredNotificationDefinition.name,
|
|
109
|
+
params: {
|
|
110
|
+
opaqueToken: token,
|
|
111
|
+
},
|
|
112
|
+
});
|
|
110
113
|
const tag = yield* fx.window.freshEmissionTag;
|
|
111
|
-
yield* fx.window.
|
|
114
|
+
yield* fx.window.emitTaggedNotification({
|
|
112
115
|
connection: fx.connection,
|
|
113
116
|
base,
|
|
114
117
|
emissionTag: tag,
|
|
@@ -118,58 +121,58 @@ export function registerPayloadOpacityClient(ctx) {
|
|
|
118
121
|
budgetMs: PROPERTY_BUDGET_MS,
|
|
119
122
|
});
|
|
120
123
|
if (observed.length === 0) {
|
|
121
|
-
return yield* Effect.fail(invariant(CATEGORY,
|
|
124
|
+
return yield* Effect.fail(invariant(CATEGORY, PROPERTY_PAYLOAD_OPACITY_CLIENT, `token ${token} emission not surfaced by real client`));
|
|
122
125
|
}
|
|
123
126
|
const surfaced = observed[0];
|
|
124
127
|
const surfacedStr = new TextDecoder().decode(surfaced.raw);
|
|
125
128
|
if (!surfacedStr.includes(token)) {
|
|
126
|
-
return yield* Effect.fail(invariant(CATEGORY,
|
|
129
|
+
return yield* Effect.fail(invariant(CATEGORY, PROPERTY_PAYLOAD_OPACITY_CLIENT, `token ${token} not present byte-for-byte in surfaced raw frame`));
|
|
127
130
|
}
|
|
128
131
|
})));
|
|
129
132
|
}
|
|
130
133
|
/**
|
|
131
|
-
* C4 client half — TestServer emits N task-A
|
|
132
|
-
* and M task-B
|
|
134
|
+
* C4 client half — TestServer emits N task-A notifications (tagged campaignA)
|
|
135
|
+
* and M task-B notifications (tagged campaignB) to a real client subscribed
|
|
133
136
|
* with a `conversationId` filter set to task-A. The client's task-A
|
|
134
137
|
* subscriber surfaces zero campaignB events.
|
|
135
138
|
*
|
|
136
139
|
* Predicate: `observedCampaignB.length === 0`.
|
|
137
140
|
*/
|
|
138
141
|
export function registerTaskBoundaryIsolationClient(ctx) {
|
|
139
|
-
registerProperty(ctx, CATEGORY,
|
|
140
|
-
const fx = yield* acquireFixture(ctx, CATEGORY,
|
|
142
|
+
registerProperty(ctx, CATEGORY, PROPERTY_TASK_BOUNDARY_ISOLATION_CLIENT, "task-A subscriber does not surface task-B notifications — no leakage", Effect.scoped(Effect.gen(function* () {
|
|
143
|
+
const fx = yield* acquireFixture(ctx, CATEGORY, PROPERTY_TASK_BOUNDARY_ISOLATION_CLIENT);
|
|
141
144
|
yield* subscribeAll(fx.handle);
|
|
142
|
-
const
|
|
145
|
+
const baseNotification = fc.sample(arbitraryNotificationFrame(), {
|
|
143
146
|
numRuns: 1,
|
|
144
147
|
seed: ctx.seed,
|
|
145
148
|
})[0];
|
|
146
|
-
if (
|
|
147
|
-
return yield* Effect.fail(invariant(CATEGORY,
|
|
149
|
+
if (baseNotification === undefined) {
|
|
150
|
+
return yield* Effect.fail(invariant(CATEGORY, PROPERTY_TASK_BOUNDARY_ISOLATION_CLIENT, "sample failed"));
|
|
148
151
|
}
|
|
149
152
|
const campaignA = yield* fx.window.freshEmissionTag;
|
|
150
153
|
const campaignB = yield* fx.window.freshEmissionTag;
|
|
151
154
|
const taskA = `task-a-${ctx.seed}`;
|
|
152
155
|
const taskB = `task-b-${ctx.seed}`;
|
|
153
|
-
const
|
|
156
|
+
const baseParams = notificationParamsRecord(baseNotification.params);
|
|
154
157
|
// Emit task-A frames.
|
|
155
|
-
for (let i = 0; i <
|
|
156
|
-
yield* fx.window.
|
|
158
|
+
for (let i = 0; i < TASK_BOUNDARY_EMISSION_COUNT; i++) {
|
|
159
|
+
yield* fx.window.emitTaggedNotification({
|
|
157
160
|
connection: fx.connection,
|
|
158
161
|
base: {
|
|
159
|
-
...
|
|
160
|
-
|
|
162
|
+
...baseNotification,
|
|
163
|
+
params: { ...baseParams, conversationId: taskA },
|
|
161
164
|
},
|
|
162
165
|
emissionTag: campaignA,
|
|
163
166
|
});
|
|
164
167
|
}
|
|
165
168
|
// Emit task-B frames that must be filtered out by the client's
|
|
166
169
|
// subscription (via conversationId filter).
|
|
167
|
-
for (let i = 0; i <
|
|
168
|
-
yield* fx.window.
|
|
170
|
+
for (let i = 0; i < TASK_BOUNDARY_EMISSION_COUNT; i++) {
|
|
171
|
+
yield* fx.window.emitTaggedNotification({
|
|
169
172
|
connection: fx.connection,
|
|
170
173
|
base: {
|
|
171
|
-
...
|
|
172
|
-
|
|
174
|
+
...baseNotification,
|
|
175
|
+
params: { ...baseParams, conversationId: taskB },
|
|
173
176
|
},
|
|
174
177
|
emissionTag: campaignB,
|
|
175
178
|
});
|
|
@@ -185,24 +188,22 @@ export function registerTaskBoundaryIsolationClient(ctx) {
|
|
|
185
188
|
// filter, the property is vacuous; architect O5 notes channel
|
|
186
189
|
// packages re-export a bare WS client so no server-side filter is
|
|
187
190
|
// inserted. To keep the predicate discriminating, require that
|
|
188
|
-
// every task-A-emitted
|
|
191
|
+
// every task-A-emitted notification carry the task-A conversationId on
|
|
189
192
|
// the surfaced raw frame (positive-path witness) — a
|
|
190
193
|
// cross-wiring bug in the client would route a task-B payload
|
|
191
194
|
// under a task-A campaign tag and fail this predicate.
|
|
192
195
|
const taggedA = yield* collectTagged(fx.handle, (t) => t === campaignA, { expected: 3, budgetMs: 0 });
|
|
193
196
|
for (const obs of taggedA) {
|
|
194
|
-
const cid = obs.
|
|
195
|
-
?.conversationId;
|
|
197
|
+
const cid = obs.params.conversationId;
|
|
196
198
|
if (cid !== taskA) {
|
|
197
|
-
return yield* Effect.fail(invariant(CATEGORY,
|
|
199
|
+
return yield* Effect.fail(invariant(CATEGORY, PROPERTY_TASK_BOUNDARY_ISOLATION_CLIENT, `task-A emission surfaced with conversationId ${String(cid)}`));
|
|
198
200
|
}
|
|
199
201
|
}
|
|
200
202
|
const taggedB = yield* collectTagged(fx.handle, (t) => t === campaignB, { expected: 0, budgetMs: 0 });
|
|
201
203
|
for (const obs of taggedB) {
|
|
202
|
-
const cid = obs.
|
|
203
|
-
?.conversationId;
|
|
204
|
+
const cid = obs.params.conversationId;
|
|
204
205
|
if (cid !== taskB) {
|
|
205
|
-
return yield* Effect.fail(invariant(CATEGORY,
|
|
206
|
+
return yield* Effect.fail(invariant(CATEGORY, PROPERTY_TASK_BOUNDARY_ISOLATION_CLIENT, `task-B emission surfaced with conversationId ${String(cid)} (cross-wiring)`));
|
|
206
207
|
}
|
|
207
208
|
}
|
|
208
209
|
})));
|
|
@@ -214,38 +215,41 @@ export function registerTaskBoundaryIsolationClient(ctx) {
|
|
|
214
215
|
*/
|
|
215
216
|
export function registerArchiveLifecycleClient(ctx) {
|
|
216
217
|
const name = "archive-lifecycle-client";
|
|
217
|
-
registerProperty(ctx, CATEGORY, name, "archive/unarchive lifecycle
|
|
218
|
+
registerProperty(ctx, CATEGORY, name, "archive/unarchive lifecycle notifications surface on the real client in order", Effect.scoped(Effect.gen(function* () {
|
|
218
219
|
const fx = yield* acquireFixture(ctx, CATEGORY, name);
|
|
219
220
|
yield* subscribeAll(fx.handle);
|
|
220
|
-
const conversationId = "00000000-0000-4000-8000-000000000001";
|
|
221
|
+
const conversationId = toConversationId("00000000-0000-4000-8000-000000000001");
|
|
222
|
+
const by = agentId(fx.handle.agentId);
|
|
221
223
|
const tag = yield* fx.window.freshEmissionTag;
|
|
222
|
-
yield* fx.window.
|
|
224
|
+
yield* fx.window.emitTaggedNotification({
|
|
223
225
|
connection: fx.connection,
|
|
224
|
-
base:
|
|
226
|
+
base: notificationFrame(ConversationArchivedNotificationDefinition, {
|
|
225
227
|
conversationId,
|
|
226
228
|
archivedAt: new Date(0).toISOString(),
|
|
227
|
-
by
|
|
229
|
+
by,
|
|
228
230
|
}),
|
|
229
231
|
emissionTag: tag,
|
|
230
232
|
});
|
|
231
|
-
yield* fx.window.
|
|
233
|
+
yield* fx.window.emitTaggedNotification({
|
|
232
234
|
connection: fx.connection,
|
|
233
|
-
base:
|
|
235
|
+
base: notificationFrame(ConversationUnarchivedNotificationDefinition, {
|
|
234
236
|
conversationId,
|
|
235
|
-
by
|
|
237
|
+
by,
|
|
236
238
|
}),
|
|
237
239
|
emissionTag: tag,
|
|
238
240
|
});
|
|
239
241
|
const observed = yield* collectTagged(fx.handle, (t) => t === tag, {
|
|
240
|
-
expected:
|
|
242
|
+
expected: LIFECYCLE_OBSERVATION_COUNT,
|
|
241
243
|
budgetMs: PROPERTY_BUDGET_MS,
|
|
242
244
|
});
|
|
243
|
-
if (observed.length !==
|
|
244
|
-
return yield* Effect.fail(invariant(CATEGORY, name, `expected
|
|
245
|
+
if (observed.length !== LIFECYCLE_OBSERVATION_COUNT) {
|
|
246
|
+
return yield* Effect.fail(invariant(CATEGORY, name, `expected ${LIFECYCLE_OBSERVATION_COUNT} lifecycle observations, got ${observed.length}`));
|
|
245
247
|
}
|
|
246
|
-
if (observed[0]?.
|
|
247
|
-
|
|
248
|
-
|
|
248
|
+
if (observed[0]?.notificationName !==
|
|
249
|
+
ConversationArchivedNotificationDefinition.name ||
|
|
250
|
+
observed[1]?.notificationName !==
|
|
251
|
+
ConversationUnarchivedNotificationDefinition.name) {
|
|
252
|
+
return yield* Effect.fail(invariant(CATEGORY, name, `lifecycle order mismatch: ${observed.map((o) => o.notificationName).join(",")}`));
|
|
249
253
|
}
|
|
250
254
|
})));
|
|
251
255
|
}
|