@effect-agent/testing 0.1.0-beta.41 → 0.1.0-beta.44

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/package.json CHANGED
@@ -1,71 +1 @@
1
- {
2
- "name": "@effect-agent/testing",
3
- "version": "0.1.0-beta.41",
4
- "exports": {
5
- ".": {
6
- "types": "./dist/index.d.mts",
7
- "default": "./dist/index.mjs"
8
- },
9
- "./certification": {
10
- "types": "./dist/certification.d.mts",
11
- "default": "./dist/certification.mjs"
12
- },
13
- "./chaos": {
14
- "types": "./dist/chaos.d.mts",
15
- "default": "./dist/chaos.mjs"
16
- },
17
- "./code-executor": {
18
- "types": "./dist/code-executor.d.mts",
19
- "default": "./dist/code-executor.mjs"
20
- },
21
- "./fixtures/docs-researcher": {
22
- "types": "./dist/docs-researcher.d.mts",
23
- "default": "./dist/docs-researcher.mjs"
24
- },
25
- "./fixtures/travel-planner": {
26
- "types": "./dist/travel-planner.d.mts",
27
- "default": "./dist/travel-planner.mjs"
28
- }
29
- },
30
- "dependencies": {
31
- "@effect-agent/capabilities": "0.1.0-beta.41",
32
- "@effect-agent/core": "0.1.0-beta.41",
33
- "@effect-agent/engine": "0.1.0-beta.41",
34
- "@effect-agent/sandbox": "0.1.0-beta.41",
35
- "@effect-agent/thread": "0.1.0-beta.41"
36
- },
37
- "peerDependencies": {
38
- "effect": "^4.0.0-rc.111"
39
- },
40
- "description": "Scripted models, deterministic fixtures, and adapter conformance kits for testing Effect Agent applications.",
41
- "license": "MIT",
42
- "repository": {
43
- "type": "git",
44
- "url": "git+https://github.com/danieljvdm/effect-agent.git",
45
- "directory": "packages/testing"
46
- },
47
- "files": [
48
- "dist",
49
- "src"
50
- ],
51
- "type": "module",
52
- "publishConfig": {
53
- "access": "public"
54
- },
55
- "scripts": {
56
- "build": "vp pack",
57
- "check": "tsc --noEmit -p tsconfig.json",
58
- "test": "vp test --passWithNoTests"
59
- },
60
- "devDependencies": {
61
- "@effect-agent/platform-node": "0.1.0-beta.40",
62
- "@effect-agent/storage-memory": "0.1.0-beta.40",
63
- "@effect-agent/storage-sqlite": "0.1.0-beta.40",
64
- "@effect/platform-node": "4.0.0-rc.111",
65
- "@effect/sql-sqlite-node": "4.0.0-rc.111",
66
- "@effect/vitest": "4.0.0-rc.111",
67
- "effect": "4.0.0-rc.111",
68
- "typescript": "7.0.2",
69
- "vite-plus": "0.3.0"
70
- }
71
- }
1
+ {"name":"@effect-agent/testing","version":"0.1.0-beta.44","dependencies":{"@effect-agent/capabilities":"0.1.0-beta.44","@effect-agent/core":"0.1.0-beta.44","@effect-agent/engine":"0.1.0-beta.44","@effect-agent/sandbox":"0.1.0-beta.44","@effect-agent/thread":"0.1.0-beta.44"},"devDependencies":{"@effect-agent/platform-node":"0.1.0-beta.44","@effect-agent/storage-memory":"0.1.0-beta.44","@effect-agent/storage-sqlite":"0.1.0-beta.44","@effect/platform-node":"4.0.0-rc.112","@effect/sql-sqlite-node":"4.0.0-rc.112","@effect/vitest":"4.0.0-rc.112","effect":"4.0.0-rc.112","typescript":"7.0.2","vite-plus":"0.3.0"},"peerDependencies":{"effect":"^4.0.0-rc.112"},"exports":{".":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"./certification":{"types":"./dist/certification.d.mts","default":"./dist/certification.mjs"},"./chaos":{"types":"./dist/chaos.d.mts","default":"./dist/chaos.mjs"},"./code-executor":{"types":"./dist/code-executor.d.mts","default":"./dist/code-executor.mjs"},"./fixtures/docs-researcher":{"types":"./dist/docs-researcher.d.mts","default":"./dist/docs-researcher.mjs"},"./fixtures/travel-planner":{"types":"./dist/travel-planner.d.mts","default":"./dist/travel-planner.mjs"}},"description":"Scripted models, deterministic fixtures, and adapter conformance kits for testing Effect Agent applications.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/danieljvdm/effect-agent.git","directory":"packages/testing"},"files":["dist","src"],"type":"module","publishConfig":{"access":"public"},"scripts":{"build":"vp pack","check":"tsc --noEmit -p tsconfig.json","test":"vp test --passWithNoTests"}}
@@ -184,9 +184,11 @@ export const tier2NeverFiredLocations = (
184
184
  tier2: ReadonlyArray<CertificationSweepResult>,
185
185
  ): ReadonlyArray<DurableRuntimeFailpointLocation> => {
186
186
  const fired = new Set<DurableRuntimeFailpointLocation>();
187
+
187
188
  for (const row of tier2) {
188
189
  if (row.failpointFired) fired.add(row.location);
189
190
  }
191
+
190
192
  return DurableRuntimeFailpointLocation.literals.filter((location) => !fired.has(location)).sort();
191
193
  };
