@indigoai-us/hq-cloud 6.14.49 → 6.15.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/.github/workflows/unreleased-commits-nag.yml +256 -0
- package/dist/bin/sync-runner-watch-loop.d.ts +12 -0
- package/dist/bin/sync-runner-watch-loop.d.ts.map +1 -1
- package/dist/bin/sync-runner-watch-loop.js +153 -25
- package/dist/bin/sync-runner-watch-loop.js.map +1 -1
- package/dist/bin/sync-runner-watch-routes.d.ts +17 -0
- package/dist/bin/sync-runner-watch-routes.d.ts.map +1 -1
- package/dist/bin/sync-runner-watch-routes.js +11 -0
- package/dist/bin/sync-runner-watch-routes.js.map +1 -1
- package/dist/bin/sync-runner-watch-routes.test.d.ts +2 -0
- package/dist/bin/sync-runner-watch-routes.test.d.ts.map +1 -0
- package/dist/bin/sync-runner-watch-routes.test.js +51 -0
- package/dist/bin/sync-runner-watch-routes.test.js.map +1 -0
- package/dist/bin/sync-runner.d.ts +11 -12
- package/dist/bin/sync-runner.d.ts.map +1 -1
- package/dist/bin/sync-runner.js.map +1 -1
- package/dist/bin/sync-runner.test.js +173 -52
- package/dist/bin/sync-runner.test.js.map +1 -1
- package/dist/cli/reindex-knowledge.d.ts +7 -0
- package/dist/cli/reindex-knowledge.d.ts.map +1 -0
- package/dist/cli/reindex-knowledge.js +389 -0
- package/dist/cli/reindex-knowledge.js.map +1 -0
- package/dist/cli/reindex-knowledge.test.d.ts +12 -0
- package/dist/cli/reindex-knowledge.test.d.ts.map +1 -0
- package/dist/cli/reindex-knowledge.test.js +260 -0
- package/dist/cli/reindex-knowledge.test.js.map +1 -0
- package/dist/cli/reindex.d.ts.map +1 -1
- package/dist/cli/reindex.js +8 -0
- package/dist/cli/reindex.js.map +1 -1
- package/dist/cli/rescue-classify-ordering.test.js +8 -2
- package/dist/cli/rescue-classify-ordering.test.js.map +1 -1
- package/dist/cli/rescue-core.d.ts +85 -0
- package/dist/cli/rescue-core.d.ts.map +1 -1
- package/dist/cli/rescue-core.js +390 -1
- package/dist/cli/rescue-core.js.map +1 -1
- package/dist/cli/rescue-settings-reconcile.test.d.ts +2 -0
- package/dist/cli/rescue-settings-reconcile.test.d.ts.map +1 -0
- package/dist/cli/rescue-settings-reconcile.test.js +523 -0
- package/dist/cli/rescue-settings-reconcile.test.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/journal.d.ts.map +1 -1
- package/dist/journal.js +81 -51
- package/dist/journal.js.map +1 -1
- package/dist/journal.test.js +11 -19
- package/dist/journal.test.js.map +1 -1
- package/dist/sync/event-sync.d.ts +8 -25
- package/dist/sync/event-sync.d.ts.map +1 -1
- package/dist/sync/event-sync.js +23 -48
- package/dist/sync/event-sync.js.map +1 -1
- package/dist/sync/event-sync.test.js +6 -32
- package/dist/sync/event-sync.test.js.map +1 -1
- package/dist/sync/feature-flags.d.ts +20 -117
- package/dist/sync/feature-flags.d.ts.map +1 -1
- package/dist/sync/feature-flags.js +24 -147
- package/dist/sync/feature-flags.js.map +1 -1
- package/dist/sync/feature-flags.test.js +15 -61
- package/dist/sync/feature-flags.test.js.map +1 -1
- package/dist/sync/index.d.ts +1 -1
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +1 -1
- package/dist/sync/index.js.map +1 -1
- package/dist/sync/lease-client.d.ts +67 -0
- package/dist/sync/lease-client.d.ts.map +1 -0
- package/dist/sync/lease-client.js +158 -0
- package/dist/sync/lease-client.js.map +1 -0
- package/dist/sync/lease-client.test.d.ts +2 -0
- package/dist/sync/lease-client.test.d.ts.map +1 -0
- package/dist/sync/lease-client.test.js +119 -0
- package/dist/sync/lease-client.test.js.map +1 -0
- package/dist/sync/push-receiver.d.ts +8 -19
- package/dist/sync/push-receiver.d.ts.map +1 -1
- package/dist/sync/push-receiver.js +6 -50
- package/dist/sync/push-receiver.js.map +1 -1
- package/dist/sync/push-receiver.test.js +6 -57
- package/dist/sync/push-receiver.test.js.map +1 -1
- package/dist/sync/realtime-rollout.d.ts +85 -0
- package/dist/sync/realtime-rollout.d.ts.map +1 -0
- package/dist/sync/realtime-rollout.js +206 -0
- package/dist/sync/realtime-rollout.js.map +1 -0
- package/dist/sync/realtime-rollout.test.d.ts +2 -0
- package/dist/sync/realtime-rollout.test.d.ts.map +1 -0
- package/dist/sync/realtime-rollout.test.js +71 -0
- package/dist/sync/realtime-rollout.test.js.map +1 -0
- package/dist/sync/state-store.d.ts +40 -0
- package/dist/sync/state-store.d.ts.map +1 -0
- package/dist/sync/state-store.js +680 -0
- package/dist/sync/state-store.js.map +1 -0
- package/dist/sync/state-store.test.d.ts +2 -0
- package/dist/sync/state-store.test.d.ts.map +1 -0
- package/dist/sync/state-store.test.js +159 -0
- package/dist/sync/state-store.test.js.map +1 -0
- package/dist/types.d.ts +12 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/bin/sync-runner-watch-loop.ts +176 -32
- package/src/bin/sync-runner-watch-routes.test.ts +71 -0
- package/src/bin/sync-runner-watch-routes.ts +27 -0
- package/src/bin/sync-runner.test.ts +188 -53
- package/src/bin/sync-runner.ts +11 -12
- package/src/cli/reindex-knowledge.test.ts +307 -0
- package/src/cli/reindex-knowledge.ts +450 -0
- package/src/cli/reindex.ts +9 -0
- package/src/cli/rescue-classify-ordering.test.ts +9 -3
- package/src/cli/rescue-core.ts +459 -1
- package/src/cli/rescue-settings-reconcile.test.ts +637 -0
- package/src/index.ts +2 -4
- package/src/journal.test.ts +19 -21
- package/src/journal.ts +87 -54
- package/src/sync/event-sync.test.ts +9 -44
- package/src/sync/event-sync.ts +26 -52
- package/src/sync/feature-flags.test.ts +22 -72
- package/src/sync/feature-flags.ts +32 -199
- package/src/sync/index.ts +2 -4
- package/src/sync/lease-client.test.ts +128 -0
- package/src/sync/lease-client.ts +207 -0
- package/src/sync/push-receiver.test.ts +6 -58
- package/src/sync/push-receiver.ts +9 -74
- package/src/sync/realtime-rollout.test.ts +86 -0
- package/src/sync/realtime-rollout.ts +262 -0
- package/src/sync/state-store.test.ts +194 -0
- package/src/sync/state-store.ts +727 -0
- package/src/types.ts +14 -0
- package/test/e2e/sync/transient-company-leg.test.ts +8 -4
|
@@ -13,7 +13,7 @@ import * as path from "path";
|
|
|
13
13
|
import { runRunner, runRunnerWithLoop, resolveDeletePolicy, resolveSkipPersonal, resolveSkipCompanies, selectObjectIOFactory, routeChangeToTarget, buildTargetedPushArgv, resolvePullScope, readPinnedPrefixes, SESSIONS_SCOPE_PREFIX, defaultCollectTelemetry, AUTH_REQUIRED_PASS_EXIT, } from "./sync-runner.js";
|
|
14
14
|
import { PresignObjectIO, S3SdkObjectIO } from "../object-io.js";
|
|
15
15
|
import { FakeClock, } from "../watcher.js";
|
|
16
|
-
import { adaptivePollMs, journalEntriesForWatcherDelete, shouldQuarantineWatcherDeleteIntents, } from "./sync-runner-watch-loop.js";
|
|
16
|
+
import { adaptivePollMs, advanceMonotonicDeadline, journalEntriesForWatcherDelete, shouldQuarantineWatcherDeleteIntents, } from "./sync-runner-watch-loop.js";
|
|
17
17
|
import { PERSONAL_VAULT_JOURNAL_SLUG, readJournal, writeJournal, } from "../journal.js";
|
|
18
18
|
import { HQ_CLOUD_VERSION } from "../version.js";
|
|
19
19
|
import { lockPathFor, OPERATION_LOCKED_EXIT } from "../operation-lock.js";
|
|
@@ -3383,6 +3383,21 @@ describe("adaptivePollMs (whole-box CPU backoff)", () => {
|
|
|
3383
3383
|
expect(adaptivePollMs(4, 4, 30_000, 300_000)).toBe(300_000);
|
|
3384
3384
|
});
|
|
3385
3385
|
});
|
|
3386
|
+
describe("advanceMonotonicDeadline", () => {
|
|
3387
|
+
it("runs one immediate catch-up after missed slots, then resumes at the first future slot", () => {
|
|
3388
|
+
// A pass due at t=0 took through t=1,800 with a 600 ms cadence. It does
|
|
3389
|
+
// not replay the three missed slots; exactly one catch-up is immediate and
|
|
3390
|
+
// the following regular deadline is t=2,400.
|
|
3391
|
+
expect(advanceMonotonicDeadline(0, 1_800, 600)).toEqual({
|
|
3392
|
+
nextDueAt: 2_400,
|
|
3393
|
+
delayMs: 0,
|
|
3394
|
+
});
|
|
3395
|
+
expect(advanceMonotonicDeadline(2_400, 1_800, 600)).toEqual({
|
|
3396
|
+
nextDueAt: 3_000,
|
|
3397
|
+
delayMs: 1_200,
|
|
3398
|
+
});
|
|
3399
|
+
});
|
|
3400
|
+
});
|
|
3386
3401
|
describe("runRunnerWithLoop — adaptive poll interval (no --poll-remote-ms)", () => {
|
|
3387
3402
|
// Capture the ms handed to sleep on the FIRST poll, then hang so the loop
|
|
3388
3403
|
// parks until shutdown (mirrors the never-resolving sleep the other loop
|
|
@@ -3397,55 +3412,78 @@ describe("runRunnerWithLoop — adaptive poll interval (no --poll-remote-ms)", (
|
|
|
3397
3412
|
},
|
|
3398
3413
|
};
|
|
3399
3414
|
}
|
|
3400
|
-
it("
|
|
3415
|
+
it("waits only the bounded remainder of the idle cadence slot", async () => {
|
|
3401
3416
|
const { calls, sleep } = makeCapturingSleep();
|
|
3417
|
+
const monotonicNow = vi
|
|
3418
|
+
.fn()
|
|
3419
|
+
.mockReturnValueOnce(1_000)
|
|
3420
|
+
// A 20 ms pass consumes part of this 60 s slot. The scheduler must
|
|
3421
|
+
// retain the original deadline rather than slewing a new 60 s wait.
|
|
3422
|
+
.mockReturnValueOnce(1_020);
|
|
3402
3423
|
let triggerShutdown = () => { };
|
|
3403
3424
|
const loop = runRunnerWithLoop(["--companies", "--watch", "--hq-root", "/tmp/hq"], {
|
|
3404
3425
|
runPass: vi.fn().mockResolvedValue(0),
|
|
3405
3426
|
sleep,
|
|
3406
3427
|
sampleLoadAvg: () => 0,
|
|
3428
|
+
monotonicNow,
|
|
3407
3429
|
onShutdownSignal: (handler) => {
|
|
3408
3430
|
triggerShutdown = handler;
|
|
3409
3431
|
return () => { };
|
|
3410
3432
|
},
|
|
3411
3433
|
});
|
|
3412
3434
|
await flushLoopMicrotasks();
|
|
3413
|
-
expect(calls[0]).toBe(
|
|
3435
|
+
expect(calls[0]).toBe(59_980);
|
|
3436
|
+
expect(calls[0]).toBeGreaterThan(0);
|
|
3437
|
+
expect(calls[0]).toBeLessThanOrEqual(60_000);
|
|
3414
3438
|
triggerShutdown();
|
|
3415
3439
|
await loop;
|
|
3416
3440
|
});
|
|
3417
|
-
it("
|
|
3441
|
+
it("waits only the bounded remainder of the saturated cadence slot", async () => {
|
|
3418
3442
|
const { calls, sleep } = makeCapturingSleep();
|
|
3443
|
+
const monotonicNow = vi
|
|
3444
|
+
.fn()
|
|
3445
|
+
.mockReturnValueOnce(1_000)
|
|
3446
|
+
.mockReturnValueOnce(1_075);
|
|
3419
3447
|
let triggerShutdown = () => { };
|
|
3420
3448
|
const loop = runRunnerWithLoop(["--companies", "--watch", "--hq-root", "/tmp/hq"], {
|
|
3421
3449
|
runPass: vi.fn().mockResolvedValue(0),
|
|
3422
3450
|
sleep,
|
|
3423
3451
|
sampleLoadAvg: () => 1_000, // far above any core count → clamps to ceil
|
|
3452
|
+
monotonicNow,
|
|
3424
3453
|
onShutdownSignal: (handler) => {
|
|
3425
3454
|
triggerShutdown = handler;
|
|
3426
3455
|
return () => { };
|
|
3427
3456
|
},
|
|
3428
3457
|
});
|
|
3429
3458
|
await flushLoopMicrotasks();
|
|
3430
|
-
expect(calls[0]).toBe(
|
|
3459
|
+
expect(calls[0]).toBe(599_925);
|
|
3460
|
+
expect(calls[0]).toBeGreaterThan(0);
|
|
3461
|
+
expect(calls[0]).toBeLessThanOrEqual(600_000);
|
|
3431
3462
|
triggerShutdown();
|
|
3432
3463
|
await loop;
|
|
3433
3464
|
});
|
|
3434
|
-
it("
|
|
3465
|
+
it("uses the bounded remainder of an explicit cadence slot and ignores load", async () => {
|
|
3435
3466
|
const { calls, sleep } = makeCapturingSleep();
|
|
3436
3467
|
const sampleLoadAvg = vi.fn(() => 1_000);
|
|
3468
|
+
const monotonicNow = vi
|
|
3469
|
+
.fn()
|
|
3470
|
+
.mockReturnValueOnce(1_000)
|
|
3471
|
+
.mockReturnValueOnce(1_123);
|
|
3437
3472
|
let triggerShutdown = () => { };
|
|
3438
3473
|
const loop = runRunnerWithLoop(["--companies", "--watch", "--poll-remote-ms", "123456", "--hq-root", "/tmp/hq"], {
|
|
3439
3474
|
runPass: vi.fn().mockResolvedValue(0),
|
|
3440
3475
|
sleep,
|
|
3441
3476
|
sampleLoadAvg,
|
|
3477
|
+
monotonicNow,
|
|
3442
3478
|
onShutdownSignal: (handler) => {
|
|
3443
3479
|
triggerShutdown = handler;
|
|
3444
3480
|
return () => { };
|
|
3445
3481
|
},
|
|
3446
3482
|
});
|
|
3447
3483
|
await flushLoopMicrotasks();
|
|
3448
|
-
expect(calls[0]).toBe(
|
|
3484
|
+
expect(calls[0]).toBe(123_333);
|
|
3485
|
+
expect(calls[0]).toBeGreaterThan(0);
|
|
3486
|
+
expect(calls[0]).toBeLessThanOrEqual(123_456);
|
|
3449
3487
|
expect(sampleLoadAvg).not.toHaveBeenCalled();
|
|
3450
3488
|
triggerShutdown();
|
|
3451
3489
|
await loop;
|
|
@@ -3476,35 +3514,45 @@ describe("runRunnerWithLoop — event-push wiring", () => {
|
|
|
3476
3514
|
expect(runPass).toHaveBeenCalled();
|
|
3477
3515
|
expect(watcher.disposed).toBe(true);
|
|
3478
3516
|
});
|
|
3479
|
-
it("
|
|
3517
|
+
it("U07: watcher interrupts a fake 600 s wait and drains through the guarded scheduler", async () => {
|
|
3480
3518
|
const watcher = makeWatcherStub();
|
|
3481
3519
|
const clock = new FakeClock();
|
|
3482
3520
|
let triggerShutdown = () => { };
|
|
3483
3521
|
const runPass = vi.fn().mockResolvedValue(0);
|
|
3484
|
-
const
|
|
3522
|
+
const sleepCalls = [];
|
|
3523
|
+
const loop = runRunnerWithLoop([
|
|
3524
|
+
"--companies",
|
|
3525
|
+
"--watch",
|
|
3526
|
+
"--event-push",
|
|
3527
|
+
"--poll-remote-ms",
|
|
3528
|
+
"600000",
|
|
3529
|
+
"--hq-root",
|
|
3530
|
+
"/tmp/hq",
|
|
3531
|
+
], {
|
|
3485
3532
|
runPass,
|
|
3486
3533
|
clock,
|
|
3487
3534
|
createWatcher: () => watcher,
|
|
3488
|
-
|
|
3535
|
+
monotonicNow: () => 0,
|
|
3536
|
+
sleep: (ms) => {
|
|
3537
|
+
sleepCalls.push(ms);
|
|
3538
|
+
return new Promise(() => { });
|
|
3539
|
+
},
|
|
3489
3540
|
onShutdownSignal: (handler) => {
|
|
3490
3541
|
triggerShutdown = handler;
|
|
3491
3542
|
return () => { };
|
|
3492
3543
|
},
|
|
3493
3544
|
});
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
// Fire a change for an indigo file; advance the (zero) driver window.
|
|
3545
|
+
await flushLoopMicrotasks();
|
|
3546
|
+
expect(sleepCalls).toEqual([600_000]);
|
|
3547
|
+
// The fake 600-second timer remains unresolved. The watcher wake must
|
|
3548
|
+
// nonetheless start the next pass immediately through runGuarded.
|
|
3499
3549
|
watcher.emit("companies/indigo/board.json");
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
await Promise.resolve();
|
|
3503
|
-
expect(runPass).not.toHaveBeenCalled();
|
|
3550
|
+
await flushLoopMicrotasks();
|
|
3551
|
+
expect(runPass).toHaveBeenCalledTimes(2);
|
|
3504
3552
|
triggerShutdown();
|
|
3505
3553
|
await loop;
|
|
3506
3554
|
});
|
|
3507
|
-
it("bare signal
|
|
3555
|
+
it("bare watcher signal interrupts the wait and schedules a full reconcile", async () => {
|
|
3508
3556
|
const watcher = makeWatcherStub();
|
|
3509
3557
|
const clock = new FakeClock();
|
|
3510
3558
|
let triggerShutdown = () => { };
|
|
@@ -3524,9 +3572,8 @@ describe("runRunnerWithLoop — event-push wiring", () => {
|
|
|
3524
3572
|
runPass.mockClear();
|
|
3525
3573
|
watcher.emit(); // no path — TreeWatcher's bare signal shape
|
|
3526
3574
|
clock.advance(0);
|
|
3527
|
-
await
|
|
3528
|
-
|
|
3529
|
-
expect(runPass).not.toHaveBeenCalled();
|
|
3575
|
+
await flushLoopMicrotasks();
|
|
3576
|
+
expect(runPass).toHaveBeenCalledTimes(1);
|
|
3530
3577
|
triggerShutdown();
|
|
3531
3578
|
await loop;
|
|
3532
3579
|
});
|
|
@@ -3584,7 +3631,54 @@ describe("runRunnerWithLoop — event-push wiring", () => {
|
|
|
3584
3631
|
triggerShutdown();
|
|
3585
3632
|
await loop;
|
|
3586
3633
|
});
|
|
3587
|
-
it("
|
|
3634
|
+
it("U07: retains a watcher event raised before the next wait registers, without guarded overlap", async () => {
|
|
3635
|
+
const watcher = makeWatcherStub();
|
|
3636
|
+
let triggerShutdown = () => { };
|
|
3637
|
+
let releaseFirstPass = () => { };
|
|
3638
|
+
const firstPassGate = new Promise((resolve) => {
|
|
3639
|
+
releaseFirstPass = resolve;
|
|
3640
|
+
});
|
|
3641
|
+
const spans = [];
|
|
3642
|
+
let passes = 0;
|
|
3643
|
+
const runPass = vi.fn().mockImplementation(async () => {
|
|
3644
|
+
const span = { enteredAt: process.hrtime.bigint(), exitedAt: 0n };
|
|
3645
|
+
spans.push(span);
|
|
3646
|
+
passes += 1;
|
|
3647
|
+
if (passes === 1)
|
|
3648
|
+
await firstPassGate;
|
|
3649
|
+
span.exitedAt = process.hrtime.bigint();
|
|
3650
|
+
return 0;
|
|
3651
|
+
});
|
|
3652
|
+
const loop = runRunnerWithLoop([
|
|
3653
|
+
"--companies",
|
|
3654
|
+
"--watch",
|
|
3655
|
+
"--event-push",
|
|
3656
|
+
"--poll-remote-ms",
|
|
3657
|
+
"600000",
|
|
3658
|
+
"--hq-root",
|
|
3659
|
+
"/tmp/hq",
|
|
3660
|
+
], {
|
|
3661
|
+
runPass,
|
|
3662
|
+
createWatcher: () => watcher,
|
|
3663
|
+
sleep: () => new Promise(() => { }),
|
|
3664
|
+
onShutdownSignal: (handler) => {
|
|
3665
|
+
triggerShutdown = handler;
|
|
3666
|
+
return () => { };
|
|
3667
|
+
},
|
|
3668
|
+
});
|
|
3669
|
+
await flushLoopMicrotasks();
|
|
3670
|
+
expect(spans).toHaveLength(1);
|
|
3671
|
+
// This event arrives while the initial guarded pass owns the scheduler,
|
|
3672
|
+
// before its next sleep/wait has been registered.
|
|
3673
|
+
watcher.emit("companies/indigo/pre-registration.md");
|
|
3674
|
+
releaseFirstPass();
|
|
3675
|
+
await flushLoopMicrotasks();
|
|
3676
|
+
expect(spans).toHaveLength(2);
|
|
3677
|
+
expect(spans[1].enteredAt >= spans[0].exitedAt).toBe(true);
|
|
3678
|
+
triggerShutdown();
|
|
3679
|
+
await loop;
|
|
3680
|
+
});
|
|
3681
|
+
it("F05: guard-held watcher changes stay accumulated, then drain immediately after the guarded pass", async () => {
|
|
3588
3682
|
const watcher = makeWatcherStub();
|
|
3589
3683
|
const clock = new FakeClock();
|
|
3590
3684
|
let triggerShutdown = () => { };
|
|
@@ -3665,8 +3759,7 @@ describe("runRunnerWithLoop — event-push wiring", () => {
|
|
|
3665
3759
|
expect(passes).toBe(1);
|
|
3666
3760
|
// Step the poll interval → a second poll pass runs (safety net cadence).
|
|
3667
3761
|
poll.resolve?.();
|
|
3668
|
-
await
|
|
3669
|
-
await Promise.resolve();
|
|
3762
|
+
await flushLoopMicrotasks();
|
|
3670
3763
|
expect(passes).toBeGreaterThanOrEqual(2);
|
|
3671
3764
|
triggerShutdown();
|
|
3672
3765
|
// Release any pending sleep so the loop can observe shutdown and exit.
|
|
@@ -3930,7 +4023,7 @@ describe("runRunnerWithLoop — accumulate-and-drain scoped push", () => {
|
|
|
3930
4023
|
shutdown: () => triggerShutdown(),
|
|
3931
4024
|
};
|
|
3932
4025
|
}
|
|
3933
|
-
it("startup runs one full reconcile, then watcher edits
|
|
4026
|
+
it("startup runs one full reconcile, then watcher edits interrupt the pending wait", async () => {
|
|
3934
4027
|
const runPass = vi.fn().mockResolvedValue(0);
|
|
3935
4028
|
const { loop, watcher, tick, shutdown } = startDrainLoop(runPass);
|
|
3936
4029
|
await flushLoopMicrotasks();
|
|
@@ -3938,9 +4031,6 @@ describe("runRunnerWithLoop — accumulate-and-drain scoped push", () => {
|
|
|
3938
4031
|
runPass.mockClear();
|
|
3939
4032
|
watcher.emit("companies/indigo/knowledge/a.md");
|
|
3940
4033
|
await flushLoopMicrotasks();
|
|
3941
|
-
expect(runPass).not.toHaveBeenCalled();
|
|
3942
|
-
tick();
|
|
3943
|
-
await flushLoopMicrotasks();
|
|
3944
4034
|
expect(runPass.mock.calls.map((call) => call[0])).toEqual([
|
|
3945
4035
|
[
|
|
3946
4036
|
"--company",
|
|
@@ -3986,6 +4076,24 @@ describe("runRunnerWithLoop — accumulate-and-drain scoped push", () => {
|
|
|
3986
4076
|
tick();
|
|
3987
4077
|
await loop;
|
|
3988
4078
|
});
|
|
4079
|
+
it("U08: --companies --skip-personal drops a personal watcher edit before it queues work", async () => {
|
|
4080
|
+
const runPass = vi.fn().mockResolvedValue(0);
|
|
4081
|
+
const { loop, watcher, tick, shutdown } = startDrainLoopWithArgv(runPass, [
|
|
4082
|
+
...watchArgv,
|
|
4083
|
+
"--skip-personal",
|
|
4084
|
+
]);
|
|
4085
|
+
await flushLoopMicrotasks();
|
|
4086
|
+
runPass.mockClear();
|
|
4087
|
+
watcher.emit("knowledge/private.md");
|
|
4088
|
+
await flushLoopMicrotasks();
|
|
4089
|
+
// The watcher event is rejected before the scheduler is interrupted:
|
|
4090
|
+
// no route-specific pass is queued and therefore no runner/network work
|
|
4091
|
+
// is reachable for a disabled personal scope.
|
|
4092
|
+
expect(runPass).not.toHaveBeenCalled();
|
|
4093
|
+
shutdown();
|
|
4094
|
+
tick();
|
|
4095
|
+
await loop;
|
|
4096
|
+
});
|
|
3989
4097
|
it("drains an unlink path through the same scoped tombstone push path", async () => {
|
|
3990
4098
|
const runPass = vi.fn().mockResolvedValue(0);
|
|
3991
4099
|
const { loop, watcher, tick, shutdown } = startDrainLoop(runPass);
|
|
@@ -4989,7 +5097,7 @@ function makeBatchWatcherStub() {
|
|
|
4989
5097
|
};
|
|
4990
5098
|
}
|
|
4991
5099
|
describe("runRunnerWithLoop — Phase 3 event-sync wiring (US-017/018/019)", () => {
|
|
4992
|
-
const
|
|
5100
|
+
const EMAIL_CLAIMS = { email: "hassaan@getindigo.ai" };
|
|
4993
5101
|
const UNENROLLED_CLAIMS = { email: "someone@getindigo.ai" };
|
|
4994
5102
|
function runLoop(opts) {
|
|
4995
5103
|
const watcher = opts.watcher ?? makeBatchWatcherStub();
|
|
@@ -5036,7 +5144,7 @@ describe("runRunnerWithLoop — Phase 3 event-sync wiring (US-017/018/019)", ()
|
|
|
5036
5144
|
for (let i = 0; i < n; i++)
|
|
5037
5145
|
await Promise.resolve();
|
|
5038
5146
|
}
|
|
5039
|
-
it("
|
|
5147
|
+
it("does not use an unenrolled email as a client-side denial", async () => {
|
|
5040
5148
|
const startEventSync = vi.fn().mockResolvedValue(null);
|
|
5041
5149
|
const { loop, shutdown } = runLoop({
|
|
5042
5150
|
claims: UNENROLLED_CLAIMS,
|
|
@@ -5045,20 +5153,20 @@ describe("runRunnerWithLoop — Phase 3 event-sync wiring (US-017/018/019)", ()
|
|
|
5045
5153
|
await microtasks();
|
|
5046
5154
|
shutdown();
|
|
5047
5155
|
await loop;
|
|
5048
|
-
expect(startEventSync).
|
|
5156
|
+
expect(startEventSync).toHaveBeenCalledTimes(1);
|
|
5049
5157
|
});
|
|
5050
|
-
it("
|
|
5158
|
+
it("does not use missing cached claims as a client-side denial", async () => {
|
|
5051
5159
|
const startEventSync = vi.fn().mockResolvedValue(null);
|
|
5052
5160
|
const { loop, shutdown } = runLoop({ claims: null, startEventSync });
|
|
5053
5161
|
await microtasks();
|
|
5054
5162
|
shutdown();
|
|
5055
5163
|
await loop;
|
|
5056
|
-
expect(startEventSync).
|
|
5164
|
+
expect(startEventSync).toHaveBeenCalledTimes(1);
|
|
5057
5165
|
});
|
|
5058
|
-
it("
|
|
5166
|
+
it("asks the server for authority before wiring publish and receive", async () => {
|
|
5059
5167
|
const startEventSync = vi.fn().mockResolvedValue(null);
|
|
5060
5168
|
const { loop, shutdown } = runLoop({
|
|
5061
|
-
claims:
|
|
5169
|
+
claims: EMAIL_CLAIMS,
|
|
5062
5170
|
startEventSync,
|
|
5063
5171
|
});
|
|
5064
5172
|
await microtasks();
|
|
@@ -5081,9 +5189,16 @@ describe("runRunnerWithLoop — Phase 3 event-sync wiring (US-017/018/019)", ()
|
|
|
5081
5189
|
ownDeviceId: "dev-test",
|
|
5082
5190
|
dispose: vi.fn().mockResolvedValue(undefined),
|
|
5083
5191
|
});
|
|
5084
|
-
|
|
5085
|
-
const
|
|
5086
|
-
|
|
5192
|
+
let finishTargetedPush = () => { };
|
|
5193
|
+
const targetedPush = new Promise((resolve) => {
|
|
5194
|
+
finishTargetedPush = resolve;
|
|
5195
|
+
});
|
|
5196
|
+
const runPass = vi
|
|
5197
|
+
.fn()
|
|
5198
|
+
.mockResolvedValueOnce(0)
|
|
5199
|
+
.mockImplementation((passArgv) => passArgv.includes("--company") ? targetedPush : Promise.resolve(0));
|
|
5200
|
+
const { loop, watcher, clock, shutdown } = runLoop({
|
|
5201
|
+
claims: EMAIL_CLAIMS,
|
|
5087
5202
|
startEventSync,
|
|
5088
5203
|
runPass,
|
|
5089
5204
|
});
|
|
@@ -5095,12 +5210,18 @@ describe("runRunnerWithLoop — Phase 3 event-sync wiring (US-017/018/019)", ()
|
|
|
5095
5210
|
watcher.emit("companies/indigo/a.md", batch);
|
|
5096
5211
|
clock.advance(0);
|
|
5097
5212
|
await microtasks();
|
|
5098
|
-
|
|
5213
|
+
// U07 wakes the scheduler immediately. It may start the scoped push, but
|
|
5214
|
+
// it must not announce the batch until that push has succeeded.
|
|
5215
|
+
expect(runPass).toHaveBeenCalledWith(expect.arrayContaining([
|
|
5216
|
+
"--company",
|
|
5217
|
+
"indigo",
|
|
5218
|
+
"--direction",
|
|
5219
|
+
"push",
|
|
5220
|
+
]));
|
|
5099
5221
|
expect(publishBatch).not.toHaveBeenCalled();
|
|
5100
|
-
|
|
5222
|
+
finishTargetedPush(0);
|
|
5101
5223
|
await microtasks();
|
|
5102
5224
|
// Scoped push drained and succeeded → batch published.
|
|
5103
|
-
expect(runPass).toHaveBeenCalled();
|
|
5104
5225
|
expect(publishBatch).toHaveBeenCalledTimes(1);
|
|
5105
5226
|
expect([...publishBatch.mock.calls[0][0].paths.values()]).toEqual([
|
|
5106
5227
|
"companies/indigo/a.md",
|
|
@@ -5122,7 +5243,7 @@ describe("runRunnerWithLoop — Phase 3 event-sync wiring (US-017/018/019)", ()
|
|
|
5122
5243
|
.mockResolvedValueOnce(0)
|
|
5123
5244
|
.mockResolvedValue(1);
|
|
5124
5245
|
const { loop, watcher, clock, tick, shutdown } = runLoop({
|
|
5125
|
-
claims:
|
|
5246
|
+
claims: EMAIL_CLAIMS,
|
|
5126
5247
|
startEventSync,
|
|
5127
5248
|
runPass,
|
|
5128
5249
|
});
|
|
@@ -5153,7 +5274,7 @@ describe("runRunnerWithLoop — Phase 3 event-sync wiring (US-017/018/019)", ()
|
|
|
5153
5274
|
});
|
|
5154
5275
|
const runPass = vi.fn().mockResolvedValue(0);
|
|
5155
5276
|
const { loop, watcher, clock, tick, shutdown } = runLoop({
|
|
5156
|
-
claims:
|
|
5277
|
+
claims: EMAIL_CLAIMS,
|
|
5157
5278
|
startEventSync,
|
|
5158
5279
|
runPass,
|
|
5159
5280
|
});
|
|
@@ -5205,7 +5326,7 @@ describe("runRunnerWithLoop — Phase 3 event-sync wiring (US-017/018/019)", ()
|
|
|
5205
5326
|
})
|
|
5206
5327
|
.mockResolvedValue(0);
|
|
5207
5328
|
const { loop, watcher, tick, shutdown } = runLoop({
|
|
5208
|
-
claims:
|
|
5329
|
+
claims: EMAIL_CLAIMS,
|
|
5209
5330
|
startEventSync,
|
|
5210
5331
|
runPass,
|
|
5211
5332
|
});
|
|
@@ -5246,7 +5367,7 @@ describe("runRunnerWithLoop — Phase 3 event-sync wiring (US-017/018/019)", ()
|
|
|
5246
5367
|
})
|
|
5247
5368
|
.mockResolvedValue(0);
|
|
5248
5369
|
const { loop, watcher, tick, shutdown } = runLoop({
|
|
5249
|
-
claims:
|
|
5370
|
+
claims: EMAIL_CLAIMS,
|
|
5250
5371
|
startEventSync,
|
|
5251
5372
|
runPass,
|
|
5252
5373
|
});
|
|
@@ -5275,7 +5396,7 @@ describe("runRunnerWithLoop — Phase 3 event-sync wiring (US-017/018/019)", ()
|
|
|
5275
5396
|
});
|
|
5276
5397
|
const runPass = vi.fn().mockResolvedValue(0);
|
|
5277
5398
|
const { loop, watcher, clock, tick, shutdown } = runLoop({
|
|
5278
|
-
claims:
|
|
5399
|
+
claims: EMAIL_CLAIMS,
|
|
5279
5400
|
startEventSync,
|
|
5280
5401
|
runPass,
|
|
5281
5402
|
});
|
|
@@ -5382,7 +5503,7 @@ describe("runRunnerWithLoop — Phase 3 event-sync wiring (US-017/018/019)", ()
|
|
|
5382
5503
|
triggerShutdown = handler;
|
|
5383
5504
|
return () => { };
|
|
5384
5505
|
},
|
|
5385
|
-
getIdTokenClaims: () =>
|
|
5506
|
+
getIdTokenClaims: () => EMAIL_CLAIMS,
|
|
5386
5507
|
getAccessToken: async () => "jwt-test",
|
|
5387
5508
|
startEventSync,
|
|
5388
5509
|
});
|
|
@@ -5482,7 +5603,7 @@ describe("runRunnerWithLoop — Phase 3 event-sync wiring (US-017/018/019)", ()
|
|
|
5482
5603
|
dispose: vi.fn().mockResolvedValue(undefined),
|
|
5483
5604
|
});
|
|
5484
5605
|
const { loop, watcher, clock, tick, shutdown } = runLoop({
|
|
5485
|
-
claims:
|
|
5606
|
+
claims: EMAIL_CLAIMS,
|
|
5486
5607
|
startEventSync,
|
|
5487
5608
|
});
|
|
5488
5609
|
await microtasks();
|
|
@@ -5506,7 +5627,7 @@ describe("runRunnerWithLoop — Phase 3 event-sync wiring (US-017/018/019)", ()
|
|
|
5506
5627
|
dispose,
|
|
5507
5628
|
});
|
|
5508
5629
|
const { loop, shutdown } = runLoop({
|
|
5509
|
-
claims:
|
|
5630
|
+
claims: EMAIL_CLAIMS,
|
|
5510
5631
|
startEventSync,
|
|
5511
5632
|
});
|
|
5512
5633
|
await microtasks();
|
|
@@ -5520,7 +5641,7 @@ describe("runRunnerWithLoop — Phase 3 event-sync wiring (US-017/018/019)", ()
|
|
|
5520
5641
|
try {
|
|
5521
5642
|
const startEventSync = vi.fn().mockResolvedValue(null);
|
|
5522
5643
|
const { loop, shutdown } = runLoop({
|
|
5523
|
-
claims:
|
|
5644
|
+
claims: EMAIL_CLAIMS,
|
|
5524
5645
|
startEventSync,
|
|
5525
5646
|
});
|
|
5526
5647
|
await microtasks();
|