@effect-agent/storage-memory 0.1.0-beta.42 → 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.
@@ -329,10 +329,12 @@ const withChildReservation = (
329
329
 
330
330
  const findHead = (state: LedgerState, threadId: ThreadId): StoredSubmission | undefined => {
331
331
  let head: StoredSubmission | undefined;
332
+
332
333
  for (const stored of state.submissions.values()) {
333
334
  if (stored.row.threadId !== threadId || stored.row.state === "settled") continue;
334
335
  if (head === undefined || stored.row.queueSequence < head.row.queueSequence) head = stored;
335
336
  }
337
+
336
338
  return head;
337
339
  };
338
340
 
@@ -379,6 +381,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
379
381
  childReservations: new Map(),
380
382
  mintCounter: 0,
381
383
  });
384
+
382
385
  const leaseMillis = Duration.toMillis(DEFAULT_OWNERSHIP_LEASE_DURATION);
383
386
 
384
387
  const capabilities = Effect.succeed(LedgerCapabilities.make({ durability: "non-durable" }));
@@ -388,6 +391,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
388
391
  Effect.gen(function* () {
389
392
  const request = yield* validate(AdmissionRequest, "admit", unvalidated);
390
393
  const nowMillis = yield* Clock.currentTimeMillis;
394
+
391
395
  const decision = yield* Ref.modify(
392
396
  state,
393
397
  (
@@ -398,8 +402,10 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
398
402
  ] => {
399
403
  const key = admissionKey(request.threadId, request.principal, request.idempotencyKey);
400
404
  const existingId = current.admissionIndex.get(key);
405
+
401
406
  if (existingId !== undefined) {
402
407
  const existing = current.submissions.get(existingId);
408
+
403
409
  if (existing === undefined) {
404
410
  return [
405
411
  failure(
@@ -427,6 +433,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
427
433
  current,
428
434
  ];
429
435
  }
436
+
430
437
  return [
431
438
  success(
432
439
  AdmissionResult.make({
@@ -448,11 +455,14 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
448
455
  current,
449
456
  ];
450
457
  }
458
+
451
459
  const lane = current.lanes.get(request.threadId) ?? {
452
460
  nextQueueSequence: 1,
453
461
  producerEpoch: 0,
454
462
  };
463
+
455
464
  const mintCounter = current.mintCounter + 1;
465
+
456
466
  const row: SubmissionRow = {
457
467
  submissionId: decodeSubmissionId(`submission-memory-${mintCounter}`),
458
468
  threadId: request.threadId,
@@ -471,6 +481,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
471
481
  readyAtMillis: undefined,
472
482
  parentLinkage: request.parentLinkage,
473
483
  };
484
+
474
485
  const submissions = new Map(current.submissions).set(row.submissionId, {
475
486
  row,
476
487
  ownership: undefined,
@@ -483,11 +494,14 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
483
494
  approvalDecisions: new Map<ToolCallId, ApprovalDecisionIntent>(),
484
495
  unknownResolutions: new Map<ToolCallId, StoredUnknownResolution>(),
485
496
  });
497
+
486
498
  const admissionIndex = new Map(current.admissionIndex).set(key, row.submissionId);
499
+
487
500
  const lanes = new Map(current.lanes).set(request.threadId, {
488
501
  nextQueueSequence: lane.nextQueueSequence + 1,
489
502
  producerEpoch: lane.producerEpoch,
490
503
  });
504
+
491
505
  return [
492
506
  success(
493
507
  AdmissionResult.make({
@@ -502,7 +516,9 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
502
516
  ];
503
517
  },
504
518
  );
519
+
505
520
  if (decision._tag === "failure") return yield* decision.error;
521
+
506
522
  return decision.value;
507
523
  }),
508
524
  );
@@ -513,10 +529,12 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
513
529
  Effect.gen(function* () {
514
530
  const request = yield* validate(MarkReadyRequest, "markReady", unvalidated);
515
531
  const nowMillis = yield* Clock.currentTimeMillis;
532
+
516
533
  const decision = yield* Ref.modify(
517
534
  state,
518
535
  (current): readonly [Decision<void, LedgerError>, LedgerState] => {
519
536
  const stored = current.submissions.get(request.submissionId);
537
+
520
538
  if (stored === undefined) {
521
539
  return [
522
540
  failure(ledgerError("markReady", `Unknown Submission ${request.submissionId}`)),
@@ -524,6 +542,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
524
542
  ];
525
543
  }
526
544
  if (stored.row.state !== "admitted") return [success(undefined), current];
545
+
527
546
  return [
528
547
  success(undefined),
529
548
  withSubmission(current, {
@@ -533,6 +552,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
533
552
  ];
534
553
  },
535
554
  );
555
+
536
556
  if (decision._tag === "failure") return yield* decision.error;
537
557
  }),
538
558
  );
@@ -543,14 +563,17 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
543
563
  Effect.gen(function* () {
544
564
  const request = yield* validate(SubmissionLookup, "lookup", unvalidated);
545
565
  const current = yield* Ref.get(state);
566
+
546
567
  const submissionId =
547
568
  request._tag === "SubmissionLookupById"
548
569
  ? request.submissionId
549
570
  : current.admissionIndex.get(
550
571
  admissionKey(request.threadId, request.principal, request.idempotencyKey),
551
572
  );
573
+
552
574
  const stored =
553
575
  submissionId === undefined ? undefined : current.submissions.get(submissionId);
576
+
554
577
  return stored === undefined ? Option.none() : Option.some(toSnapshot(stored.row));
555
578
  }),
556
579
  );
@@ -560,20 +583,25 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
560
583
  )((unvalidated) =>
561
584
  Effect.gen(function* () {
562
585
  const request = yield* validate(SubmissionLookupByKey, "resolveAdmission", unvalidated);
586
+
563
587
  // Test-only fault seam: lets suites exercise the Indeterminate classification that a
564
588
  // single strongly consistent store never produces on its own (SUB-031, P6 honesty).
565
589
  if (options.resolveAdmissionFault !== undefined) {
566
590
  const fault = yield* options.resolveAdmissionFault;
591
+
567
592
  if (Option.isSome(fault)) {
568
593
  return AdmissionIndeterminate.make({ reason: fault.value });
569
594
  }
570
595
  }
571
596
  const current = yield* Ref.get(state);
597
+
572
598
  const submissionId = current.admissionIndex.get(
573
599
  admissionKey(request.threadId, request.principal, request.idempotencyKey),
574
600
  );
601
+
575
602
  const stored =
576
603
  submissionId === undefined ? undefined : current.submissions.get(submissionId);
604
+
577
605
  return stored === undefined
578
606
  ? AdmissionNotAdmitted.make()
579
607
  : AdmissionAdmitted.make({ submission: toSnapshot(stored.row) });
@@ -585,10 +613,12 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
585
613
  Effect.gen(function* () {
586
614
  const request = yield* validate(ClaimRequest, "claim", unvalidated);
587
615
  const nowMillis = yield* Clock.currentTimeMillis;
616
+
588
617
  const decision = yield* Ref.modify(
589
618
  state,
590
619
  (current): readonly [Decision<Option.Option<Claim>, LedgerError>, LedgerState] => {
591
620
  const head = findHead(current, request.threadId);
621
+
592
622
  if (head === undefined) return [success(Option.none()), current];
593
623
  if (
594
624
  BLOCKED_HEAD_STATES.has(head.row.state) ||
@@ -603,6 +633,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
603
633
  return [success(Option.none()), current];
604
634
  }
605
635
  const lane = current.lanes.get(request.threadId);
636
+
606
637
  if (lane === undefined) {
607
638
  return [
608
639
  failure(ledgerError("claim", "Claimable head without a Thread lane")),
@@ -611,6 +642,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
611
642
  }
612
643
  const producerEpoch = decodeProducerEpoch(lane.producerEpoch + 1);
613
644
  const mintCounter = current.mintCounter + 1;
645
+
614
646
  const ownership: StoredOwnership = {
615
647
  attemptId: decodeAttemptId(`attempt-memory-${mintCounter}`),
616
648
  ownershipToken: decodeOwnershipToken(`ownership-memory-${mintCounter}`),
@@ -618,13 +650,17 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
618
650
  ownerProducerId: request.producerId,
619
651
  leaseExpiresAtMillis: nowMillis + leaseMillis,
620
652
  };
653
+
621
654
  const row: SubmissionRow =
622
655
  head.row.state === "ready" ? { ...head.row, state: "running" } : head.row;
656
+
623
657
  const next = withSubmission(current, { ...head, row, ownership });
658
+
624
659
  const lanes = new Map(next.lanes).set(request.threadId, {
625
660
  nextQueueSequence: lane.nextQueueSequence,
626
661
  producerEpoch: lane.producerEpoch + 1,
627
662
  });
663
+
628
664
  return [
629
665
  success(
630
666
  Option.some(
@@ -642,7 +678,9 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
642
678
  ];
643
679
  },
644
680
  );
681
+
645
682
  if (decision._tag === "failure") return yield* decision.error;
683
+
646
684
  return decision.value;
647
685
  }),
648
686
  );
@@ -653,12 +691,14 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
653
691
  Effect.gen(function* () {
654
692
  const request = yield* validate(RenewOwnershipRequest, "renewOwnership", unvalidated);
655
693
  const nowMillis = yield* Clock.currentTimeMillis;
694
+
656
695
  const decision = yield* Ref.modify(
657
696
  state,
658
697
  (
659
698
  current,
660
699
  ): readonly [Decision<OwnershipRenewal, OwnershipLost | LedgerError>, LedgerState] => {
661
700
  const stored = current.submissions.get(request.submissionId);
701
+
662
702
  if (stored === undefined) {
663
703
  return [
664
704
  failure(
@@ -670,10 +710,12 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
670
710
  if (stored.ownership === undefined || !ownsLane(stored, request.ownershipToken)) {
671
711
  return [failure(ownershipLost(current, stored)), current];
672
712
  }
713
+
673
714
  const ownership: StoredOwnership = {
674
715
  ...stored.ownership,
675
716
  leaseExpiresAtMillis: nowMillis + leaseMillis,
676
717
  };
718
+
677
719
  return [
678
720
  success(
679
721
  OwnershipRenewal.make({
@@ -685,7 +727,9 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
685
727
  ];
686
728
  },
687
729
  );
730
+
688
731
  if (decision._tag === "failure") return yield* decision.error;
732
+
689
733
  return decision.value;
690
734
  }),
691
735
  );
@@ -695,10 +739,12 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
695
739
  )((unvalidated) =>
696
740
  Effect.gen(function* () {
697
741
  const request = yield* validate(ReleaseOwnershipRequest, "releaseOwnership", unvalidated);
742
+
698
743
  const decision = yield* Ref.modify(
699
744
  state,
700
745
  (current): readonly [Decision<void, OwnershipLost | LedgerError>, LedgerState] => {
701
746
  const stored = current.submissions.get(request.submissionId);
747
+
702
748
  if (stored === undefined) {
703
749
  return [
704
750
  failure(
@@ -710,12 +756,14 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
710
756
  if (!ownsLane(stored, request.ownershipToken)) {
711
757
  return [failure(ownershipLost(current, stored)), current];
712
758
  }
759
+
713
760
  return [
714
761
  success(undefined),
715
762
  withSubmission(current, { ...stored, ownership: undefined }),
716
763
  ];
717
764
  },
718
765
  );
766
+
719
767
  if (decision._tag === "failure") return yield* decision.error;
720
768
  }),
721
769
  );
@@ -725,10 +773,12 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
725
773
  )((unvalidated) =>
726
774
  Effect.gen(function* () {
727
775
  const request = yield* validate(MarkInputAppliedRequest, "markInputApplied", unvalidated);
776
+
728
777
  const decision = yield* Ref.modify(
729
778
  state,
730
779
  (current): readonly [Decision<void, OwnershipLost | LedgerError>, LedgerState] => {
731
780
  const stored = current.submissions.get(request.submissionId);
781
+
732
782
  if (stored === undefined) {
733
783
  return [
734
784
  failure(
@@ -740,20 +790,24 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
740
790
  if (!ownsLane(stored, request.ownershipToken)) {
741
791
  return [failure(ownershipLost(current, stored)), current];
742
792
  }
793
+
743
794
  const marker = InputAppliedMarker.make({
744
795
  recordId: request.recordId,
745
796
  sequence: request.sequence,
746
797
  });
798
+
747
799
  const row: SubmissionRow =
748
800
  STATE_RANK[stored.row.state] < STATE_RANK["input-applied"]
749
801
  ? { ...stored.row, state: "input-applied" }
750
802
  : stored.row;
803
+
751
804
  return [
752
805
  success(undefined),
753
806
  withSubmission(current, { ...stored, row, inputApplied: marker }),
754
807
  ];
755
808
  },
756
809
  );
810
+
757
811
  if (decision._tag === "failure") return yield* decision.error;
758
812
  }),
759
813
  );
@@ -763,6 +817,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
763
817
  )((unvalidated) =>
764
818
  Effect.gen(function* () {
765
819
  const request = yield* validate(SettlementReservation, "reserveSettlement", unvalidated);
820
+
766
821
  const decision = yield* Ref.modify(
767
822
  state,
768
823
  (
@@ -772,6 +827,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
772
827
  LedgerState,
773
828
  ] => {
774
829
  const stored = current.submissions.get(request.submissionId);
830
+
775
831
  if (stored === undefined) {
776
832
  return [
777
833
  failure(
@@ -780,11 +836,13 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
780
836
  current,
781
837
  ];
782
838
  }
839
+
783
840
  // A `joined` Submission settles WITH its host (plan §2.5) and its lane is never
784
841
  // worker-claimable, so no ownership token can exist for it: the recorded host
785
842
  // linkage authorizes the reservation and the presented token is not consulted.
786
843
  const joinedSettlement =
787
844
  stored.row.state === "joined" && stored.joinedHostSubmissionId !== undefined;
845
+
788
846
  // P7 §7(c): an aborted, never-claimed, still-queued Submission likewise has no
789
847
  // live ownership to fence against — its durable abort intent authorizes exactly
790
848
  // its ABORTED settlement (`terminalizing` is the same pass's crash replay). Every
@@ -794,6 +852,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
794
852
  stored.abortIntent !== undefined &&
795
853
  stored.ownership === undefined &&
796
854
  (stored.row.state === "ready" || stored.row.state === "terminalizing");
855
+
797
856
  if (
798
857
  !joinedSettlement &&
799
858
  !queuedAbortSettlement &&
@@ -802,6 +861,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
802
861
  return [failure(ownershipLost(current, stored)), current];
803
862
  }
804
863
  const existing = stored.reservation;
864
+
805
865
  if (existing !== undefined) {
806
866
  if (
807
867
  existing.settlementId !== request.settlementId ||
@@ -818,6 +878,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
818
878
  current,
819
879
  ];
820
880
  }
881
+
821
882
  return [
822
883
  success(
823
884
  ReservedSettlement.make({
@@ -832,6 +893,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
832
893
  current,
833
894
  ];
834
895
  }
896
+
835
897
  const reservation: StoredReservation = {
836
898
  settlementId: request.settlementId,
837
899
  outcome: request.outcome,
@@ -839,10 +901,12 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
839
901
  recordDigest: request.recordDigest,
840
902
  finalizedAtMillis: undefined,
841
903
  };
904
+
842
905
  const row: SubmissionRow =
843
906
  STATE_RANK[stored.row.state] < STATE_RANK.terminalizing
844
907
  ? { ...stored.row, state: "terminalizing" }
845
908
  : stored.row;
909
+
846
910
  return [
847
911
  success(
848
912
  ReservedSettlement.make({
@@ -858,7 +922,9 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
858
922
  ];
859
923
  },
860
924
  );
925
+
861
926
  if (decision._tag === "failure") return yield* decision.error;
927
+
862
928
  return decision.value;
863
929
  }),
864
930
  );
@@ -869,12 +935,14 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
869
935
  Effect.gen(function* () {
870
936
  const request = yield* validate(SettlementFinalization, "finalizeSettlement", unvalidated);
871
937
  const nowMillis = yield* Clock.currentTimeMillis;
938
+
872
939
  const decision = yield* Ref.modify(
873
940
  state,
874
941
  (
875
942
  current,
876
943
  ): readonly [Decision<Settlement, SettlementConflict | LedgerError>, LedgerState] => {
877
944
  const stored = current.submissions.get(request.submissionId);
945
+
878
946
  if (stored === undefined) {
879
947
  return [
880
948
  failure(
@@ -884,6 +952,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
884
952
  ];
885
953
  }
886
954
  const reservation = stored.reservation;
955
+
887
956
  if (reservation === undefined) {
888
957
  return [
889
958
  failure(
@@ -896,6 +965,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
896
965
  ];
897
966
  }
898
967
  const settlementFailure = settlementFailureFromRecord(reservation.record);
968
+
899
969
  if ((reservation.outcome === "failed") !== (settlementFailure !== undefined)) {
900
970
  return [
901
971
  failure(
@@ -933,12 +1003,14 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
933
1003
  current,
934
1004
  ];
935
1005
  }
1006
+
936
1007
  const next = withSubmission(current, {
937
1008
  ...stored,
938
1009
  row: { ...stored.row, state: "settled", settledOutcome: reservation.outcome },
939
1010
  ownership: undefined,
940
1011
  reservation: { ...reservation, finalizedAtMillis: nowMillis },
941
1012
  });
1013
+
942
1014
  return [
943
1015
  success(
944
1016
  Settlement.make({
@@ -954,7 +1026,9 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
954
1026
  ];
955
1027
  },
956
1028
  );
1029
+
957
1030
  if (decision._tag === "failure") return yield* decision.error;
1031
+
958
1032
  return decision.value;
959
1033
  }),
960
1034
  );
@@ -965,6 +1039,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
965
1039
  Effect.gen(function* () {
966
1040
  const request = yield* validate(AbortCommand, "requestAbort", unvalidated);
967
1041
  const nowMillis = yield* Clock.currentTimeMillis;
1042
+
968
1043
  const decision = yield* Ref.modify(
969
1044
  state,
970
1045
  (
@@ -974,6 +1049,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
974
1049
  LedgerState,
975
1050
  ] => {
976
1051
  const stored = current.submissions.get(request.submissionId);
1052
+
977
1053
  if (stored === undefined) {
978
1054
  return [
979
1055
  failure(ledgerError("requestAbort", `Unknown Submission ${request.submissionId}`)),
@@ -995,6 +1071,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
995
1071
  current,
996
1072
  ];
997
1073
  }
1074
+
998
1075
  return [
999
1076
  failure(
1000
1077
  JoinedToHost.make({
@@ -1017,6 +1094,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1017
1094
  current,
1018
1095
  ];
1019
1096
  }
1097
+
1020
1098
  return [
1021
1099
  failure(
1022
1100
  SettlementConflict.make({
@@ -1028,16 +1106,20 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1028
1106
  ];
1029
1107
  }
1030
1108
  if (stored.abortIntent !== undefined) return [success(stored.abortIntent), current];
1109
+
1031
1110
  const intent = AbortIntent.make({
1032
1111
  submissionId: request.submissionId,
1033
1112
  author: request.author,
1034
1113
  reason: request.reason,
1035
1114
  requestedAt: utc(nowMillis),
1036
1115
  });
1116
+
1037
1117
  return [success(intent), withSubmission(current, { ...stored, abortIntent: intent })];
1038
1118
  },
1039
1119
  );
1120
+
1040
1121
  if (decision._tag === "failure") return yield* decision.error;
1122
+
1041
1123
  return decision.value;
1042
1124
  }),
1043
1125
  );
@@ -1047,6 +1129,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1047
1129
  )((unvalidated) =>
1048
1130
  Effect.gen(function* () {
1049
1131
  const request = yield* validate(ClaimJoiningRequest, "claimJoining", unvalidated);
1132
+
1050
1133
  const decision = yield* Ref.modify(
1051
1134
  state,
1052
1135
  (
@@ -1056,6 +1139,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1056
1139
  LedgerState,
1057
1140
  ] => {
1058
1141
  const host = current.submissions.get(request.hostSubmissionId);
1142
+
1059
1143
  if (host === undefined) {
1060
1144
  return [
1061
1145
  failure(
@@ -1078,6 +1162,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1078
1162
  if (!ownsLane(host, request.ownershipToken)) {
1079
1163
  return [failure(ownershipLost(current, host)), current];
1080
1164
  }
1165
+
1081
1166
  const later = [...current.submissions.values()]
1082
1167
  .filter(
1083
1168
  (stored) =>
@@ -1085,8 +1170,10 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1085
1170
  stored.row.queueSequence > host.row.queueSequence,
1086
1171
  )
1087
1172
  .sort((left, right) => left.row.queueSequence - right.row.queueSequence);
1173
+
1088
1174
  const claims: Array<JoiningClaim> = [];
1089
1175
  const submissions = new Map(current.submissions);
1176
+
1090
1177
  for (const stored of later) {
1091
1178
  if (claims.length >= request.maxCount) break;
1092
1179
  // Rows already claimed by THIS host extend its contiguous prefix and are skipped;
@@ -1119,10 +1206,13 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1119
1206
  }),
1120
1207
  );
1121
1208
  }
1209
+
1122
1210
  return [success(claims), { ...current, submissions }];
1123
1211
  },
1124
1212
  );
1213
+
1125
1214
  if (decision._tag === "failure") return yield* decision.error;
1215
+
1126
1216
  return decision.value;
1127
1217
  }),
1128
1218
  );
@@ -1132,10 +1222,12 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1132
1222
  )((unvalidated) =>
1133
1223
  Effect.gen(function* () {
1134
1224
  const request = yield* validate(MarkJoinedRequest, "markJoined", unvalidated);
1225
+
1135
1226
  const decision = yield* Ref.modify(
1136
1227
  state,
1137
1228
  (current): readonly [Decision<void, OwnershipLost | LedgerError>, LedgerState] => {
1138
1229
  const stored = current.submissions.get(request.submissionId);
1230
+
1139
1231
  if (stored === undefined) {
1140
1232
  return [
1141
1233
  failure(ledgerError("markJoined", `Unknown Submission ${request.submissionId}`)),
@@ -1154,6 +1246,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1154
1246
  ];
1155
1247
  }
1156
1248
  const host = current.submissions.get(stored.joinedHostSubmissionId);
1249
+
1157
1250
  if (host === undefined) {
1158
1251
  return [
1159
1252
  failure(
@@ -1177,6 +1270,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1177
1270
  ) {
1178
1271
  return [success(undefined), current];
1179
1272
  }
1273
+
1180
1274
  return [
1181
1275
  failure(
1182
1276
  ledgerError(
@@ -1198,10 +1292,12 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1198
1292
  current,
1199
1293
  ];
1200
1294
  }
1295
+
1201
1296
  const marker = InputAppliedMarker.make({
1202
1297
  recordId: request.recordId,
1203
1298
  sequence: request.sequence,
1204
1299
  });
1300
+
1205
1301
  return [
1206
1302
  success(undefined),
1207
1303
  withSubmission(current, {
@@ -1212,6 +1308,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1212
1308
  ];
1213
1309
  },
1214
1310
  );
1311
+
1215
1312
  if (decision._tag === "failure") return yield* decision.error;
1216
1313
  }),
1217
1314
  );
@@ -1221,10 +1318,12 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1221
1318
  )((unvalidated) =>
1222
1319
  Effect.gen(function* () {
1223
1320
  const request = yield* validate(RevertJoiningRequest, "revertJoining", unvalidated);
1321
+
1224
1322
  const decision = yield* Ref.modify(
1225
1323
  state,
1226
1324
  (current): readonly [Decision<void, LedgerError>, LedgerState] => {
1227
1325
  const stored = current.submissions.get(request.submissionId);
1326
+
1228
1327
  if (stored === undefined) {
1229
1328
  return [
1230
1329
  failure(ledgerError("revertJoining", `Unknown Submission ${request.submissionId}`)),
@@ -1234,6 +1333,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1234
1333
  // Idempotent and recovery-only: only a still-`joining` Submission reverts; an
1235
1334
  // already-joined (or already-reverted) Submission is a no-op (DUR-016).
1236
1335
  if (stored.row.state !== "joining") return [success(undefined), current];
1336
+
1237
1337
  return [
1238
1338
  success(undefined),
1239
1339
  withSubmission(current, {
@@ -1244,6 +1344,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1244
1344
  ];
1245
1345
  },
1246
1346
  );
1347
+
1247
1348
  if (decision._tag === "failure") return yield* decision.error;
1248
1349
  }),
1249
1350
  );
@@ -1254,6 +1355,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1254
1355
  Effect.gen(function* () {
1255
1356
  const request = yield* validate(SuspendRequest, "suspend", unvalidated);
1256
1357
  const nowMillis = yield* Clock.currentTimeMillis;
1358
+
1257
1359
  const decision = yield* Ref.modify(
1258
1360
  state,
1259
1361
  (
@@ -1263,6 +1365,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1263
1365
  LedgerState,
1264
1366
  ] => {
1265
1367
  const stored = current.submissions.get(request.submissionId);
1368
+
1266
1369
  if (stored === undefined) {
1267
1370
  return [
1268
1371
  failure(ledgerError("suspend", `Unknown Submission ${request.submissionId}`)),
@@ -1281,6 +1384,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1281
1384
  current,
1282
1385
  ];
1283
1386
  }
1387
+
1284
1388
  return [
1285
1389
  failure(
1286
1390
  SettlementConflict.make({
@@ -1307,6 +1411,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1307
1411
  if (!ownsLane(stored, request.ownershipToken)) {
1308
1412
  return [failure(ownershipLost(current, stored)), current];
1309
1413
  }
1414
+
1310
1415
  // A covering event that raced ahead of the suspend transaction (an approval decision,
1311
1416
  // or a child settlement observed directly from the child's row in this single store)
1312
1417
  // resumes the caller immediately WITHOUT releasing the lane (plan §2.6, spec §12).
@@ -1319,9 +1424,11 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1319
1424
  (child) =>
1320
1425
  current.submissions.get(child.childSubmissionId)?.row.state === "settled",
1321
1426
  );
1427
+
1322
1428
  if (alreadyCovered) {
1323
1429
  return [success("resume-immediately" as const), current];
1324
1430
  }
1431
+
1325
1432
  return [
1326
1433
  success("suspended" as const),
1327
1434
  withSubmission(current, {
@@ -1333,7 +1440,9 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1333
1440
  ];
1334
1441
  },
1335
1442
  );
1443
+
1336
1444
  if (decision._tag === "failure") return yield* decision.error;
1445
+
1337
1446
  return decision.value;
1338
1447
  }),
1339
1448
  );
@@ -1347,7 +1456,9 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1347
1456
  "recordApprovalDecision",
1348
1457
  unvalidated,
1349
1458
  );
1459
+
1350
1460
  const nowMillis = yield* Clock.currentTimeMillis;
1461
+
1351
1462
  const decision = yield* Ref.modify(
1352
1463
  state,
1353
1464
  (
@@ -1357,6 +1468,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1357
1468
  LedgerState,
1358
1469
  ] => {
1359
1470
  const stored = current.submissions.get(command.submissionId);
1471
+
1360
1472
  if (stored === undefined) {
1361
1473
  return [
1362
1474
  failure(
@@ -1380,6 +1492,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1380
1492
  current,
1381
1493
  ];
1382
1494
  }
1495
+
1383
1496
  return [
1384
1497
  failure(
1385
1498
  SettlementConflict.make({
@@ -1391,6 +1504,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1391
1504
  ];
1392
1505
  }
1393
1506
  const existing = stored.approvalDecisions.get(command.toolCallId);
1507
+
1394
1508
  if (existing !== undefined) {
1395
1509
  if (existing.decision !== command.decision) {
1396
1510
  return [
@@ -1404,8 +1518,10 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1404
1518
  current,
1405
1519
  ];
1406
1520
  }
1521
+
1407
1522
  return [success(existing), current];
1408
1523
  }
1524
+
1409
1525
  const intent = ApprovalDecisionIntent.make({
1410
1526
  submissionId: command.submissionId,
1411
1527
  toolCallId: command.toolCallId,
@@ -1414,10 +1530,12 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1414
1530
  reason: command.reason,
1415
1531
  decidedAt: utc(nowMillis),
1416
1532
  });
1533
+
1417
1534
  const approvalDecisions = new Map(stored.approvalDecisions).set(
1418
1535
  command.toolCallId,
1419
1536
  intent,
1420
1537
  );
1538
+
1421
1539
  // Once every pending call of an ApprovalPending suspension is decided, the lane
1422
1540
  // wakes: suspended → input-applied (plan §2.6). A WaitingForChild suspension wakes
1423
1541
  // only through recordChildSettled.
@@ -1428,6 +1546,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1428
1546
  stored.suspension.reason.toolCallIds.every((toolCallId) =>
1429
1547
  approvalDecisions.has(toolCallId),
1430
1548
  );
1549
+
1431
1550
  return [
1432
1551
  success(intent),
1433
1552
  withSubmission(current, {
@@ -1439,7 +1558,9 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1439
1558
  ];
1440
1559
  },
1441
1560
  );
1561
+
1442
1562
  if (decision._tag === "failure") return yield* decision.error;
1563
+
1443
1564
  return decision.value;
1444
1565
  }),
1445
1566
  );
@@ -1449,10 +1570,12 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1449
1570
  )((unvalidated) =>
1450
1571
  Effect.gen(function* () {
1451
1572
  const request = yield* validate(MarkUnknownRequest, "markUnknown", unvalidated);
1573
+
1452
1574
  const decision = yield* Ref.modify(
1453
1575
  state,
1454
1576
  (current): readonly [Decision<void, SettlementConflict | LedgerError>, LedgerState] => {
1455
1577
  const stored = current.submissions.get(request.submissionId);
1578
+
1456
1579
  if (stored === undefined) {
1457
1580
  return [
1458
1581
  failure(ledgerError("markUnknown", `Unknown Submission ${request.submissionId}`)),
@@ -1471,6 +1594,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1471
1594
  current,
1472
1595
  ];
1473
1596
  }
1597
+
1474
1598
  return [
1475
1599
  failure(
1476
1600
  SettlementConflict.make({
@@ -1498,10 +1622,12 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1498
1622
  // set while the first recorded reason is kept.
1499
1623
  const existing = stored.unknownMark;
1500
1624
  const known = new Set(existing?.toolCallIds ?? []);
1625
+
1501
1626
  const merged = [
1502
1627
  ...(existing?.toolCallIds ?? []),
1503
1628
  ...request.toolCallIds.filter((toolCallId) => !known.has(toolCallId)),
1504
1629
  ];
1630
+
1505
1631
  return [
1506
1632
  success(undefined),
1507
1633
  withSubmission(current, {
@@ -1513,6 +1639,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1513
1639
  ];
1514
1640
  },
1515
1641
  );
1642
+
1516
1643
  if (decision._tag === "failure") return yield* decision.error;
1517
1644
  }),
1518
1645
  );
@@ -1525,7 +1652,9 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1525
1652
  "recordUnknownResolution",
1526
1653
  unvalidated,
1527
1654
  );
1655
+
1528
1656
  const nowMillis = yield* Clock.currentTimeMillis;
1657
+
1529
1658
  const decision = yield* Ref.modify(
1530
1659
  state,
1531
1660
  (
@@ -1538,6 +1667,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1538
1667
  LedgerState,
1539
1668
  ] => {
1540
1669
  const stored = current.submissions.get(command.submissionId);
1670
+
1541
1671
  if (stored === undefined) {
1542
1672
  return [
1543
1673
  failure(
@@ -1561,6 +1691,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1561
1691
  current,
1562
1692
  ];
1563
1693
  }
1694
+
1564
1695
  return [
1565
1696
  failure(
1566
1697
  SettlementConflict.make({
@@ -1572,6 +1703,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1572
1703
  ];
1573
1704
  }
1574
1705
  const existing = stored.unknownResolutions.get(command.toolCallId);
1706
+
1575
1707
  if (
1576
1708
  existing !== undefined &&
1577
1709
  !equivalentUnknownResolution(existing.intent.resolution, command.resolution)
@@ -1586,6 +1718,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1586
1718
  current,
1587
1719
  ];
1588
1720
  }
1721
+
1589
1722
  const intent =
1590
1723
  existing?.intent ??
1591
1724
  UnknownResolutionIntent.make({
@@ -1596,12 +1729,14 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1596
1729
  resolution: command.resolution,
1597
1730
  resolvedAt: utc(nowMillis),
1598
1731
  });
1732
+
1599
1733
  const unknownResolutions =
1600
1734
  existing !== undefined
1601
1735
  ? stored.unknownResolutions
1602
1736
  : new Map(stored.unknownResolutions).set(command.toolCallId, {
1603
1737
  intent,
1604
1738
  });
1739
+
1605
1740
  // The lane reopens only when EVERY marked open call has a durable resolution
1606
1741
  // intent: unknown → input-applied (DUR-017). Replays re-run the coverage check so
1607
1742
  // a recovering caller can wake the lane idempotently.
@@ -1611,6 +1746,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1611
1746
  stored.unknownMark.toolCallIds.every((toolCallId) =>
1612
1747
  unknownResolutions.has(toolCallId),
1613
1748
  );
1749
+
1614
1750
  return [
1615
1751
  success(intent),
1616
1752
  withSubmission(current, {
@@ -1622,7 +1758,9 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1622
1758
  ];
1623
1759
  },
1624
1760
  );
1761
+
1625
1762
  if (decision._tag === "failure") return yield* decision.error;
1763
+
1626
1764
  return decision.value;
1627
1765
  }),
1628
1766
  );
@@ -1636,10 +1774,12 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1636
1774
  "recordChildSettled",
1637
1775
  unvalidated,
1638
1776
  );
1777
+
1639
1778
  const decision = yield* Ref.modify(
1640
1779
  state,
1641
1780
  (current): readonly [Decision<ChildSettledOutcome, LedgerError>, LedgerState] => {
1642
1781
  const parent = current.submissions.get(request.parentSubmissionId);
1782
+
1643
1783
  if (parent === undefined) {
1644
1784
  return [
1645
1785
  failure(
@@ -1656,10 +1796,12 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1656
1796
  // narrow durable prefix that makes that ordering admissible; earlier states remain
1657
1797
  // a caller error.
1658
1798
  const child = current.submissions.get(request.childSubmissionId);
1799
+
1659
1800
  const announced =
1660
1801
  child !== undefined &&
1661
1802
  (child.row.state === "settled" ||
1662
1803
  (child.row.state === "terminalizing" && child.reservation !== undefined));
1804
+
1663
1805
  if (!announced) {
1664
1806
  return [
1665
1807
  failure(
@@ -1679,19 +1821,24 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1679
1821
  return [success("not-waiting" as const), current];
1680
1822
  }
1681
1823
  const children = parent.suspension.reason.children;
1824
+
1682
1825
  if (!children.some((entry) => entry.childSubmissionId === request.childSubmissionId)) {
1683
1826
  return [success("not-waiting" as const), current];
1684
1827
  }
1828
+
1685
1829
  // Every listed child must be either finalized or canonically announced from the
1686
1830
  // exact terminalizing reservation. Replays re-run this coverage check idempotently.
1687
1831
  const allSettled = children.every((entry) => {
1688
1832
  const listed = current.submissions.get(entry.childSubmissionId);
1833
+
1689
1834
  return (
1690
1835
  listed?.row.state === "settled" ||
1691
1836
  (listed?.row.state === "terminalizing" && listed.reservation !== undefined)
1692
1837
  );
1693
1838
  });
1839
+
1694
1840
  if (!allSettled) return [success("still-waiting" as const), current];
1841
+
1695
1842
  return [
1696
1843
  success("woken" as const),
1697
1844
  withSubmission(current, {
@@ -1702,7 +1849,9 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1702
1849
  ];
1703
1850
  },
1704
1851
  );
1852
+
1705
1853
  if (decision._tag === "failure") return yield* decision.error;
1854
+
1706
1855
  return decision.value;
1707
1856
  }),
1708
1857
  );
@@ -1716,7 +1865,9 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1716
1865
  "reserveChildBudget",
1717
1866
  unvalidated,
1718
1867
  );
1868
+
1719
1869
  const nowMillis = yield* Clock.currentTimeMillis;
1870
+
1720
1871
  const decision = yield* Ref.modify(
1721
1872
  state,
1722
1873
  (
@@ -1726,6 +1877,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1726
1877
  LedgerState,
1727
1878
  ] => {
1728
1879
  const existing = current.childReservations.get(request.reservationId);
1880
+
1729
1881
  if (existing !== undefined) {
1730
1882
  // Identical replays short-circuit before the fence, mirroring reserveSettlement:
1731
1883
  // a replay creates nothing, so a recovering caller resumes rather than duplicates.
@@ -1734,6 +1886,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1734
1886
  existing.parentToolCallId === request.parentToolCallId &&
1735
1887
  existing.allocationDigest === request.allocationDigest &&
1736
1888
  equivalentPersistedJson(existing.allocation, request.allocation);
1889
+
1737
1890
  if (!identical) {
1738
1891
  return [
1739
1892
  failure(
@@ -1747,6 +1900,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1747
1900
  current,
1748
1901
  ];
1749
1902
  }
1903
+
1750
1904
  return [
1751
1905
  success(
1752
1906
  ReservedChildBudget.make({
@@ -1775,6 +1929,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1775
1929
  }
1776
1930
  }
1777
1931
  const parent = current.submissions.get(request.parentSubmissionId);
1932
+
1778
1933
  if (parent === undefined) {
1779
1934
  return [
1780
1935
  failure(
@@ -1791,6 +1946,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1791
1946
  if (!ownsLane(parent, request.ownershipToken)) {
1792
1947
  return [failure(ownershipLost(current, parent)), current];
1793
1948
  }
1949
+
1794
1950
  const reservation: StoredChildReservation = {
1795
1951
  reservationId: request.reservationId,
1796
1952
  parentSubmissionId: request.parentSubmissionId,
@@ -1804,6 +1960,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1804
1960
  releaseBeganAtMillis: undefined,
1805
1961
  releasedAtMillis: undefined,
1806
1962
  };
1963
+
1807
1964
  return [
1808
1965
  success(
1809
1966
  ReservedChildBudget.make({
@@ -1815,7 +1972,9 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1815
1972
  ];
1816
1973
  },
1817
1974
  );
1975
+
1818
1976
  if (decision._tag === "failure") return yield* decision.error;
1977
+
1819
1978
  return decision.value;
1820
1979
  }),
1821
1980
  );
@@ -1828,6 +1987,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1828
1987
  "attachChildToReservation",
1829
1988
  unvalidated,
1830
1989
  );
1990
+
1831
1991
  const decision = yield* Ref.modify(
1832
1992
  state,
1833
1993
  (
@@ -1840,6 +2000,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1840
2000
  LedgerState,
1841
2001
  ] => {
1842
2002
  const reservation = current.childReservations.get(request.reservationId);
2003
+
1843
2004
  if (reservation === undefined) {
1844
2005
  return [
1845
2006
  failure(
@@ -1856,6 +2017,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1856
2017
  if (reservation.childSubmissionId === request.childSubmissionId) {
1857
2018
  return [success(toReservationSnapshot(reservation)), current];
1858
2019
  }
2020
+
1859
2021
  return [
1860
2022
  failure(
1861
2023
  ChildReservationConflict.make({
@@ -1868,6 +2030,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1868
2030
  ];
1869
2031
  }
1870
2032
  const parent = current.submissions.get(reservation.parentSubmissionId);
2033
+
1871
2034
  if (parent === undefined) {
1872
2035
  return [
1873
2036
  failure(
@@ -1907,17 +2070,21 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1907
2070
  current,
1908
2071
  ];
1909
2072
  }
2073
+
1910
2074
  const attached: StoredChildReservation = {
1911
2075
  ...reservation,
1912
2076
  childSubmissionId: request.childSubmissionId,
1913
2077
  };
2078
+
1914
2079
  return [
1915
2080
  success(toReservationSnapshot(attached)),
1916
2081
  withChildReservation(current, attached),
1917
2082
  ];
1918
2083
  },
1919
2084
  );
2085
+
1920
2086
  if (decision._tag === "failure") return yield* decision.error;
2087
+
1921
2088
  return decision.value;
1922
2089
  }),
1923
2090
  );
@@ -1930,7 +2097,9 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1930
2097
  "beginChildBudgetRelease",
1931
2098
  unvalidated,
1932
2099
  );
2100
+
1933
2101
  const nowMillis = yield* Clock.currentTimeMillis;
2102
+
1934
2103
  const decision = yield* Ref.modify(
1935
2104
  state,
1936
2105
  (
@@ -1940,6 +2109,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1940
2109
  LedgerState,
1941
2110
  ] => {
1942
2111
  const reservation = current.childReservations.get(request.reservationId);
2112
+
1943
2113
  if (reservation === undefined) {
1944
2114
  return [
1945
2115
  failure(
@@ -1960,6 +2130,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1960
2130
  ) {
1961
2131
  return [success(toReservationSnapshot(reservation)), current];
1962
2132
  }
2133
+
1963
2134
  return [
1964
2135
  failure(
1965
2136
  ChildReservationConflict.make({
@@ -1972,19 +2143,23 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1972
2143
  current,
1973
2144
  ];
1974
2145
  }
2146
+
1975
2147
  const frozen: StoredChildReservation = {
1976
2148
  ...reservation,
1977
2149
  status: "releasePending",
1978
2150
  accounting: request.accounting,
1979
2151
  releaseBeganAtMillis: nowMillis,
1980
2152
  };
2153
+
1981
2154
  return [
1982
2155
  success(toReservationSnapshot(frozen)),
1983
2156
  withChildReservation(current, frozen),
1984
2157
  ];
1985
2158
  },
1986
2159
  );
2160
+
1987
2161
  if (decision._tag === "failure") return yield* decision.error;
2162
+
1988
2163
  return decision.value;
1989
2164
  }),
1990
2165
  );
@@ -1998,7 +2173,9 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
1998
2173
  "releaseChildBudget",
1999
2174
  unvalidated,
2000
2175
  );
2176
+
2001
2177
  const nowMillis = yield* Clock.currentTimeMillis;
2178
+
2002
2179
  const decision = yield* Ref.modify(
2003
2180
  state,
2004
2181
  (
@@ -2008,6 +2185,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
2008
2185
  LedgerState,
2009
2186
  ] => {
2010
2187
  const reservation = current.childReservations.get(request.reservationId);
2188
+
2011
2189
  if (reservation === undefined) {
2012
2190
  return [
2013
2191
  failure(
@@ -2037,18 +2215,22 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
2037
2215
  current,
2038
2216
  ];
2039
2217
  }
2218
+
2040
2219
  const released: StoredChildReservation = {
2041
2220
  ...reservation,
2042
2221
  status: "released",
2043
2222
  releasedAtMillis: nowMillis,
2044
2223
  };
2224
+
2045
2225
  return [
2046
2226
  success(toReservationSnapshot(released)),
2047
2227
  withChildReservation(current, released),
2048
2228
  ];
2049
2229
  },
2050
2230
  );
2231
+
2051
2232
  if (decision._tag === "failure") return yield* decision.error;
2233
+
2052
2234
  return decision.value;
2053
2235
  }),
2054
2236
  );
@@ -2066,6 +2248,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
2066
2248
  : left.row.queueSequence - right.row.queueSequence,
2067
2249
  )
2068
2250
  .map((stored) => toSnapshot(stored.row));
2251
+
2069
2252
  return Stream.fromIterable(snapshots);
2070
2253
  }),
2071
2254
  ),
@@ -2080,14 +2263,17 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
2080
2263
  "loadRecoverySnapshot",
2081
2264
  unvalidated,
2082
2265
  );
2266
+
2083
2267
  const current = yield* Ref.get(state);
2084
2268
  const stored = current.submissions.get(request.submissionId);
2269
+
2085
2270
  if (stored === undefined) {
2086
2271
  return yield* ledgerError(
2087
2272
  "loadRecoverySnapshot",
2088
2273
  `Unknown Submission ${request.submissionId}`,
2089
2274
  );
2090
2275
  }
2276
+
2091
2277
  const joins = [...current.submissions.values()]
2092
2278
  .filter((candidate) => candidate.joinedHostSubmissionId === request.submissionId)
2093
2279
  .sort((left, right) => left.row.queueSequence - right.row.queueSequence)
@@ -2098,11 +2284,13 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
2098
2284
  hostSubmissionId: request.submissionId,
2099
2285
  }),
2100
2286
  );
2287
+
2101
2288
  const byToolCallId = <A extends { readonly toolCallId: ToolCallId }>(
2102
2289
  left: A,
2103
2290
  right: A,
2104
2291
  ): number =>
2105
2292
  left.toolCallId < right.toolCallId ? -1 : left.toolCallId > right.toolCallId ? 1 : 0;
2293
+
2106
2294
  // Parent-side subagent view: this Submission's child budget reservations in parent Tool
2107
2295
  // Call order, plus each attached child's current lane state (a disposable derived view;
2108
2296
  // the canonical records stay the recovery truth, DUR-015).
@@ -2115,10 +2303,13 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
2115
2303
  ? 1
2116
2304
  : 0,
2117
2305
  );
2306
+
2118
2307
  const childAttachments: Array<ChildAttachmentSnapshot> = [];
2308
+
2119
2309
  for (const reservation of childReservations) {
2120
2310
  if (reservation.childSubmissionId === undefined) continue;
2121
2311
  const child = current.submissions.get(reservation.childSubmissionId);
2312
+
2122
2313
  if (child === undefined) continue;
2123
2314
  childAttachments.push(
2124
2315
  ChildAttachmentSnapshot.make({
@@ -2131,6 +2322,7 @@ const makeSubmissionLedger = (options: MemorySubmissionLedgerOptions = {}) =>
2131
2322
  }),
2132
2323
  );
2133
2324
  }
2325
+
2134
2326
  return RecoverySnapshot.make({
2135
2327
  submission: toSnapshot(stored.row),
2136
2328
  joins,