192
194
 
@@ -196,16 +198,19 @@ export const tier2NeverFiredLocations = (
196
198
 
197
199
  const SHA_A = Schema.decodeSync(Digest)("a".repeat(64));
198
200
  const DIGESTS = DefinitionDigests.make({ agent: SHA_A, model: SHA_A, tools: SHA_A });
201
+
199
202
  const CHILD_DIGEST_STRINGS = {
200
203
  agent: "b".repeat(64),
201
204
  model: "c".repeat(64),
202
205
  tools: "d".repeat(64),
203
206
  } as const;
207
+
204
208
  const CHILD_DIGESTS = DefinitionDigests.make({
205
209
  agent: Schema.decodeSync(Digest)(CHILD_DIGEST_STRINGS.agent),
206
210
  model: Schema.decodeSync(Digest)(CHILD_DIGEST_STRINGS.model),
207
211
  tools: Schema.decodeSync(Digest)(CHILD_DIGEST_STRINGS.tools),
208
212
  });
213
+
209
214
  const PRINCIPAL = Schema.decodeSync(Principal)("principal-certification");
210
215
  const decodeThreadId = Schema.decodeSync(ThreadId);
211
216
  const decodeIdempotencyKey = Schema.decodeSync(IdempotencyKey);
@@ -257,8 +262,10 @@ const promptShapeModel = (
257
262
  generateText: () => Effect.succeed([]),
258
263
  streamText: (request) => {
259
264
  const hasToolResult = request.prompt.content.some((message) => message.role === "tool");
265
+
260
266
  const parts =
261
267
  toolParts === undefined || hasToolResult ? finalParts(finalText) : toolParts;
268
+
262
269
  return Stream.fromIterable(parts);
263
270
  },
264
271
  }),
@@ -295,7 +302,9 @@ const Book = Tool.make("book", {
295
302
  parameters: Schema.Struct({ ref: Schema.String }),
296
303
  success: Schema.Struct({ confirmation: Schema.String }),
297
304
  });
305
+
298
306
  const bookToolkit = Toolkit.make(Book);
