@pegma/support-desk-application 0.2.7 → 0.2.9

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.
Files changed (42) hide show
  1. package/README.md +46 -20
  2. package/dist/engineering-followup-internal.d.ts +9 -2
  3. package/dist/engineering-followup-internal.d.ts.map +1 -1
  4. package/dist/engineering-followup-records.d.ts +12 -0
  5. package/dist/engineering-followup-records.d.ts.map +1 -1
  6. package/dist/engineering-followup-records.js +55 -5
  7. package/dist/engineering-followup-records.js.map +1 -1
  8. package/dist/engineering-followup-source.d.ts +18 -7
  9. package/dist/engineering-followup-source.d.ts.map +1 -1
  10. package/dist/engineering-followup-source.js +191 -23
  11. package/dist/engineering-followup-source.js.map +1 -1
  12. package/dist/engineering-followup-types.d.ts +75 -9
  13. package/dist/engineering-followup-types.d.ts.map +1 -1
  14. package/dist/engineering-followup-work.d.ts +9 -12
  15. package/dist/engineering-followup-work.d.ts.map +1 -1
  16. package/dist/engineering-followup-work.js +783 -234
  17. package/dist/engineering-followup-work.js.map +1 -1
  18. package/dist/engineering-handoff.d.ts +6 -0
  19. package/dist/engineering-handoff.d.ts.map +1 -0
  20. package/dist/engineering-handoff.js +31 -0
  21. package/dist/engineering-handoff.js.map +1 -0
  22. package/dist/engineering-types.d.ts +28 -0
  23. package/dist/engineering-types.d.ts.map +1 -1
  24. package/dist/engineering-work.d.ts +1 -1
  25. package/dist/engineering-work.d.ts.map +1 -1
  26. package/dist/engineering-work.js +534 -32
  27. package/dist/engineering-work.js.map +1 -1
  28. package/dist/index.d.ts +13 -1
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +65 -4
  31. package/dist/index.js.map +1 -1
  32. package/dist/resolution-learning-delivery-handoff.d.ts +5 -0
  33. package/dist/resolution-learning-delivery-handoff.d.ts.map +1 -0
  34. package/dist/resolution-learning-delivery-handoff.js +20 -0
  35. package/dist/resolution-learning-delivery-handoff.js.map +1 -0
  36. package/dist/resolution-learning-types.d.ts +42 -1
  37. package/dist/resolution-learning-types.d.ts.map +1 -1
  38. package/dist/resolution-learning-worker.d.ts +1 -1
  39. package/dist/resolution-learning-worker.d.ts.map +1 -1
  40. package/dist/resolution-learning-worker.js +594 -369
  41. package/dist/resolution-learning-worker.js.map +1 -1
  42. package/package.json +3 -3
