model-runtime-lane-unit.test.mjs
429 lines 14.5 KB
Raw
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd feat(calendar): enforce agent context tiers in retrieval AP… Human minor ⚠ breaking 1 day ago
1 /**
2 * Tier 1 — UNIT: lib/model-runtime-lane.mjs
3 *
4 * Tests the smallest behavioural contracts of selectLane, isManagedLane, and
5 * enforceConsentPolicy in total isolation — pure functions, no network, no env.
6 *
7 * Reference: docs/COMPANION-APP-PHASE-1-ADAPTER-SEAM.md §1.3–1.5
8 */
9 import { describe, it } from 'node:test';
10 import assert from 'node:assert/strict';
11 import {
12 selectLane,
13 isManagedLane,
14 enforceConsentPolicy,
15 RUNTIME_LANES,
16 } from '../lib/model-runtime-lane.mjs';
17
18 // ── helpers ───────────────────────────────────────────────────────────────────
19
20 /** No capabilities — nothing available. */
21 const NO_CAPS = {};
22 /** All capabilities available. */
23 const ALL_CAPS = {
24 inBrowserAvailable: true,
25 companionAvailable: true,
26 selfHostedAvailable: true,
27 enterpriseAvailable: true,
28 openrouterKeyAvailable: true,
29 managedKeyAvailable: true,
30 };
31 /** Only managed cloud available. */
32 const MANAGED_ONLY = { managedKeyAvailable: true };
33 /** Only in-browser available. */
34 const INBROWSER_ONLY = { inBrowserAvailable: true };
35 /** Only companion available. */
36 const COMPANION_ONLY = { companionAvailable: true };
37 /** Only self-hosted available. */
38 const SELF_HOSTED_ONLY = { selfHostedAvailable: true };
39 /** Only enterprise available. */
40 const ENTERPRISE_ONLY = { enterpriseAvailable: true };
41 /** Only openrouter available. */
42 const OR_ONLY = { openrouterKeyAvailable: true };
43
44 const NO_PREFS = {};
45 const KEEP_ON_DEVICE = { keepOnDevice: true };
46 const ORG_PRIVACY = { orgPrivacyMode: true };
47
48 // ── RUNTIME_LANES export ─────────────────────────────────────────────────────
49
50 describe('RUNTIME_LANES', () => {
51 it('exports all six lane identifiers', () => {
52 const expected = ['local', 'self_hosted', 'enterprise', 'openrouter', 'direct_provider', 'disabled'];
53 assert.deepEqual([...RUNTIME_LANES].sort(), expected.sort());
54 });
55 });
56
57 // ── selectLane — individual user path ────────────────────────────────────────
58
59 describe('selectLane — individual user (no org privacy mode)', () => {
60 it('returns disabled when no capability is available', () => {
61 assert.equal(selectLane(NO_CAPS, NO_PREFS), 'disabled');
62 });
63
64 it('prefers local (in-browser) over all other lanes when inBrowserAvailable', () => {
65 assert.equal(selectLane(ALL_CAPS, NO_PREFS), 'local');
66 });
67
68 it('returns local when only inBrowserAvailable=true', () => {
69 assert.equal(selectLane(INBROWSER_ONLY, NO_PREFS), 'local');
70 });
71
72 it('returns local when only companionAvailable=true', () => {
73 assert.equal(selectLane(COMPANION_ONLY, NO_PREFS), 'local');
74 });
75
76 it('returns local when both inBrowserAvailable and companionAvailable are true', () => {
77 assert.equal(selectLane({ inBrowserAvailable: true, companionAvailable: true }, NO_PREFS), 'local');
78 });
79
80 it('returns self_hosted over enterprise when both available and no local', () => {
81 assert.equal(
82 selectLane({ selfHostedAvailable: true, enterpriseAvailable: true, managedKeyAvailable: true }, NO_PREFS),
83 'self_hosted',
84 );
85 });
86
87 it('returns enterprise when only enterprise available (no local/self-hosted)', () => {
88 assert.equal(selectLane(ENTERPRISE_ONLY, NO_PREFS), 'enterprise');
89 });
90
91 it('returns openrouter over managed when both available and no local', () => {
92 assert.equal(
93 selectLane({ openrouterKeyAvailable: true, managedKeyAvailable: true }, NO_PREFS),
94 'openrouter',
95 );
96 });
97
98 it('returns openrouter when only openrouterKeyAvailable', () => {
99 assert.equal(selectLane(OR_ONLY, NO_PREFS), 'openrouter');
100 });
101
102 it('returns direct_provider when only managedKeyAvailable', () => {
103 assert.equal(selectLane(MANAGED_ONLY, NO_PREFS), 'direct_provider');
104 });
105
106 it('local beats managed even when keepOnDevice=false', () => {
107 assert.equal(selectLane({ inBrowserAvailable: true, managedKeyAvailable: true }, NO_PREFS), 'local');
108 });
109 });
110
111 // ── selectLane — orgPrivacyMode ───────────────────────────────────────────────
112
113 describe('selectLane — orgPrivacyMode=true', () => {
114 it('never returns direct_provider in org privacy mode', () => {
115 const lane = selectLane({ managedKeyAvailable: true }, ORG_PRIVACY);
116 assert.notEqual(lane, 'direct_provider');
117 assert.equal(lane, 'disabled');
118 });
119
120 it('returns self_hosted first when available', () => {
121 assert.equal(selectLane(ALL_CAPS, ORG_PRIVACY), 'self_hosted');
122 });
123
124 it('falls to enterprise when no self-hosted', () => {
125 assert.equal(
126 selectLane({ enterpriseAvailable: true, openrouterKeyAvailable: true, managedKeyAvailable: true }, ORG_PRIVACY),
127 'enterprise',
128 );
129 });
130
131 it('prefers local (zero egress) over openrouter (third-party egress) in privacy mode', () => {
132 assert.equal(
133 selectLane({ openrouterKeyAvailable: true, inBrowserAvailable: true, managedKeyAvailable: true }, ORG_PRIVACY),
134 'local',
135 );
136 });
137
138 it('falls to openrouter when no org endpoints and no local compute', () => {
139 assert.equal(
140 selectLane({ openrouterKeyAvailable: true, managedKeyAvailable: true }, ORG_PRIVACY),
141 'openrouter',
142 );
143 });
144
145 it('falls to local (in-browser) when no org endpoints and no openrouter key', () => {
146 assert.equal(
147 selectLane({ inBrowserAvailable: true, managedKeyAvailable: true }, ORG_PRIVACY),
148 'local',
149 );
150 });
151
152 it('falls to local (companion) when no org endpoints, companion available', () => {
153 assert.equal(
154 selectLane({ companionAvailable: true, managedKeyAvailable: true }, ORG_PRIVACY),
155 'local',
156 );
157 });
158
159 it('returns disabled when truly nothing is available', () => {
160 assert.equal(selectLane(NO_CAPS, ORG_PRIVACY), 'disabled');
161 });
162 });
163
164 // ── selectLane — keepOnDevice ─────────────────────────────────────────────────
165
166 describe('selectLane — keepOnDevice', () => {
167 it('returns local immediately when inBrowserAvailable', () => {
168 assert.equal(selectLane(INBROWSER_ONLY, KEEP_ON_DEVICE), 'local');
169 });
170
171 it('returns local when companion available and keepOnDevice=true', () => {
172 assert.equal(selectLane(COMPANION_ONLY, KEEP_ON_DEVICE), 'local');
173 });
174
175 it('falls through to direct_provider when keepOnDevice=true but no local compute', () => {
176 // D2.2 fallback chain: in-browser → companion → managed-with-explicit-consent.
177 // enforceConsentPolicy then gates the managed lane for private data.
178 assert.equal(selectLane(MANAGED_ONLY, KEEP_ON_DEVICE), 'direct_provider');
179 });
180 });
181
182 // ── isManagedLane ─────────────────────────────────────────────────────────────
183
184 describe('isManagedLane', () => {
185 it('returns true only for direct_provider', () => {
186 assert.equal(isManagedLane('direct_provider'), true);
187 });
188
189 it('returns false for local', () => assert.equal(isManagedLane('local'), false));
190 it('returns false for self_hosted', () => assert.equal(isManagedLane('self_hosted'), false));
191 it('returns false for enterprise', () => assert.equal(isManagedLane('enterprise'), false));
192 it('returns false for openrouter', () => assert.equal(isManagedLane('openrouter'), false));
193 it('returns false for disabled', () => assert.equal(isManagedLane('disabled'), false));
194 it('returns false for unknown string', () => assert.equal(isManagedLane('unknown'), false));
195 });
196
197 // ── enforceConsentPolicy ──────────────────────────────────────────────────────
198
199 describe('enforceConsentPolicy — non-managed lanes always allow', () => {
200 const nonManaged = ['local', 'self_hosted', 'enterprise', 'openrouter', 'disabled'];
201 for (const lane of nonManaged) {
202 it(`allows ${lane} regardless of private data / delegate status`, () => {
203 assert.equal(
204 enforceConsentPolicy({
205 lane,
206 containsPrivateData: true,
207 consentId: undefined,
208 isDelegate: true,
209 delegatedManagedAllowed: false,
210 }),
211 'allow',
212 );
213 });
214 }
215 });
216
217 describe('enforceConsentPolicy — managed lane (direct_provider)', () => {
218 it('allows when data is not private and no delegation issue', () => {
219 assert.equal(
220 enforceConsentPolicy({
221 lane: 'direct_provider',
222 containsPrivateData: false,
223 consentId: undefined,
224 isDelegate: false,
225 delegatedManagedAllowed: false,
226 }),
227 'allow',
228 );
229 });
230
231 it('allows when private data + consentId + not a delegate', () => {
232 assert.equal(
233 enforceConsentPolicy({
234 lane: 'direct_provider',
235 containsPrivateData: true,
236 consentId: 'cid-123',
237 isDelegate: false,
238 delegatedManagedAllowed: false,
239 }),
240 'allow',
241 );
242 });
243
244 it('returns cloud_consent_required when private data without consentId', () => {
245 assert.equal(
246 enforceConsentPolicy({
247 lane: 'direct_provider',
248 containsPrivateData: true,
249 consentId: undefined,
250 isDelegate: false,
251 delegatedManagedAllowed: false,
252 }),
253 'cloud_consent_required',
254 );
255 });
256
257 it('returns lane_policy_denied when delegate without owner opt-in (beats consent check)', () => {
258 assert.equal(
259 enforceConsentPolicy({
260 lane: 'direct_provider',
261 containsPrivateData: true,
262 consentId: 'cid-999', // consentId present, but policy still denies
263 isDelegate: true,
264 delegatedManagedAllowed: false,
265 }),
266 'lane_policy_denied',
267 );
268 });
269
270 it('policy denial is NOT fixable by providing a consentId (order check)', () => {
271 // Even with a valid consentId, a delegate without owner opt-in is denied.
272 assert.equal(
273 enforceConsentPolicy({
274 lane: 'direct_provider',
275 containsPrivateData: false,
276 consentId: 'cid-abc',
277 isDelegate: true,
278 delegatedManagedAllowed: false,
279 }),
280 'lane_policy_denied',
281 );
282 });
283
284 it('allows delegate when owner has opted in (delegatedManagedAllowed=true)', () => {
285 assert.equal(
286 enforceConsentPolicy({
287 lane: 'direct_provider',
288 containsPrivateData: false,
289 consentId: undefined,
290 isDelegate: true,
291 delegatedManagedAllowed: true,
292 }),
293 'allow',
294 );
295 });
296
297 it('requires consent for delegate + private data + owner opt-in', () => {
298 assert.equal(
299 enforceConsentPolicy({
300 lane: 'direct_provider',
301 containsPrivateData: true,
302 consentId: undefined,
303 isDelegate: true,
304 delegatedManagedAllowed: true,
305 }),
306 'cloud_consent_required',
307 );
308 });
309
310 it('allows delegate + private data + owner opt-in + consentId provided', () => {
311 assert.equal(
312 enforceConsentPolicy({
313 lane: 'direct_provider',
314 containsPrivateData: true,
315 consentId: 'cid-xyz',
316 isDelegate: true,
317 delegatedManagedAllowed: true,
318 }),
319 'allow',
320 );
321 });
322 });
323
324 describe('enforceConsentPolicy — D1.3(2) delegated companion enrichment gate', () => {
325 it('denies delegate local-companion enrichment of owner partition by default (default OFF)', () => {
326 assert.equal(
327 enforceConsentPolicy({
328 lane: 'local',
329 containsPrivateData: true,
330 consentId: undefined,
331 isDelegate: true,
332 delegatedManagedAllowed: false,
333 enrichesDelegatedPartition: true,
334 // delegatedEnrichmentAllowed omitted → defaults to false (fail-closed)
335 }),
336 'lane_policy_denied',
337 );
338 });
339
340 it('allows delegate local enrichment once owner opts in (delegatedEnrichmentAllowed=true)', () => {
341 assert.equal(
342 enforceConsentPolicy({
343 lane: 'local',
344 containsPrivateData: true,
345 consentId: undefined,
346 isDelegate: true,
347 delegatedManagedAllowed: false,
348 enrichesDelegatedPartition: true,
349 delegatedEnrichmentAllowed: true,
350 }),
351 'allow',
352 );
353 });
354
355 it('denies delegate openrouter (BYO) enrichment of owner partition by default', () => {
356 assert.equal(
357 enforceConsentPolicy({
358 lane: 'openrouter',
359 containsPrivateData: true,
360 consentId: undefined,
361 isDelegate: true,
362 delegatedManagedAllowed: false,
363 enrichesDelegatedPartition: true,
364 delegatedEnrichmentAllowed: false,
365 }),
366 'lane_policy_denied',
367 );
368 });
369
370 it('does NOT gate a delegate local completion that is not an enrichment write-back', () => {
371 // enrichesDelegatedPartition defaults to false → a read-only/ephemeral completion is allowed
372 // (the delegate already has read scope per D1.3(1)); no artifact is written.
373 assert.equal(
374 enforceConsentPolicy({
375 lane: 'local',
376 containsPrivateData: true,
377 consentId: undefined,
378 isDelegate: true,
379 delegatedManagedAllowed: false,
380 }),
381 'allow',
382 );
383 });
384
385 it('does NOT gate a NON-delegate (owner) enriching their own partition locally', () => {
386 assert.equal(
387 enforceConsentPolicy({
388 lane: 'local',
389 containsPrivateData: true,
390 consentId: undefined,
391 isDelegate: false,
392 delegatedManagedAllowed: false,
393 enrichesDelegatedPartition: false,
394 delegatedEnrichmentAllowed: false,
395 }),
396 'allow',
397 );
398 });
399
400 it('does NOT apply the enrichment gate to org lanes (self_hosted) — org policy governs', () => {
401 assert.equal(
402 enforceConsentPolicy({
403 lane: 'self_hosted',
404 containsPrivateData: true,
405 consentId: undefined,
406 isDelegate: true,
407 delegatedManagedAllowed: false,
408 enrichesDelegatedPartition: true,
409 delegatedEnrichmentAllowed: false,
410 }),
411 'allow',
412 );
413 });
414
415 it('does NOT apply the enrichment gate to enterprise lanes — org policy governs', () => {
416 assert.equal(
417 enforceConsentPolicy({
418 lane: 'enterprise',
419 containsPrivateData: true,
420 consentId: undefined,
421 isDelegate: true,
422 delegatedManagedAllowed: false,
423 enrichesDelegatedPartition: true,
424 delegatedEnrichmentAllowed: false,
425 }),
426 'allow',
427 );
428 });
429 });
File History 2 commits
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd feat(calendar): enforce agent context tiers in retrieval AP… Human minor 1 day ago
sha256:9103f98c89257ed2b01c237cea895dabb3e85ea337dccb1161c175e4422355b6 docs: accept Calendar Events v0 spec with Phase 0 security … Human 1 day ago