@indigoai-us/hq-cloud 6.15.82 → 6.15.83

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.
@@ -4067,6 +4067,238 @@ describe("runRunnerWithLoop — adaptive poll interval (no --poll-remote-ms)", (
4067
4067
  });
4068
4068
  });
4069
4069
  describe("runRunnerWithLoop — event-push wiring", () => {
4070
+ it("parallel coordinator activates every event surface after a partial completed full reconcile", async () => {
4071
+ const previousCoordinator = process.env.HQ_SYNC_PARALLEL_COORDINATOR;
4072
+ const previousEventSync = process.env.HQ_SYNC_EVENT_SYNC;
4073
+ const previousFullReconcileMs = process.env.HQ_SYNC_FULL_RECONCILE_MS;
4074
+ process.env.HQ_SYNC_PARALLEL_COORDINATOR = "1";
4075
+ process.env.HQ_SYNC_EVENT_SYNC = "1";
4076
+ process.env.HQ_SYNC_FULL_RECONCILE_MS = "60000";
4077
+ const watcher = makeWatcherStub();
4078
+ const receiver = {
4079
+ connected: false,
4080
+ start: vi.fn().mockResolvedValue(undefined),
4081
+ dispose: vi.fn().mockResolvedValue(undefined),
4082
+ };
4083
+ const scheduler = {
4084
+ signal: vi.fn(),
4085
+ checkHighWater: vi.fn().mockResolvedValue(undefined),
4086
+ dispose: vi.fn().mockResolvedValue(undefined),
4087
+ };
4088
+ const sleep = makeSteppableSleep();
4089
+ let now = 0;
4090
+ let triggerShutdown = () => { };
4091
+ const createWatcher = vi.fn(() => watcher);
4092
+ const createReceiver = vi.fn(() => receiver);
4093
+ const startRealtimeScheduler = vi.fn().mockResolvedValue(scheduler);
4094
+ const startEventSync = vi.fn().mockResolvedValue(null);
4095
+ const runPass = vi.fn().mockResolvedValue(completedPassOutcome(PARTIAL_SYNC_EXIT));
4096
+ const loop = runRunnerWithLoop(["--companies", "--watch", "--event-push", "--hq-root", "/tmp/hq"], {
4097
+ runPass,
4098
+ createWatcher,
4099
+ createReceiver,
4100
+ startRealtimeScheduler,
4101
+ startEventSync,
4102
+ getIdTokenClaims: () => null,
4103
+ getAccessToken: async () => "jwt-test",
4104
+ monotonicNow: () => now,
4105
+ sleep: sleep.sleep,
4106
+ onShutdownSignal: (handler) => {
4107
+ triggerShutdown = handler;
4108
+ return () => { };
4109
+ },
4110
+ });
4111
+ try {
4112
+ await flushLoopMicrotasks(30);
4113
+ expect(createWatcher).toHaveBeenCalledTimes(1);
4114
+ expect(createReceiver).toHaveBeenCalledTimes(1);
4115
+ expect(receiver.start).toHaveBeenCalledTimes(1);
4116
+ expect(startRealtimeScheduler).toHaveBeenCalledTimes(1);
4117
+ expect(startEventSync).toHaveBeenCalledTimes(1);
4118
+ // A partial RunnerPassOutcome is a completed legacy-equivalent pass, so
4119
+ // its completion must restart the configured full-reconcile cadence.
4120
+ now = 1;
4121
+ sleep.tick();
4122
+ await flushLoopMicrotasks();
4123
+ expect(runPass).toHaveBeenCalledTimes(1);
4124
+ }
4125
+ finally {
4126
+ triggerShutdown();
4127
+ await loop;
4128
+ if (previousCoordinator === undefined)
4129
+ delete process.env.HQ_SYNC_PARALLEL_COORDINATOR;
4130
+ else
4131
+ process.env.HQ_SYNC_PARALLEL_COORDINATOR = previousCoordinator;
4132
+ if (previousEventSync === undefined)
4133
+ delete process.env.HQ_SYNC_EVENT_SYNC;
4134
+ else
4135
+ process.env.HQ_SYNC_EVENT_SYNC = previousEventSync;
4136
+ if (previousFullReconcileMs === undefined)
4137
+ delete process.env.HQ_SYNC_FULL_RECONCILE_MS;
4138
+ else
4139
+ process.env.HQ_SYNC_FULL_RECONCILE_MS = previousFullReconcileMs;
4140
+ }
4141
+ });
4142
+ it("parallel coordinator activates event surfaces and advances cadence after a numeric-zero full reconcile", async () => {
4143
+ const previousCoordinator = process.env.HQ_SYNC_PARALLEL_COORDINATOR;
4144
+ const previousEventSync = process.env.HQ_SYNC_EVENT_SYNC;
4145
+ const previousFullReconcileMs = process.env.HQ_SYNC_FULL_RECONCILE_MS;
4146
+ process.env.HQ_SYNC_PARALLEL_COORDINATOR = "1";
4147
+ process.env.HQ_SYNC_EVENT_SYNC = "1";
4148
+ process.env.HQ_SYNC_FULL_RECONCILE_MS = "60000";
4149
+ const watcher = makeWatcherStub();
4150
+ const receiver = {
4151
+ connected: false,
4152
+ start: vi.fn().mockResolvedValue(undefined),
4153
+ dispose: vi.fn().mockResolvedValue(undefined),
4154
+ };
4155
+ const scheduler = {
4156
+ signal: vi.fn(),
4157
+ checkHighWater: vi.fn().mockResolvedValue(undefined),
4158
+ dispose: vi.fn().mockResolvedValue(undefined),
4159
+ };
4160
+ const sleep = makeSteppableSleep();
4161
+ let now = 0;
4162
+ let triggerShutdown = () => { };
4163
+ const createWatcher = vi.fn(() => watcher);
4164
+ const createReceiver = vi.fn(() => receiver);
4165
+ const startRealtimeScheduler = vi.fn().mockResolvedValue(scheduler);
4166
+ const startEventSync = vi.fn().mockResolvedValue(null);
4167
+ const runPass = vi.fn().mockResolvedValue(0);
4168
+ const loop = runRunnerWithLoop(["--companies", "--watch", "--event-push", "--hq-root", "/tmp/hq"], {
4169
+ runPass,
4170
+ createWatcher,
4171
+ createReceiver,
4172
+ startRealtimeScheduler,
4173
+ startEventSync,
4174
+ getIdTokenClaims: () => null,
4175
+ getAccessToken: async () => "jwt-test",
4176
+ monotonicNow: () => now,
4177
+ sleep: sleep.sleep,
4178
+ onShutdownSignal: (handler) => {
4179
+ triggerShutdown = handler;
4180
+ return () => { };
4181
+ },
4182
+ });
4183
+ try {
4184
+ await flushLoopMicrotasks(30);
4185
+ expect(createWatcher).toHaveBeenCalledTimes(1);
4186
+ expect(createReceiver).toHaveBeenCalledTimes(1);
4187
+ expect(receiver.start).toHaveBeenCalledTimes(1);
4188
+ expect(startRealtimeScheduler).toHaveBeenCalledTimes(1);
4189
+ expect(startEventSync).toHaveBeenCalledTimes(1);
4190
+ // The accepted numeric success seam must advance cadence, preventing an
4191
+ // immediate second full reconcile before the configured interval.
4192
+ now = 1;
4193
+ sleep.tick();
4194
+ await flushLoopMicrotasks();
4195
+ expect(runPass).toHaveBeenCalledTimes(1);
4196
+ }
4197
+ finally {
4198
+ triggerShutdown();
4199
+ await loop;
4200
+ if (previousCoordinator === undefined)
4201
+ delete process.env.HQ_SYNC_PARALLEL_COORDINATOR;
4202
+ else
4203
+ process.env.HQ_SYNC_PARALLEL_COORDINATOR = previousCoordinator;
4204
+ if (previousEventSync === undefined)
4205
+ delete process.env.HQ_SYNC_EVENT_SYNC;
4206
+ else
4207
+ process.env.HQ_SYNC_EVENT_SYNC = previousEventSync;
4208
+ if (previousFullReconcileMs === undefined)
4209
+ delete process.env.HQ_SYNC_FULL_RECONCILE_MS;
4210
+ else
4211
+ process.env.HQ_SYNC_FULL_RECONCILE_MS = previousFullReconcileMs;
4212
+ }
4213
+ });
4214
+ it("parallel coordinator keeps event surfaces off for an auth-required full reconcile", async () => {
4215
+ const previousCoordinator = process.env.HQ_SYNC_PARALLEL_COORDINATOR;
4216
+ const previousEventSync = process.env.HQ_SYNC_EVENT_SYNC;
4217
+ process.env.HQ_SYNC_PARALLEL_COORDINATOR = "1";
4218
+ process.env.HQ_SYNC_EVENT_SYNC = "1";
4219
+ const watcher = makeWatcherStub();
4220
+ const createWatcher = vi.fn(() => watcher);
4221
+ const createReceiver = vi.fn();
4222
+ const startRealtimeScheduler = vi.fn();
4223
+ const startEventSync = vi.fn();
4224
+ let triggerShutdown = () => { };
4225
+ const loop = runRunnerWithLoop(["--companies", "--watch", "--event-push", "--hq-root", "/tmp/hq"], {
4226
+ runPass: vi.fn().mockResolvedValue(AUTH_REQUIRED_PASS_EXIT),
4227
+ createWatcher,
4228
+ createReceiver,
4229
+ startRealtimeScheduler,
4230
+ startEventSync,
4231
+ sleep: () => new Promise(() => { }),
4232
+ onShutdownSignal: (handler) => {
4233
+ triggerShutdown = handler;
4234
+ return () => { };
4235
+ },
4236
+ });
4237
+ try {
4238
+ await flushLoopMicrotasks(30);
4239
+ expect(createWatcher).not.toHaveBeenCalled();
4240
+ expect(createReceiver).not.toHaveBeenCalled();
4241
+ expect(startRealtimeScheduler).not.toHaveBeenCalled();
4242
+ expect(startEventSync).not.toHaveBeenCalled();
4243
+ }
4244
+ finally {
4245
+ triggerShutdown();
4246
+ await loop;
4247
+ if (previousCoordinator === undefined)
4248
+ delete process.env.HQ_SYNC_PARALLEL_COORDINATOR;
4249
+ else
4250
+ process.env.HQ_SYNC_PARALLEL_COORDINATOR = previousCoordinator;
4251
+ if (previousEventSync === undefined)
4252
+ delete process.env.HQ_SYNC_EVENT_SYNC;
4253
+ else
4254
+ process.env.HQ_SYNC_EVENT_SYNC = previousEventSync;
4255
+ }
4256
+ });
4257
+ it("parallel coordinator keeps event surfaces off when no RunnerPassOutcome completed", async () => {
4258
+ const previousCoordinator = process.env.HQ_SYNC_PARALLEL_COORDINATOR;
4259
+ const previousEventSync = process.env.HQ_SYNC_EVENT_SYNC;
4260
+ process.env.HQ_SYNC_PARALLEL_COORDINATOR = "1";
4261
+ process.env.HQ_SYNC_EVENT_SYNC = "1";
4262
+ const watcher = makeWatcherStub();
4263
+ const createWatcher = vi.fn(() => watcher);
4264
+ const createReceiver = vi.fn();
4265
+ const startRealtimeScheduler = vi.fn();
4266
+ const startEventSync = vi.fn();
4267
+ let triggerShutdown = () => { };
4268
+ const loop = runRunnerWithLoop(["--companies", "--watch", "--event-push", "--hq-root", "/tmp/hq"], {
4269
+ // A bare nonzero status represents a chore that did not produce the
4270
+ // post-repair runner outcome needed to prove completion.
4271
+ runPass: vi.fn().mockResolvedValue(PARTIAL_SYNC_EXIT),
4272
+ createWatcher,
4273
+ createReceiver,
4274
+ startRealtimeScheduler,
4275
+ startEventSync,
4276
+ sleep: () => new Promise(() => { }),
4277
+ onShutdownSignal: (handler) => {
4278
+ triggerShutdown = handler;
4279
+ return () => { };
4280
+ },
4281
+ });
4282
+ try {
4283
+ await flushLoopMicrotasks(30);
4284
+ expect(createWatcher).not.toHaveBeenCalled();
4285
+ expect(createReceiver).not.toHaveBeenCalled();
4286
+ expect(startRealtimeScheduler).not.toHaveBeenCalled();
4287
+ expect(startEventSync).not.toHaveBeenCalled();
4288
+ }
4289
+ finally {
4290
+ triggerShutdown();
4291
+ await loop;
4292
+ if (previousCoordinator === undefined)
4293
+ delete process.env.HQ_SYNC_PARALLEL_COORDINATOR;
4294
+ else
4295
+ process.env.HQ_SYNC_PARALLEL_COORDINATOR = previousCoordinator;
4296
+ if (previousEventSync === undefined)
4297
+ delete process.env.HQ_SYNC_EVENT_SYNC;
4298
+ else
4299
+ process.env.HQ_SYNC_EVENT_SYNC = previousEventSync;
4300
+ }
4301
+ });
4070
4302
  it("keeps a healthy watcher through repeated poll cycles and the periodic full reconcile", async () => {
4071
4303
  const sleep = makeSteppableSleep();
4072
4304
  const watchers = [];