307
+
299
308
  const uncertainDefinition = Agent.make("certify-uncertain", {
300
309
  input: QuestionInput,
301
310
  output: AnswerOutput,
@@ -311,7 +320,9 @@ const Itinerary = Tool.make("itinerary", {
311
320
  failure: DurableStepError,
312
321
  dependencies: [DurableStep],
313
322
  });
323
+
314
324
  const itineraryToolkit = Toolkit.make(Itinerary);
325
+
315
326
  const stepsDefinition = Agent.make("certify-steps", {
316
327
  input: QuestionInput,
317
328
  output: AnswerOutput,
@@ -326,7 +337,9 @@ const BookApproval = Tool.make("book", {
326
337
  success: Schema.Struct({ confirmation: Schema.String }),
327
338
  needsApproval: true,
328
339
  });
340
+
329
341
  const approvalToolkit = Toolkit.make(BookApproval);
342
+
330
343
  const approvalDefinition = Agent.make("certify-approval", {
331
344
  input: QuestionInput,
332
345
  output: AnswerOutput,
@@ -399,10 +412,12 @@ const identifiers = Layer.effect(
399
412
  IdGenerator,
400
413
  Effect.gen(function* () {
401
414
  const counter = yield* Ref.make(0);
415
+
402
416
  const next = <A>(decode: (value: string) => A, prefix: string) =>
403
417
  Ref.getAndUpdate(counter, (value) => value + 1).pipe(
404
418
  Effect.map((value) => decode(`${prefix}-${value}`)),
405
419
  );
420
+
406
421
  return {
407
422
  nextThreadId: next(decodeThreadId, "certify-fixture-thread"),
408
423
  nextRunId: next(Schema.decodeSync(RunId), "certify-fixture-run"),
@@ -439,15 +454,19 @@ const makeSingleAgentCell = (
439
454
  slug: string,
440
455
  ): CertificationCell => {
441
456
  const threadId = decodeThreadId(`certify-${slug}`);
457
+
442
458
  const submit = Effect.gen(function* () {
443
459
  const runtime = yield* DurableAgentRuntime;
460
+
444
461
  const receipt = yield* runtime.submit(
445
462
  { definition: { id: definition.id, input: definition.input } },
446
463
  { question: `certify ${slug}` },
447
464
  submitOptionsFor(slug, threadId),
448
465
  );
466
+
449
467
  return [receipt];
450
468
  });
469
+
451
470
  return {
452
471
  bindings: [resolved],
453
472
  submit,
@@ -465,7 +484,9 @@ const makeCell = Effect.fn("Certification.makeCell")(function* (
465
484
  plainDefinition,
466
485
  promptShapeModel("certify-plain", '{"answer":"done"}'),
467
486
  );
487
+
468
488
  const resolved = yield* DurableWorkerBinding.make(binding, DIGESTS);
489
+
469
490
  return makeSingleAgentCell(plainDefinition, resolved, slug);
470
491
  }
471
492
  case "uncertain-tool": {
@@ -477,12 +498,15 @@ const makeCell = Effect.fn("Certification.makeCell")(function* (
477
498
  toolTurn(toolCallPart("book-1", "book", { ref: `r-${slug}` })),
478
499
  ),
479
500
  );
501
+
480
502
  const toolLayer = bookToolkit.toLayer({
481
503
  book: ({ ref }) => Effect.succeed({ confirmation: `confirmed-${ref}` }),
482
504
  });
505
+
483
506
  const resolved = yield* DurableWorkerBinding.make(binding, DIGESTS).pipe(
484
507
  Effect.provide(toolLayer),
485
508
  );
509
+
486
510
  return makeSingleAgentCell(uncertainDefinition, resolved, slug);
487
511
  }
488
512
  case "durable-steps": {
@@ -494,26 +518,32 @@ const makeCell = Effect.fn("Certification.makeCell")(function* (
494
518
  toolTurn(toolCallPart("itinerary-1", "itinerary", { ref: `trip-${slug}` })),
495
519
  ),
496
520
  );
521
+
497
522
  const toolLayer = itineraryToolkit.toLayer({
498
523
  itinerary: ({ ref }) =>
499
524
  Effect.gen(function* () {
500
525
  const step = yield* DurableStep;
526
+
501
527
  const flight = yield* step.do(
502
528
  "reserve-flight",
503
529
  Schema.String,
504
530
  Effect.succeed(`flight-${ref}`),
505
531
  );
532
+
506
533
  const lodging = yield* step.do(
507
534
  "reserve-lodging",
508
535
  Schema.String,
509
536
  Effect.succeed(`lodging-${ref}`),
510
537
  );
538
+
511
539
  return { state: `${flight}+${lodging}` };
512
540
  }),
513
541
  });
542
+
514
543
  const resolved = yield* DurableWorkerBinding.make(binding, DIGESTS).pipe(
515
544
  Effect.provide(toolLayer),
516
545
  );
546
+
517
547
  return makeSingleAgentCell(stepsDefinition, resolved, slug);
518
548
  }
519
549
  case "approval": {
@@ -525,12 +555,15 @@ const makeCell = Effect.fn("Certification.makeCell")(function* (
525
555
  toolTurn(toolCallPart("book-1", "book", { ref: `r-${slug}` })),
526
556
  ),
527
557
  );
558
+
528
559
  const toolLayer = approvalToolkit.toLayer({
529
560
  book: ({ ref }) => Effect.succeed({ confirmation: `confirmed-${ref}` }),
530
561
  });
562
+
531
563
  const resolved = yield* DurableWorkerBinding.make(binding, DIGESTS).pipe(
532
564
  Effect.provide(toolLayer),
533
565
  );
566
+
534
567
  return makeSingleAgentCell(approvalDefinition, resolved, slug);
535
568
  }
536
569
  case "join": {
@@ -538,11 +571,14 @@ const makeCell = Effect.fn("Certification.makeCell")(function* (
538
571
  plainDefinition,
539
572
  promptShapeModel("certify-join", '{"answer":"host answer"}'),
540
573
  );
574
+
541
575
  const resolved = yield* DurableWorkerBinding.make(binding, DIGESTS);
542
576
  const threadId = decodeThreadId(`certify-${slug}`);
577
+
543
578
  const submitOne = (key: string, question: string) =>
544
579
  Effect.gen(function* () {
545
580
  const runtime = yield* DurableAgentRuntime;
581
+
546
582
  return yield* runtime.submit(
547
583
  { definition: { id: plainDefinition.id, input: plainDefinition.input } },
548
584
  { question },
@@ -554,15 +590,18 @@ const makeCell = Effect.fn("Certification.makeCell")(function* (
554
590
  },
555
591
  );
556
592
  });
593
+
557
594
  const cell: CertificationCell = {
558
595
  bindings: [resolved],
559
596
  submit: Effect.gen(function* () {
560
597
  const host = yield* submitOne(`certify-key-${slug}-host`, "host question");
561
598
  const queued = yield* submitOne(`certify-key-${slug}-queued`, "queued question");
599
+
562
600
  return [host, queued];
563
601
  }),
564
602
  lanes: () => [threadId],
565
603
  };
604
+
566
605
  return cell;
567
606
  }
568
607
  case "delegation": {
@@ -570,6 +609,7 @@ const makeCell = Effect.fn("Certification.makeCell")(function* (
570
609
  childDefinition,
571
610
  promptShapeModel("certify-child", '{"answer":"child-answer"}'),
572
611
  );
612
+
573
613
  const parentBinding = Agent.withModel(
574
614
  coordinatorDefinition,
575
615
  promptShapeModel(
@@ -581,36 +621,45 @@ const makeCell = Effect.fn("Certification.makeCell")(function* (
581
621
  ),
582
622
  ),
583
623
  );
624
+
584
625
  const delegationLayer = SubagentRuntime.layer(researchDelegation, childBinding, {
585
626
  mapChildFailure,
586
627
  durable: { targetDigests: CHILD_DIGEST_STRINGS },
587
628
  }).pipe(Layer.provide(delegationSupport));
629
+
588
630
  const lookupLayer = Toolkit.make(Lookup).toLayer({
589
631
  lookup: ({ key }) => Effect.succeed({ value: `found-${key}` }),
590
632
  });
633
+
591
634
  const parentResolved = yield* DurableWorkerBinding.make(parentBinding, DIGESTS).pipe(
592
635
  Effect.provide(Layer.mergeAll(delegationLayer, lookupLayer)),
593
636
  );
637
+
594
638
  const childResolved = yield* DurableWorkerBinding.make(childBinding, CHILD_DIGESTS);
595
639
  const threadId = decodeThreadId(`certify-${slug}`);
640
+
596
641
  const cell: CertificationCell = {
597
642
  bindings: [parentResolved, childResolved],
598
643
  submit: Effect.gen(function* () {
599
644
  const runtime = yield* DurableAgentRuntime;
645
+
600
646
  const receipt = yield* runtime.submit(
601
647
  { definition: { id: coordinatorDefinition.id, input: coordinatorDefinition.input } },
602
648
  { mission: "plan" },
603
649
  submitOptionsFor(slug, threadId),
604
650
  );
651
+
605
652
  return [receipt];
606
653
  }),
607
654
  lanes: (receipts) => {
608
655
  const parent = receipts.at(0);
656
+
609
657
  return parent === undefined
610
658
  ? [threadId]
611
659
  : [threadId, childThreadIdFor(parent.submissionId, DELEGATE_CALL)];
612
660
  },
613
661
  };
662
+
614
663
  return cell;
615
664
  }
616
665
  }
@@ -634,12 +683,15 @@ const verifyLane = Effect.fn("Certification.verifyLane")(function* (
634
683
  const exported = yield* store.export(ThreadExportRequest.make({ threadId: lane }));
635
684
  const rows = new Map<SubmissionId, SubmissionSnapshot>();
636
685
  const nonterminal = yield* Stream.runCollect(ledger.scanNonterminal);
686
+
637
687
  for (const submission of nonterminal) {
638
688
  if (submission.threadId === lane) rows.set(submission.submissionId, submission);
639
689
  }
640
690
  const named = new Set<SubmissionId>();
691
+
641
692
  for (const envelope of exported.records) {
642
693
  const payload = envelope.record.payload;
694
+
643
695
  if (
644
696
  payload._tag === "UserInputRecorded" ||
645
697
  payload._tag === "SubmissionSettled" ||
@@ -651,14 +703,17 @@ const verifyLane = Effect.fn("Certification.verifyLane")(function* (
651
703
  for (const submissionId of named) {
652
704
  if (rows.has(submissionId)) continue;
653
705
  const found = yield* ledger.lookup(SubmissionLookupById.make({ submissionId }));
706
+
654
707
  if (Option.isSome(found) && found.value.threadId === lane) {
655
708
  rows.set(submissionId, found.value);
656
709
  }
657
710
  }
711
+
658
712
  const checkpoint =
659
713
  store.checkpoints === undefined
660
714
  ? Option.none()
661
715
  : yield* store.checkpoints.load(LoadCheckpointRequest.make({ threadId: lane }));
716
+
662
717
  return yield* verifyThreadInvariants({
663
718
  export: exported,
664
719
  submissions: [...rows.values()],
@@ -671,13 +726,17 @@ const verifyLane = Effect.fn("Certification.verifyLane")(function* (
671
726
 
672
727
  const failureTagOf = <E>(cause: Cause.Cause<E>): string => {
673
728
  const failure = Cause.findErrorOption(cause);
729
+
674
730
  if (Option.isSome(failure)) {
675
731
  const error: unknown = failure.value;
732
+
676
733
  if (typeof error === "object" && error !== null && "_tag" in error) {
677
734
  return String(error._tag);
678
735
  }
736
+
679
737
  return String(error).slice(0, 256);
680
738
  }
739
+
681
740
  return "defect";
682
741
  };
683
742
 
@@ -708,6 +767,7 @@ const runSweepCell = Effect.fn("Certification.runSweepCell")(function* (
708
767
  // One-shot arm: the fault fires at most once anywhere in the cell (initial drive OR a
709
768
  // re-drive round's public unblocking operation), modelling one crash at this boundary.
710
769
  const fired = yield* Ref.make(false);
770
+
711
771
  yield* control.setHandler((hit) =>
712
772
  hit !== location
713
773
  ? Effect.void
@@ -723,12 +783,15 @@ const runSweepCell = Effect.fn("Certification.runSweepCell")(function* (
723
783
  // Submissions are idempotent (DUR-001): one replay recovers a submit-boundary fault.
724
784
  let receipts: ReadonlyArray<Receipt>;
725
785
  const firstSubmit = yield* Effect.exit(cell.submit);
786
+
726
787
  if (Exit.isSuccess(firstSubmit)) {
727
788
  receipts = firstSubmit.value;
728
789
  } else {
729
790
  const secondSubmit = yield* Effect.exit(cell.submit);
791
+
730
792
  if (Exit.isFailure(secondSubmit)) {
731
793
  yield* control.clear;
794
+
732
795
  return failed(
733
796
  `submission replay did not recover: ${failureTagOf(secondSubmit.cause)}`,
734
797
  yield* Ref.get(fired),
@@ -745,14 +808,17 @@ const runSweepCell = Effect.fn("Certification.runSweepCell")(function* (
745
808
  const snapshot = yield* ledger.lookup(
746
809
  SubmissionLookupById.make({ submissionId: receipt.submissionId }),
747
810
  );
811
+
748
812
  if (Option.isNone(snapshot) || snapshot.value.state !== "settled") return false;
749
813
  }
814
+
750
815
  return true;
751
816
  });
752
817
 
753
818
  // Re-drive to convergence using ONLY public operations: worker drives, recovery passes,
754
819
  // and the authorized DUR-017/approval unblocking paths chosen from `explainThread`.
755
820
  let converged = false;
821
+
756
822
  for (let round = 0; round < MAX_REDRIVE_ROUNDS && !converged; round++) {
757
823
  // Expire any lease a faulted Attempt left behind (D5): virtual time is the
758
824
  // adapter-neutral reclaim lever — a live lease may block every new claim.
@@ -763,6 +829,7 @@ const runSweepCell = Effect.fn("Certification.runSweepCell")(function* (
763
829
  }
764
830
  for (const lane of lanes) {
765
831
  const explains = yield* Effect.exit(runtime.explainThread(lane));
832
+
766
833
  if (Exit.isFailure(explains)) continue;
767
834
  for (const explanation of explains.value) {
768
835
  for (const unknown of explanation.evidence.unknownCalls) {
@@ -783,6 +850,7 @@ const runSweepCell = Effect.fn("Certification.runSweepCell")(function* (
783
850
  const decided = explanation.evidence.approvalDecisions.some(
784
851
  (decision) => decision.toolCallId === pending.toolCallId,
785
852
  );
853
+
786
854
  if (decided) continue;
787
855
  yield* Effect.exit(
788
856
  runtime.resolveApproval(
@@ -799,6 +867,7 @@ const runSweepCell = Effect.fn("Certification.runSweepCell")(function* (
799
867
  }
800
868
  }
801
869
  const settled = yield* Effect.exit(allSettled);
870
+
802
871
  converged = Exit.isSuccess(settled) && settled.value;
803
872
  }
804
873
  yield* control.clear;
@@ -811,8 +880,10 @@ const runSweepCell = Effect.fn("Certification.runSweepCell")(function* (
811
880
  // Every lane of the cell must verify in convergence mode with a recomputed digest chain.
812
881
  let digestChainVerified = true;
813
882
  const failedChecks: Array<string> = [];
883
+
814
884
  for (const lane of lanes) {
815
885
  const verdict = yield* Effect.exit(verifyLane(lane, batchProducers));
886
+
816
887
  if (Exit.isFailure(verdict)) {
817
888
  return failed(`lane ${lane} could not be verified: ${failureTagOf(verdict.cause)}`, wasFired);
818
889
  }
@@ -873,6 +944,7 @@ export const resolveTierThree = Effect.fn("Certification.resolveTierThree")(func
873
944
  }
874
945
  if (options.crashLever !== undefined) {
875
946
  const cases = yield* options.crashLever;
947
+
876
948
  return CertificationTierThreeReport.make({
877
949
  status: cases.length === 0 ? "not-exercised" : "exercised",
878
950
  evidence: options.tierThreeEvidence ?? [],
@@ -889,6 +961,7 @@ export const resolveTierThree = Effect.fn("Certification.resolveTierThree")(func
889
961
  cases: [],
890
962
  });
891
963
  }
964
+
892
965
  return CertificationTierThreeReport.make({
893
966
  status: "not-exercised",
894
967
  evidence: [],
@@ -917,12 +990,14 @@ export const certifyDurableAdapters = <LedgerE = never, StoreE = never>(
917
990
  options: CertifyDurableAdaptersOptions<LedgerE, StoreE>,
918
991
  ): Effect.Effect<CertificationReport, LedgerE | StoreE, Crypto.Crypto> => {
919
992
  const batchProducers = new Map<BatchId, ProducerId>();
993
+
920
994
  // Interpose the candidate store with an append-time capture of each batch's producer
921
995
  // identity — the one value the ThreadStore port deliberately does not export — so
922
996
  // Tier 2's invariant verification recomputes the FULL digest chain instead of skipping it.
923
997
  const capturingStore = Layer.effect(ThreadStore)(
924
998
  Effect.gen(function* () {
925
999
  const inner = yield* ThreadStore;
1000
+
926
1001
  return ThreadStore.of({
927
1002
  ...inner,
928
1003
  append: (request) =>
@@ -947,6 +1022,7 @@ export const certifyDurableAdapters = <LedgerE = never, StoreE = never>(
947
1022
  abortPollInterval: Duration.millis(50),
948
1023
  }),
949
1024
  );
1025
+
950
1026
  // RUN-036: certification uses the default-none Tool failure observer. Trusted application
951
1027
  // reporting adds no durable transition and is verified separately from adapter certification.
952
1028
  const environment = DurableAgentRuntime.layer.pipe(Layer.provideMerge(support));
@@ -960,6 +1036,7 @@ export const certifyDurableAdapters = <LedgerE = never, StoreE = never>(
960
1036
 
961
1037
  // Tier 2 — coordinator failpoint convergence sweep.
962
1038
  const tier2: Array<CertificationSweepResult> = [];
1039
+
963
1040
  for (const scenario of CERTIFICATION_SCENARIOS) {
964
1041
  for (const location of DurableRuntimeFailpointLocation.literals) {
965
1042
  tier2.push(yield* runSweepCell(scenario, location, batchProducers, leaseAdvance));
@@ -974,6 +1051,7 @@ export const certifyDurableAdapters = <LedgerE = never, StoreE = never>(
974
1051
  const tier3 = yield* resolveTierThree(capabilities.durability, options);
975
1052
 
976
1053
  const generatedAt = yield* nowUtc;
1054
+
977
1055
  const ok =
978
1056
  tier1.every((result) => result.status === "passed") &&
979
1057
  tier2.every((result) => result.status !== "failed") &&