@@ -5,6 +5,7 @@ import { SupportDeskAuthorizationError, SupportDeskConflictError, SupportDeskLim
5
5
  import { canonical, dataArray, encodedBytes, fields, fingerprint, integer, text, timestamp, } from "./assistant-turn-records.js";
6
6
  import { safeEngineeringSnapshot as snapshot, checkEngineeringRecordCapacity, advanceEngineeringFollowupRevocationEpoch, engineeringMaximumCandidateBytes, engineeringMaximumRecordBytes, engineeringCommandKey, engineeringSourceKey, engineeringWorkKey, validateEngineeringCandidate, validateEngineeringModelBinding, validateEngineeringExportRequest, } from "./engineering-records.js";
7
7
  import { projectEngineeringRows } from "./engineering-internal.js";
8
+ import { readEngineeringSourceVersion } from "./engineering-evidence.js";
8
9
  export async function engineeringModelManifestHash(manifest) {
9
10
  const value = fields(manifest, "engineering input manifest", [
10
11
  "messages",
@@ -304,6 +305,259 @@ export function createEngineeringWorkerServices(deps, load) {
304
305
  throw new SupportDeskNotFoundError();
305
306
  return { value: stored.value, version: stored.version };
306
307
  }
308
+ function prepareTicketRead(ticketId) {
309
+ const failed = (error) => ({ ok: false, error });
310
+ try {
311
+ return records
312
+ .getVersioned({ partition: ticketId, id: "ticket" })
313
+ .then((value) => ({ ok: true, value }), failed);
314
+ }
315
+ catch (error) {
316
+ return Promise.resolve(failed(error));
317
+ }
318
+ }
319
+ async function observedModelLoad(access, command, actorId, completion, ticketRead) {
320
+ const preparedTicket = ticketRead === undefined ? undefined : await ticketRead;
321
+ if (preparedTicket !== undefined && !preparedTicket.ok)
322
+ throw preparedTicket.error;
323
+ const ticket = preparedTicket === undefined
324
+ ? await records.getVersioned({
325
+ partition: command.ticketId,
326
+ id: "ticket",
327
+ })
328
+ : preparedTicket.value;
329
+ permission(access, false, actorId);
330
+ if (ticket?.value.kind !== "ticket")
331
+ throw new SupportDeskNotFoundError();
332
+ const source = await readEngineeringSourceVersion(records, command.ticketId);
333
+ permission(access, false, actorId);
334
+ if (source === null)
335
+ throw new SupportDeskNotFoundError();
336
+ // Terminal accepted recovery never projects private observations or invokes a guard.
337
+ const eligible = source.value.state === "eligible" &&
338
+ source.value.security === "ordinary" &&
339
+ source.value.sourceGeneration === command.sourceGeneration;
340
+ if (!eligible ||
341
+ !hasPermission(access, supportPermissions.engineeringRead) ||
342
+ !hasPermission(access, supportPermissions.queueRead)) {
343
+ if (completion !== undefined) {
344
+ const stored = await workAt(command);
345
+ permission(access, false, actorId);
346
+ if (stored.value.lastCompletion?.fingerprint === completion)
347
+ return { accepted: outcome(stored.value) };
348
+ }
349
+ modelReadPermission(access, actorId);
350
+ throw new SupportDeskConflictError("Engineering source, work or claim fence changed");
351
+ }
352
+ modelReadPermission(access, actorId);
353
+ // The complete bounded list retains error/capacity priority; Work is never used on its failure.
354
+ const [rowsResult, workResult] = await Promise.allSettled([
355
+ Promise.resolve().then(() => records.list(command.ticketId)),
356
+ Promise.resolve().then(() => workAt(command)),
357
+ ]);
358
+ permission(access, false, actorId);
359
+ modelReadPermission(access, actorId);
360
+ if (rowsResult.status === "rejected")
361
+ throw rowsResult.reason;
362
+ const rows = rowsResult.value;
363
+ if (rows.length > deps.limits.maxRecordsPerTicket)
364
+ throw new SupportDeskLimitError("ticket_partition", deps.limits.maxRecordsPerTicket);
365
+ if (workResult.status === "rejected")
366
+ throw workResult.reason;
367
+ return {
368
+ loaded: {
369
+ ticket: { value: ticket.value, version: ticket.version },
370
+ source,
371
+ rows,
372
+ },
373
+ stored: workResult.value,
374
+ };
375
+ }
376
+ async function observedDispatchLoad(access, command, actorId) {
377
+ const source = await readEngineeringSourceVersion(records, command.ticketId);
378
+ modelReadPermission(access, actorId);
379
+ if (source === null)
380
+ throw new SupportDeskNotFoundError();
381
+ if (source.value.state !== "eligible" ||
382
+ source.value.security !== "ordinary" ||
383
+ source.value.sourceGeneration !== command.sourceGeneration)
384
+ throw new SupportDeskConflictError("Engineering dispatch source changed");
385
+ const [ticketResult, rowsResult, workResult] = await Promise.allSettled([
386
+ Promise.resolve().then(() => records.getVersioned({ partition: command.ticketId, id: "ticket" })),
387
+ Promise.resolve().then(() => records.list(command.ticketId)),
388
+ Promise.resolve().then(() => workAt(command)),
389
+ ]);
390
+ modelReadPermission(access, actorId);
391
+ if (ticketResult.status === "rejected")
392
+ throw ticketResult.reason;
393
+ const ticket = ticketResult.value;
394
+ if (ticket?.value.kind !== "ticket")
395
+ throw new SupportDeskNotFoundError();
396
+ if (rowsResult.status === "rejected")
397
+ throw rowsResult.reason;
398
+ if (rowsResult.value.length > deps.limits.maxRecordsPerTicket)
399
+ throw new SupportDeskLimitError("ticket_partition", deps.limits.maxRecordsPerTicket);
400
+ if (workResult.status === "rejected")
401
+ throw workResult.reason;
402
+ return {
403
+ loaded: {
404
+ ticket: { value: ticket.value, version: ticket.version },
405
+ source,
406
+ rows: rowsResult.value,
407
+ },
408
+ stored: workResult.value,
409
+ };
410
+ }
411
+ function modelReadPermission(reader, actorId) {
412
+ permission(reader, false, actorId);
413
+ for (const name of [
414
+ supportPermissions.engineeringRead,
415
+ supportPermissions.queueRead,
416
+ ])
417
+ if (!hasPermission(reader, name))
418
+ throw new SupportDeskAuthorizationError(name);
419
+ }
420
+ function modelObservation(loaded, stored, actorId, confirmTicket = false) {
421
+ let open = true;
422
+ let confirmed = false;
423
+ let confirmations = 0;
424
+ const check = (reader) => {
425
+ modelReadPermission(reader, actorId);
426
+ if (!open)
427
+ throw new SupportDeskConflictError("Engineering model observation expired");
428
+ };
429
+ const observation = Object.freeze({
430
+ read(reader) {
431
+ check(reader);
432
+ if (!confirmed)
433
+ throw new SupportDeskConflictError("Engineering model observation requires confirmation");
434
+ confirmed = false;
435
+ return {
436
+ source: snapshot(loaded.source.value),
437
+ work: snapshot(stored.value),
438
+ };
439
+ },
440
+ async confirm(reader) {
441
+ check(reader);
442
+ confirmed = false;
443
+ const source = await records.getVersioned(engineeringSourceKey(loaded.source.value.ticketId));
444
+ check(reader);
445
+ if (source?.version !== loaded.source.version)
446
+ throw new SupportDeskConflictError("Engineering model source changed");
447
+ const workKey = engineeringWorkKey(stored.value.ticketId, stored.value.sourceGeneration);
448
+ if (confirmTicket) {
449
+ const [ticketResult, workResult] = await Promise.allSettled([
450
+ Promise.resolve().then(() => records.get({ partition: stored.value.ticketId, id: "ticket" })),
451
+ Promise.resolve().then(() => records.getVersioned(workKey)),
452
+ ]);
453
+ check(reader);
454
+ if (ticketResult.status === "rejected")
455
+ throw ticketResult.reason;
456
+ if (ticketResult.value?.kind !== "ticket")
457
+ throw new SupportDeskNotFoundError();
458
+ if (workResult.status === "rejected")
459
+ throw workResult.reason;
460
+ if (workResult.value?.version !== stored.version)
461
+ throw new SupportDeskConflictError("Engineering model work changed");
462
+ const finalSource = await records.getVersioned(engineeringSourceKey(loaded.source.value.ticketId));
463
+ check(reader);
464
+ if (finalSource?.version !== loaded.source.version)
465
+ throw new SupportDeskConflictError("Engineering model source changed");
466
+ }
467
+ else {
468
+ const work = await records.getVersioned(workKey);
469
+ check(reader);
470
+ if (work?.version !== stored.version)
471
+ throw new SupportDeskConflictError("Engineering model work changed");
472
+ }
473
+ confirmed = true;
474
+ confirmations += 1;
475
+ },
476
+ });
477
+ return {
478
+ observation,
479
+ assertOpen: check,
480
+ confirmationCount: () => confirmations,
481
+ close() {
482
+ open = false;
483
+ confirmed = false;
484
+ },
485
+ };
486
+ }
487
+ async function preparedModelCurrent(access, loaded, stored, actorId) {
488
+ const source = await records.getVersioned(engineeringSourceKey(stored.value.ticketId));
489
+ permission(access, false, actorId);
490
+ if (source?.version !== loaded.source.version)
491
+ return false;
492
+ const [ticket, work] = await Promise.allSettled([
493
+ Promise.resolve().then(() => records.getVersioned({
494
+ partition: stored.value.ticketId,
495
+ id: "ticket",
496
+ })),
497
+ Promise.resolve().then(() => records.getVersioned(engineeringWorkKey(stored.value.ticketId, stored.value.sourceGeneration))),
498
+ ]);
499
+ permission(access, false, actorId);
500
+ if (ticket.status === "rejected")
501
+ throw ticket.reason;
502
+ if (ticket.value?.value.kind !== "ticket")
503
+ throw new SupportDeskNotFoundError();
504
+ if (work.status === "rejected")
505
+ throw work.reason;
506
+ if (ticket.value.version !== loaded.ticket.version ||
507
+ work.value?.version !== stored.version)
508
+ return false;
509
+ const finalSource = await records.getVersioned(engineeringSourceKey(stored.value.ticketId));
510
+ permission(access, false, actorId);
511
+ return finalSource?.version === loaded.source.version;
512
+ }
513
+ async function modelGuard(guard, loaded, stored, actorId) {
514
+ const scope = modelObservation(loaded, stored, actorId);
515
+ const boundary = engineeringMutationBoundary({
516
+ verifyBeforeCommit: () => guard(scope.observation),
517
+ });
518
+ try {
519
+ return await boundary.verify();
520
+ }
521
+ finally {
522
+ scope.close();
523
+ }
524
+ }
525
+ async function workerGuard(guard, access, loaded, stored, verifyApplication, actorId, phase) {
526
+ const scope = modelObservation(loaded, stored, actorId, phase === "after_dispatch");
527
+ let verified = false;
528
+ let afterApplication = scope.confirmationCount();
529
+ let applicationAssertion;
530
+ const observation = Object.freeze({
531
+ phase,
532
+ read: scope.observation.read,
533
+ confirm: scope.observation.confirm,
534
+ async verifyApplication() {
535
+ scope.assertOpen(access);
536
+ applicationAssertion = await verifyApplication();
537
+ scope.assertOpen(access);
538
+ verified = true;
539
+ afterApplication = scope.confirmationCount();
540
+ return applicationAssertion;
541
+ },
542
+ });
543
+ const boundary = engineeringMutationBoundary({
544
+ verifyBeforeCommit: () => guard(observation),
545
+ });
546
+ try {
547
+ const assertion = await boundary.verify();
548
+ if (!verified)
549
+ await observation.verifyApplication();
550
+ if (scope.confirmationCount() <= afterApplication)
551
+ await observation.confirm(access);
552
+ return () => {
553
+ applicationAssertion?.();
554
+ assertion();
555
+ };
556
+ }
557
+ finally {
558
+ scope.close();
559
+ }
560
+ }
307
561
  async function save(loaded, stored, work, source = loaded.source.value, extra = [], beforeCommit, mutation) {
308
562
  const securityRestriction = (source !== loaded.source.value && source.state === "restricted") ||
309
563
  (work.security !== "ordinary" &&
@@ -347,6 +601,13 @@ export function createEngineeringWorkerServices(deps, load) {
347
601
  !current(loaded, work))
348
602
  throw new SupportDeskConflictError("Engineering source, work or claim fence changed");
349
603
  }
604
+ function modelPreparationCurrent(loaded, work, command, now) {
605
+ live(loaded, work, command, now);
606
+ if (work.stage !== "model" ||
607
+ work.dispatch !== undefined ||
608
+ work.candidate !== undefined)
609
+ throw new SupportDeskConflictError("Engineering model stage changed");
610
+ }
350
611
  function restrictedSource(loaded, work, security = "restricted") {
351
612
  const source = loaded.source.value;
352
613
  if (source.state === "withdrawn" ||
@@ -366,6 +627,9 @@ export function createEngineeringWorkerServices(deps, load) {
366
627
  }
367
628
  async function freshPolicy(access, expected) {
368
629
  const policy = snapshot(await deps.policy.resolve(access));
630
+ return checkedPolicy(policy, expected);
631
+ }
632
+ function checkedPolicy(policy, expected) {
369
633
  if (!policy.policy.enabled ||
370
634
  (expected !== undefined &&
371
635
  (policy.generation !== expected.policyGeneration ||
@@ -549,7 +813,7 @@ export function createEngineeringWorkerServices(deps, load) {
549
813
  throw new SupportDeskConflictError("Engineering proposal verification expired");
550
814
  return deadline;
551
815
  }
552
- return {
816
+ const services = {
553
817
  async scanEngineeringCandidates(access, input) {
554
818
  const actorId = access.principalId;
555
819
  const authority = scanPermission(access, actorId);
@@ -785,7 +1049,7 @@ export function createEngineeringWorkerServices(deps, load) {
785
1049
  }
786
1050
  throw new SupportDeskConflictError();
787
1051
  },
788
- async prepareEngineeringModel(access, input) {
1052
+ async prepareEngineeringModel(access, input, guard, seed) {
789
1053
  permission(access);
790
1054
  const actorId = access.principalId;
791
1055
  const command = snapshot(fields(input, "prepare engineering model", [
@@ -800,23 +1064,31 @@ export function createEngineeringWorkerServices(deps, load) {
800
1064
  validateEngineeringModelBinding(command.binding);
801
1065
  if (!options.enabled)
802
1066
  throw new SupportDeskConflictError("Engineering processing disabled");
803
- const runNow = effectiveTime(deps);
1067
+ const runNow = seed?.now ?? effectiveTime(deps);
804
1068
  for (let attempt = 0; attempt < deps.maxConflictAttempts; attempt += 1) {
805
1069
  permission(access, false, actorId);
806
- const loaded = await load(command.ticketId);
807
- const stored = await workAt(command);
1070
+ const observed = attempt === 0 && seed !== undefined
1071
+ ? seed
1072
+ : guard === undefined
1073
+ ? undefined
1074
+ : await observedModelLoad(access, command, actorId);
1075
+ if (observed !== undefined && "accepted" in observed)
1076
+ throw new SupportDeskConflictError();
1077
+ const loaded = observed === undefined
1078
+ ? await load(command.ticketId)
1079
+ : observed.loaded;
1080
+ const stored = observed === undefined ? await workAt(command) : observed.stored;
808
1081
  const work = stored.value;
809
- live(loaded, work, command, runNow());
810
- if (work.stage !== "model" ||
811
- work.dispatch !== undefined ||
812
- work.candidate !== undefined)
813
- throw new SupportDeskConflictError("Engineering model stage changed");
1082
+ modelPreparationCurrent(loaded, work, command, runNow());
814
1083
  const binding = command.binding;
815
1084
  const manifest = binding.inputManifest;
816
- const policy = await freshPolicy(access, {
1085
+ const expectedPolicy = {
817
1086
  policyGeneration: manifest.policyGeneration,
818
1087
  policyHash: manifest.policyHash,
819
- });
1088
+ };
1089
+ const policy = attempt === 0 && seed !== undefined
1090
+ ? checkedPolicy(seed.policy, expectedPolicy)
1091
+ : await freshPolicy(access, expectedPolicy);
820
1092
  if ((await engineeringModelManifestHash(manifest)) !==
821
1093
  binding.inputManifestHash ||
822
1094
  manifest.evidenceFingerprint !==
@@ -841,14 +1113,40 @@ export function createEngineeringWorkerServices(deps, load) {
841
1113
  deadline > timestamp(work.leaseUntil, "leaseUntil") ||
842
1114
  deadline > timestamp(work.deadline, "deadline"))
843
1115
  throw new SupportDeskConflictError("Engineering model deadline exceeds live claim");
1116
+ const assertDeadline = () => {
1117
+ if (deadline <= runNow())
1118
+ throw new SupportDeskConflictError("Engineering prepared model deadline expired");
1119
+ };
844
1120
  const existing = work.modelAttempts.find((entry) => canonical(entry.binding) === canonical(binding));
845
1121
  if (existing !== undefined) {
846
1122
  if (existing.preparedClaimGeneration !== command.claimGeneration ||
847
1123
  existing.stepId !== work.stepId)
848
1124
  throw new SupportDeskConflictError("Engineering model preparation belongs to another invocation");
1125
+ const assertProof = guard === undefined
1126
+ ? undefined
1127
+ : await modelGuard(guard, loaded, stored, actorId);
1128
+ if (guard !== undefined) {
1129
+ if (!(await preparedModelCurrent(access, loaded, stored, actorId)))
1130
+ continue;
1131
+ permission(access, false, actorId);
1132
+ live(loaded, work, command, runNow());
1133
+ assertProof?.();
1134
+ assertDeadline();
1135
+ return {
1136
+ stepId: existing.stepId,
1137
+ requestHash: existing.requestHash,
1138
+ maxCandidateBytes: 16_000,
1139
+ };
1140
+ }
849
1141
  live(loaded, work, command, runNow());
1142
+ assertDeadline();
850
1143
  permission(access, false, actorId);
851
- if (await save(loaded, stored, work)) {
1144
+ if (await save(loaded, stored, work, loaded.source.value, [], () => {
1145
+ permission(access, false, actorId);
1146
+ live(loaded, work, command, runNow());
1147
+ assertProof?.();
1148
+ assertDeadline();
1149
+ })) {
852
1150
  permission(access, false, actorId);
853
1151
  return {
854
1152
  stepId: existing.stepId,
@@ -885,20 +1183,30 @@ export function createEngineeringWorkerServices(deps, load) {
885
1183
  workGeneration: work.workGeneration + 1,
886
1184
  };
887
1185
  reserveReachable(next);
1186
+ const assertProof = guard === undefined
1187
+ ? undefined
1188
+ : await modelGuard(guard, loaded, stored, actorId);
1189
+ assertDeadline();
888
1190
  await freshPolicy(access, {
889
1191
  policyGeneration: manifest.policyGeneration,
890
1192
  policyHash: manifest.policyHash,
891
1193
  });
892
1194
  live(loaded, work, command, runNow());
1195
+ assertDeadline();
893
1196
  permission(access, false, actorId);
894
- if (await save(loaded, stored, next)) {
1197
+ if (await save(loaded, stored, next, loaded.source.value, [], () => {
1198
+ permission(access, false, actorId);
1199
+ live(loaded, work, command, runNow());
1200
+ assertProof?.();
1201
+ assertDeadline();
1202
+ })) {
895
1203
  permission(access, false, actorId);
896
1204
  return { stepId, requestHash, maxCandidateBytes: 16_000 };
897
1205
  }
898
1206
  }
899
1207
  throw new SupportDeskConflictError();
900
1208
  },
901
- async completeEngineeringModel(access, input, guard) {
1209
+ async completeEngineeringModel(access, input, guard, observationGuard) {
902
1210
  permission(access);
903
1211
  const actorId = access.principalId;
904
1212
  if (typeof guard !== "function")
@@ -926,8 +1234,25 @@ export function createEngineeringWorkerServices(deps, load) {
926
1234
  const runNow = effectiveTime(deps);
927
1235
  for (let attempt = 0; attempt < deps.maxConflictAttempts; attempt += 1) {
928
1236
  permission(access, false, actorId);
929
- const loaded = await load(command.ticketId);
930
- const stored = await workAt(command);
1237
+ let ticketRead;
1238
+ if (observationGuard !== undefined) {
1239
+ // Start both bounded reads in one wave. Exact receipt recovery never
1240
+ // awaits or interprets the speculative Ticket, including its failure.
1241
+ ticketRead = prepareTicketRead(command.ticketId);
1242
+ const accepted = await workAt(command);
1243
+ permission(access, false, actorId);
1244
+ if (accepted.value.lastCompletion?.fingerprint === completion)
1245
+ return outcome(accepted.value);
1246
+ }
1247
+ const observed = observationGuard === undefined
1248
+ ? undefined
1249
+ : await observedModelLoad(access, command, actorId, completion, ticketRead);
1250
+ if (observed !== undefined && "accepted" in observed)
1251
+ return observed.accepted;
1252
+ const loaded = observed === undefined
1253
+ ? await load(command.ticketId)
1254
+ : observed.loaded;
1255
+ const stored = observed === undefined ? await workAt(command) : observed.stored;
931
1256
  const work = stored.value;
932
1257
  if (work.lastCompletion?.fingerprint === completion) {
933
1258
  permission(access, false, actorId);
@@ -971,7 +1296,11 @@ export function createEngineeringWorkerServices(deps, load) {
971
1296
  }),
972
1297
  };
973
1298
  checkEngineeringRecordCapacity(next);
974
- await guard();
1299
+ let assertProof;
1300
+ if (observationGuard === undefined)
1301
+ await guard();
1302
+ else
1303
+ assertProof = await modelGuard(observationGuard, loaded, stored, actorId);
975
1304
  await freshPolicy(access, {
976
1305
  policyGeneration: model.binding.inputManifest.policyGeneration,
977
1306
  policyHash: model.binding.inputManifest.policyHash,
@@ -986,6 +1315,7 @@ export function createEngineeringWorkerServices(deps, load) {
986
1315
  if (await save(loaded, stored, next, nextSource, [], () => {
987
1316
  permission(access, false, actorId);
988
1317
  live(loaded, work, command, runNow());
1318
+ assertProof?.();
989
1319
  if (timestamp(model.binding.deadline, "model.deadline") <= runNow())
990
1320
  throw new SupportDeskConflictError("Engineering prepared model deadline expired");
991
1321
  })) {
@@ -1264,7 +1594,7 @@ export function createEngineeringWorkerServices(deps, load) {
1264
1594
  }
1265
1595
  throw new SupportDeskConflictError();
1266
1596
  },
1267
- async prepareEngineeringProposal(access, input, guard) {
1597
+ async prepareEngineeringProposal(access, input, guard, observationGuard) {
1268
1598
  permission(access);
1269
1599
  const actorId = access.principalId;
1270
1600
  if (typeof guard !== "function")
@@ -1438,7 +1768,9 @@ export function createEngineeringWorkerServices(deps, load) {
1438
1768
  requestHash: checksBinding.requestHash,
1439
1769
  preparedClaimGeneration: command.claimGeneration,
1440
1770
  };
1441
- const deadline = await verifyProposal(access, loaded, work, proposal, runNow);
1771
+ let deadline = observationGuard === undefined
1772
+ ? await verifyProposal(access, loaded, work, proposal, runNow)
1773
+ : Number.POSITIVE_INFINITY;
1442
1774
  const { approval: _approval, candidate: _candidate, proposalNeedsRebuild: _needsRebuild, ...unreviewed } = work;
1443
1775
  const next = identical
1444
1776
  ? work
@@ -1469,9 +1801,26 @@ export function createEngineeringWorkerServices(deps, load) {
1469
1801
  };
1470
1802
  reserveReachable(acknowledged);
1471
1803
  deps.admission(loaded.ticket.value.ticket, loaded.rows, 2);
1472
- await guard();
1473
- await currentRepository(proposal.request);
1474
- await freshPolicy(access, proposal);
1804
+ let assertProof;
1805
+ if (observationGuard === undefined) {
1806
+ await guard();
1807
+ await currentRepository(proposal.request);
1808
+ await freshPolicy(access, proposal);
1809
+ }
1810
+ else {
1811
+ assertProof = await workerGuard(observationGuard, access, loaded, stored, async () => {
1812
+ deadline = Math.min(deadline, await verifyProposal(access, loaded, work, proposal, runNow));
1813
+ await currentRepository(proposal.request);
1814
+ await freshPolicy(access, proposal);
1815
+ return () => {
1816
+ permission(access, false, actorId);
1817
+ live(loaded, work, command, runNow());
1818
+ if (runNow() >= deadline)
1819
+ throw new SupportDeskConflictError("Engineering exact proposal checks expired");
1820
+ return true;
1821
+ };
1822
+ }, actorId, "proposal");
1823
+ }
1475
1824
  permission(access, false, actorId);
1476
1825
  live(loaded, work, command, runNow());
1477
1826
  if (runNow() >= deadline)
@@ -1480,7 +1829,15 @@ export function createEngineeringWorkerServices(deps, load) {
1480
1829
  deps.audit(causal, loaded.ticket.value.ticket, actorId, clock.now(), "support.engineering.proposal_prepared"),
1481
1830
  receipt(causal, hash, result),
1482
1831
  ];
1483
- if (await save(loaded, stored, acknowledged, loaded.source.value, actions)) {
1832
+ if (await save(loaded, stored, acknowledged, loaded.source.value, actions, observationGuard === undefined
1833
+ ? undefined
1834
+ : () => {
1835
+ permission(access, false, actorId);
1836
+ live(loaded, work, command, runNow());
1837
+ if (runNow() >= deadline)
1838
+ throw new SupportDeskConflictError("Engineering exact proposal checks expired");
1839
+ assertProof?.();
1840
+ })) {
1484
1841
  permission(access, false, actorId);
1485
1842
  return result;
1486
1843
  }
@@ -1658,7 +2015,7 @@ export function createEngineeringWorkerServices(deps, load) {
1658
2015
  }
1659
2016
  throw new SupportDeskConflictError();
1660
2017
  },
1661
- async beginEngineeringDispatch(access, input, guard) {
2018
+ async beginEngineeringDispatch(access, input, guard, observationGuard) {
1662
2019
  permission(access);
1663
2020
  const actorId = access.principalId;
1664
2021
  if (typeof guard !== "function")
@@ -1689,8 +2046,15 @@ export function createEngineeringWorkerServices(deps, load) {
1689
2046
  const runNow = effectiveTime(deps);
1690
2047
  for (let attempt = 0; attempt < deps.maxConflictAttempts; attempt += 1) {
1691
2048
  permission(access, false, actorId);
1692
- const loaded = await load(command.ticketId);
1693
- const stored = await workAt(command);
2049
+ const observed = observationGuard === undefined
2050
+ ? undefined
2051
+ : await observedModelLoad(access, command, actorId);
2052
+ if (observed !== undefined && "accepted" in observed)
2053
+ throw new SupportDeskConflictError();
2054
+ const loaded = observed === undefined
2055
+ ? await load(command.ticketId)
2056
+ : observed.loaded;
2057
+ const stored = observed === undefined ? await workAt(command) : observed.stored;
1694
2058
  const work = stored.value;
1695
2059
  live(loaded, work, command, runNow());
1696
2060
  const proposal = work.proposal;
@@ -1722,10 +2086,29 @@ export function createEngineeringWorkerServices(deps, load) {
1722
2086
  permission(access, false, actorId);
1723
2087
  return null;
1724
2088
  }
1725
- const deadline = await verifyProposal(access, loaded, work, proposal, runNow);
1726
- await guard();
1727
- await currentRepository(proposal.request);
1728
- await freshPolicy(access, proposal);
2089
+ let deadline = observationGuard === undefined
2090
+ ? await verifyProposal(access, loaded, work, proposal, runNow)
2091
+ : Number.POSITIVE_INFINITY;
2092
+ let assertProof;
2093
+ if (observationGuard === undefined) {
2094
+ await guard();
2095
+ await currentRepository(proposal.request);
2096
+ await freshPolicy(access, proposal);
2097
+ }
2098
+ else {
2099
+ assertProof = await workerGuard(observationGuard, access, loaded, stored, async () => {
2100
+ deadline = Math.min(deadline, await verifyProposal(access, loaded, work, proposal, runNow));
2101
+ await currentRepository(proposal.request);
2102
+ await freshPolicy(access, proposal);
2103
+ return () => {
2104
+ permission(access, false, actorId);
2105
+ live(loaded, work, command, runNow());
2106
+ if (runNow() >= deadline)
2107
+ throw new SupportDeskConflictError("Engineering dispatch verification expired");
2108
+ return true;
2109
+ };
2110
+ }, actorId, "before_dispatch");
2111
+ }
1729
2112
  permission(access, false, actorId);
1730
2113
  live(loaded, work, command, runNow());
1731
2114
  if (runNow() >= deadline)
@@ -1801,7 +2184,48 @@ export function createEngineeringWorkerServices(deps, load) {
1801
2184
  : [
1802
2185
  deps.audit(causal, loaded.ticket.value.ticket, actorId, clock.now(), "support.engineering.dispatch_started"),
1803
2186
  receipt(causal, hash, outcome(next)),
1804
- ])) {
2187
+ ], observationGuard === undefined
2188
+ ? undefined
2189
+ : () => {
2190
+ permission(access, false, actorId);
2191
+ live(loaded, work, command, runNow());
2192
+ if (runNow() >= deadline)
2193
+ throw new SupportDeskConflictError("Engineering dispatch verification expired");
2194
+ assertProof?.();
2195
+ })) {
2196
+ if (observationGuard !== undefined) {
2197
+ // The start transaction changed opaque versions: obtain a new scope from real storage.
2198
+ const latest = await observedDispatchLoad(access, command, actorId);
2199
+ live(latest.loaded, latest.stored.value, command, runNow());
2200
+ if (latest.stored.value.dispatch?.dispatchId !== command.dispatchId ||
2201
+ latest.stored.value.status !== "dispatch_started")
2202
+ throw new SupportDeskConflictError("Engineering dispatch changed before physical call authorization");
2203
+ const assertPostProof = await workerGuard(observationGuard, access, latest.loaded, latest.stored, async () => {
2204
+ deadline = Math.min(deadline, await verifyProposal(access, latest.loaded, latest.stored.value, proposal, runNow));
2205
+ await currentRepository(proposal.request);
2206
+ await freshPolicy(access, proposal);
2207
+ return () => {
2208
+ permission(access, false, actorId);
2209
+ live(latest.loaded, latest.stored.value, command, runNow());
2210
+ if (runNow() >= deadline)
2211
+ throw new SupportDeskConflictError("Engineering dispatch verification expired");
2212
+ return true;
2213
+ };
2214
+ }, actorId, "after_dispatch");
2215
+ permission(access, false, actorId);
2216
+ live(latest.loaded, latest.stored.value, command, runNow());
2217
+ if (runNow() >= deadline)
2218
+ throw new SupportDeskConflictError("Engineering dispatch verification expired");
2219
+ assertPostProof();
2220
+ return {
2221
+ ticketId: work.ticketId,
2222
+ sourceGeneration: work.sourceGeneration,
2223
+ dispatchId: dispatch.dispatchId,
2224
+ exportMarker: dispatch.exportMarker,
2225
+ requestHash: proposal.requestHash,
2226
+ request: snapshot(proposal.request),
2227
+ };
2228
+ }
1805
2229
  await guard();
1806
2230
  await currentRepository(proposal.request);
1807
2231
  await freshPolicy(access, proposal);
@@ -1828,5 +2252,83 @@ export function createEngineeringWorkerServices(deps, load) {
1828
2252
  throw new SupportDeskConflictError();
1829
2253
  },
1830
2254
  };
2255
+ return {
2256
+ ...services,
2257
+ // Public legacy wrappers cannot receive the private preloaded-attempt parameter.
2258
+ prepareEngineeringModel(access, command) {
2259
+ return services.prepareEngineeringModel(access, command);
2260
+ },
2261
+ completeEngineeringModel(access, command, guard) {
2262
+ return services.completeEngineeringModel(access, command, guard);
2263
+ },
2264
+ prepareEngineeringProposal(access, command, guard) {
2265
+ return services.prepareEngineeringProposal(access, command, guard);
2266
+ },
2267
+ beginEngineeringDispatch(access, command, guard) {
2268
+ return services.beginEngineeringDispatch(access, command, guard);
2269
+ },
2270
+ prepareEngineeringProposalObserved(access, command, guard) {
2271
+ if (typeof guard !== "function")
2272
+ throw new TypeError("Engineering proposal observation requires a guard");
2273
+ return services.prepareEngineeringProposal(access, command, () => { }, guard);
2274
+ },
2275
+ beginEngineeringDispatchObserved(access, command, guard) {
2276
+ if (typeof guard !== "function")
2277
+ throw new TypeError("Engineering dispatch observation requires a guard");
2278
+ return services.beginEngineeringDispatch(access, command, () => { }, guard);
2279
+ },
2280
+ async prepareEngineeringModelPlanned(access, input, planner) {
2281
+ permission(access);
2282
+ const actorId = access.principalId;
2283
+ if (typeof planner !== "function")
2284
+ throw new TypeError("Engineering model planning requires a callback");
2285
+ const command = snapshot(fields(input, "engineering model planning fence", [
2286
+ "ticketId",
2287
+ "sourceGeneration",
2288
+ "workerId",
2289
+ "claimId",
2290
+ "claimGeneration",
2291
+ ]));
2292
+ fence(command);
2293
+ if (!options.enabled)
2294
+ throw new SupportDeskConflictError("Engineering processing disabled");
2295
+ const runNow = effectiveTime(deps);
2296
+ const current = await observedModelLoad(access, command, actorId);
2297
+ if ("accepted" in current)
2298
+ throw new SupportDeskConflictError();
2299
+ modelPreparationCurrent(current.loaded, current.stored.value, command, runNow());
2300
+ const policy = await freshPolicy(access);
2301
+ let open = true;
2302
+ let binding;
2303
+ try {
2304
+ binding = snapshot(await planner(Object.freeze({
2305
+ read(reader) {
2306
+ modelReadPermission(reader, actorId);
2307
+ if (!open)
2308
+ throw new SupportDeskConflictError("Engineering model planning expired");
2309
+ return {
2310
+ source: snapshot(current.loaded.source.value),
2311
+ claim: claimView(current.loaded, current.stored.value, policy, false),
2312
+ };
2313
+ },
2314
+ })));
2315
+ }
2316
+ finally {
2317
+ open = false;
2318
+ }
2319
+ permission(access, false, actorId);
2320
+ return services.prepareEngineeringModel(access, { ...command, binding }, () => { }, { ...current, policy, now: runNow });
2321
+ },
2322
+ prepareEngineeringModelObserved(access, command, guard) {
2323
+ if (typeof guard !== "function")
2324
+ throw new TypeError("Engineering model observation requires a guard");
2325
+ return services.prepareEngineeringModel(access, command, guard);
2326
+ },
2327
+ completeEngineeringModelObserved(access, command, guard) {
2328
+ if (typeof guard !== "function")
2329
+ throw new TypeError("Engineering model observation requires a guard");
2330
+ return services.completeEngineeringModel(access, command, () => { }, guard);
2331
+ },
2332
+ };
1831
2333
  }
1832
2334
  //# sourceMappingURL=engineering-work.js.map