@open-agent-toolkit/cli 0.2.72 → 0.2.73

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.
@@ -6,13 +6,13 @@ import { pathToFileURL } from 'node:url';
6
6
 
7
7
  import { hashCanonicalJson, hashFile, sha256 } from './lib/canonical-json.mjs';
8
8
  import {
9
- approvalFingerprintInput,
10
9
  isDigest,
11
10
  isObject,
12
11
  issue,
13
12
  profiles,
14
13
  validateArtifactShape,
15
14
  } from './lib/contracts.mjs';
15
+ import { normalizeManifestRouting } from './lib/routing.mjs';
16
16
  import {
17
17
  assertCanonicalRoot,
18
18
  assertSafeExistingPath,
@@ -40,7 +40,6 @@ const requiredPasses = {
40
40
  'reconciliation',
41
41
  'redundant-gather',
42
42
  'redundant-verification',
43
- 'contradiction-resolution',
44
43
  ],
45
44
  };
46
45
 
@@ -148,14 +147,24 @@ async function readManagedJson(
148
147
 
149
148
  function collectReferences(manifest, ledger) {
150
149
  const references = [];
151
- for (const reference of manifest?.artifacts ?? []) references.push(reference);
152
- for (const reference of ledger?.inputArtifacts ?? [])
150
+ for (const reference of Array.isArray(manifest?.artifacts)
151
+ ? manifest.artifacts
152
+ : [])
153
153
  references.push(reference);
154
- for (const evidence of ledger?.evidence ?? []) {
154
+ for (const reference of Array.isArray(ledger?.inputArtifacts)
155
+ ? ledger.inputArtifacts
156
+ : [])
157
+ references.push(reference);
158
+ for (const evidence of Array.isArray(ledger?.evidence)
159
+ ? ledger.evidence
160
+ : []) {
155
161
  if (evidence.provenance) references.push(evidence.provenance);
156
162
  }
157
- for (const claim of ledger?.claims ?? []) {
158
- for (const reference of claim.derivedFrom ?? []) references.push(reference);
163
+ for (const claim of Array.isArray(ledger?.claims) ? ledger.claims : []) {
164
+ for (const reference of Array.isArray(claim?.derivedFrom)
165
+ ? claim.derivedFrom
166
+ : [])
167
+ references.push(reference);
159
168
  }
160
169
  return references;
161
170
  }
@@ -698,6 +707,34 @@ function gapNamesMode(gap, mode) {
698
707
  );
699
708
  }
700
709
 
710
+ function gapNamesConditionalLane(gap, waveId, laneId) {
711
+ return (
712
+ (gap.code === 'PASS_FAILED' || gap.code === 'PASS_OMITTED') &&
713
+ gap.material === true &&
714
+ gap.waveId === waveId &&
715
+ gap.laneId === laneId
716
+ );
717
+ }
718
+
719
+ function gapHasLaneIdentity(gap) {
720
+ return gap.waveId !== undefined || gap.laneId !== undefined;
721
+ }
722
+
723
+ function legacyGapNamesSingletonWave(gap, wave, routing) {
724
+ if (gapHasLaneIdentity(gap) || wave.lanes?.length !== 1) return false;
725
+ const matchingWaves = (routing?.waves ?? []).filter(
726
+ (candidate) => candidate.mode === wave.mode,
727
+ );
728
+ return matchingWaves.length === 1 && gapNamesMode(gap, wave.mode);
729
+ }
730
+
731
+ function gapNamesApprovedLane(gap, wave, laneId, routing) {
732
+ return (
733
+ gapNamesConditionalLane(gap, wave.waveId, laneId) ||
734
+ (!wave.conditional && legacyGapNamesSingletonWave(gap, wave, routing))
735
+ );
736
+ }
737
+
701
738
  function artifactIsComplete(artifact) {
702
739
  return (
703
740
  (!('status' in artifact) || artifact.status === 'complete') &&
@@ -723,9 +760,206 @@ function laneWaveMatches(wave, value) {
723
760
  return reviewWaveMode[value.reviewKind] === wave.mode;
724
761
  }
725
762
 
726
- function validateApprovedLanes(manifest, artifactsById, errors) {
763
+ function artifactLaneId(value) {
764
+ return value.kind === 'recon.raw-dossier'
765
+ ? value.laneId
766
+ : value.kind === 'recon.review-result'
767
+ ? value.reviewerLane
768
+ : null;
769
+ }
770
+
771
+ function conditionPredicateSatisfied(condition, evidenceArtifacts) {
772
+ if (condition.predicate === 'insufficient-evidence') {
773
+ return evidenceArtifacts.some(
774
+ (artifact) =>
775
+ (artifact.gaps ?? []).length > 0 ||
776
+ (artifact.unresolvedIssues ?? []).length > 0 ||
777
+ (artifact.coverageFindings ?? []).some(
778
+ (finding) => finding.material === true,
779
+ ),
780
+ );
781
+ }
782
+ if (condition.predicate === 'unresolved-material-challenge') {
783
+ return evidenceArtifacts.some(
784
+ (artifact) =>
785
+ artifact.reviewKind === 'adversarial' &&
786
+ (artifact.dispositions ?? []).some(
787
+ (disposition) => disposition.disposition === 'challenged',
788
+ ) &&
789
+ (artifact.unresolvedIssues ?? []).length > 0,
790
+ );
791
+ }
792
+ return false;
793
+ }
794
+
795
+ function validateConditionalRouting(
796
+ manifest,
797
+ routing,
798
+ artifactsById,
799
+ artifactsByPath,
800
+ errors,
801
+ ) {
802
+ const activeWaveIds = new Set();
803
+ const inactiveWaveIds = new Set();
804
+ if (manifest.schemaVersion !== 2) {
805
+ return { activeWaveIds, inactiveWaveIds };
806
+ }
807
+ const wavesById = new Map(
808
+ (routing?.waves ?? []).map((wave) => [wave.waveId, wave]),
809
+ );
810
+ const lanesById = new Map();
811
+ for (const wave of routing?.waves ?? []) {
812
+ for (const lane of wave.lanes ?? []) lanesById.set(lane.laneId, wave);
813
+ }
814
+ const outcomes = new Map(
815
+ (manifest.conditionOutcomes ?? [])
816
+ .filter(isObject)
817
+ .map((outcome) => [outcome.conditionId, outcome]),
818
+ );
819
+ const knownConditionIds = new Set(
820
+ (routing?.conditions ?? []).map((condition) => condition.conditionId),
821
+ );
822
+ for (const outcome of manifest.conditionOutcomes ?? []) {
823
+ if (isObject(outcome) && !knownConditionIds.has(outcome.conditionId)) {
824
+ errors.push(
825
+ issue(
826
+ 'UNKNOWN_CONDITION_OUTCOME',
827
+ `Outcome references unknown condition ${outcome.conditionId}`,
828
+ '$.conditionOutcomes',
829
+ ),
830
+ );
831
+ }
832
+ }
833
+ for (const condition of routing?.conditions ?? []) {
834
+ const outcome = outcomes.get(condition.conditionId);
835
+ const destination = wavesById.get(condition.destinationWaveId);
836
+ if (!outcome) {
837
+ errors.push(
838
+ issue(
839
+ 'MISSING_CONDITION_OUTCOME',
840
+ `Condition ${condition.conditionId} lacks a finalized disposition`,
841
+ `condition:${condition.conditionId}`,
842
+ ),
843
+ );
844
+ if (destination) inactiveWaveIds.add(destination.waveId);
845
+ continue;
846
+ }
847
+ const evidenceArtifacts = [];
848
+ const evidencedPredecessors = new Set();
849
+ let rejectedConditionEvidence = false;
850
+ for (const reference of outcome.evidence ?? []) {
851
+ const entry = artifactsByPath.get(reference.path);
852
+ const declared = (manifest.artifacts ?? []).some((artifactReference) =>
853
+ sameReference(artifactReference, reference),
854
+ );
855
+ if (!declared || !entry || !sameReference(entry.reference, reference)) {
856
+ rejectedConditionEvidence = true;
857
+ errors.push(
858
+ issue(
859
+ 'INVALID_CONDITION_EVIDENCE',
860
+ `Condition ${condition.conditionId} evidence is not an exact validated artifact`,
861
+ reference.path,
862
+ ),
863
+ );
864
+ continue;
865
+ }
866
+ const artifact = entry.value;
867
+ if (artifact.runId !== manifest.run.id) {
868
+ rejectedConditionEvidence = true;
869
+ errors.push(
870
+ issue(
871
+ 'CONDITION_EVIDENCE_RUN_MISMATCH',
872
+ `Condition ${condition.conditionId} evidence belongs to another run`,
873
+ reference.path,
874
+ ),
875
+ );
876
+ continue;
877
+ }
878
+ const wave = lanesById.get(artifactLaneId(artifact));
879
+ if (!wave || !condition.afterWaveIds.includes(wave.waveId)) {
880
+ rejectedConditionEvidence = true;
881
+ errors.push(
882
+ issue(
883
+ 'INVALID_CONDITION_EVIDENCE',
884
+ `Condition ${condition.conditionId} evidence is not from an approved predecessor`,
885
+ reference.path,
886
+ ),
887
+ );
888
+ continue;
889
+ }
890
+ if (!artifactIsComplete(artifact)) {
891
+ rejectedConditionEvidence = true;
892
+ errors.push(
893
+ issue(
894
+ 'INCOMPLETE_CONDITION_PREDECESSOR',
895
+ `Condition ${condition.conditionId} requires completed predecessor evidence`,
896
+ reference.path,
897
+ ),
898
+ );
899
+ continue;
900
+ }
901
+ evidenceArtifacts.push(artifact);
902
+ evidencedPredecessors.add(wave.waveId);
903
+ }
904
+ if (
905
+ outcome.disposition === 'triggered' &&
906
+ !rejectedConditionEvidence &&
907
+ condition.afterWaveIds.some(
908
+ (waveId) => !evidencedPredecessors.has(waveId),
909
+ )
910
+ ) {
911
+ errors.push(
912
+ issue(
913
+ 'INCOMPLETE_CONDITION_PREDECESSOR',
914
+ `Condition ${condition.conditionId} does not bind complete evidence from every predecessor`,
915
+ `condition:${condition.conditionId}`,
916
+ ),
917
+ );
918
+ }
919
+ if (
920
+ outcome.disposition === 'triggered' &&
921
+ !rejectedConditionEvidence &&
922
+ !conditionPredicateSatisfied(condition, evidenceArtifacts)
923
+ ) {
924
+ errors.push(
925
+ issue(
926
+ 'CONDITION_PREDICATE_UNSATISFIED',
927
+ `Condition ${condition.conditionId} lacks concrete typed predicate evidence`,
928
+ `condition:${condition.conditionId}`,
929
+ ),
930
+ );
931
+ }
932
+ if (outcome.disposition === 'triggered') {
933
+ activeWaveIds.add(condition.destinationWaveId);
934
+ } else {
935
+ inactiveWaveIds.add(condition.destinationWaveId);
936
+ }
937
+ }
938
+ for (const { value } of artifactsById.values()) {
939
+ if (value.runId !== manifest.run.id) continue;
940
+ const wave = lanesById.get(artifactLaneId(value));
941
+ if (wave?.conditional && !activeWaveIds.has(wave.waveId)) {
942
+ errors.push(
943
+ issue(
944
+ 'INACTIVE_CONDITIONAL_ARTIFACT',
945
+ `Inactive conditional wave ${wave.waveId} cannot contribute artifacts`,
946
+ value.id,
947
+ ),
948
+ );
949
+ }
950
+ }
951
+ return { activeWaveIds, inactiveWaveIds };
952
+ }
953
+
954
+ function validateApprovedLanes(
955
+ manifest,
956
+ routing,
957
+ artifactsById,
958
+ conditionalState,
959
+ errors,
960
+ ) {
727
961
  const lanes = new Map();
728
- for (const wave of manifest.execution?.waves ?? []) {
962
+ for (const wave of routing?.waves ?? []) {
729
963
  for (const lane of wave.lanes ?? []) {
730
964
  lanes.set(lane.laneId, { wave, lane });
731
965
  }
@@ -733,12 +967,7 @@ function validateApprovedLanes(manifest, artifactsById, errors) {
733
967
  const written = new Set();
734
968
  for (const [id, { reference, value }] of artifactsById) {
735
969
  if (value.runId !== manifest.run.id) continue;
736
- const laneId =
737
- value.kind === 'recon.raw-dossier'
738
- ? value.laneId
739
- : value.kind === 'recon.review-result'
740
- ? value.reviewerLane
741
- : null;
970
+ const laneId = artifactLaneId(value);
742
971
  if (laneId === null) continue;
743
972
  const approved = lanes.get(laneId);
744
973
  if (
@@ -772,17 +1001,23 @@ function validateApprovedLanes(manifest, artifactsById, errors) {
772
1001
  }
773
1002
  for (const [laneId, { wave }] of lanes) {
774
1003
  // The canonical ledger is the compile outcome; it carries no lane identity.
775
- if (wave.conditional || wave.mode === 'compile' || written.has(laneId)) {
1004
+ if (
1005
+ (wave.conditional && !conditionalState.activeWaveIds.has(wave.waveId)) ||
1006
+ wave.mode === 'compile' ||
1007
+ written.has(laneId)
1008
+ ) {
776
1009
  continue;
777
1010
  }
778
- const hasOutcomeEvidence = (manifest.gaps ?? []).some((gap) =>
779
- gapNamesMode(gap, wave.mode),
1011
+ const outcomeGap = (manifest.gaps ?? []).find((gap) =>
1012
+ gapNamesApprovedLane(gap, wave, laneId, routing),
780
1013
  );
781
- if (!hasOutcomeEvidence) {
1014
+ if (!outcomeGap) {
782
1015
  errors.push(
783
1016
  issue(
784
1017
  'MISSING_LANE_OUTCOME',
785
- `Approved lane ${laneId} has neither a result nor a material ${wave.mode} outcome gap`,
1018
+ wave.conditional
1019
+ ? `Activated conditional wave ${wave.waveId} lane ${laneId} has neither a result nor a distinct material outcome gap naming both exact identities`
1020
+ : `Approved lane ${laneId} has neither a result nor a material outcome gap naming its exact wave and lane identities`,
786
1021
  `lane:${laneId}`,
787
1022
  ),
788
1023
  );
@@ -790,10 +1025,99 @@ function validateApprovedLanes(manifest, artifactsById, errors) {
790
1025
  }
791
1026
  }
792
1027
 
793
- function collectCompletePasses(artifactsById, runId) {
1028
+ function validateThoroughGatherLedgerInputs(
1029
+ manifest,
1030
+ ledger,
1031
+ routing,
1032
+ artifactsById,
1033
+ errors,
1034
+ ) {
1035
+ if (manifest.run.requestedProfile !== 'thorough') return;
1036
+
1037
+ const ledgerInputs = new Set(
1038
+ ledger.inputArtifacts.map(
1039
+ (reference) => `${reference.path}:${reference.digest}`,
1040
+ ),
1041
+ );
1042
+ const missing = [];
1043
+ for (const wave of routing.waves) {
1044
+ if (!['gather', 'redundant-gather'].includes(wave.mode)) continue;
1045
+ for (const lane of wave.lanes) {
1046
+ const completeDossiers = [...artifactsById.values()].filter(
1047
+ ({ value }) =>
1048
+ value.runId === manifest.run.id &&
1049
+ value.kind === 'recon.raw-dossier' &&
1050
+ value.waveId === wave.waveId &&
1051
+ value.laneId === lane.laneId &&
1052
+ artifactIsComplete(value),
1053
+ );
1054
+ if (completeDossiers.length === 0) continue;
1055
+ if (
1056
+ !completeDossiers.some(({ reference }) =>
1057
+ ledgerInputs.has(`${reference.path}:${reference.digest}`),
1058
+ )
1059
+ ) {
1060
+ missing.push(`${wave.mode}/${lane.laneId}`);
1061
+ }
1062
+ }
1063
+ }
1064
+ if (missing.length > 0) {
1065
+ errors.push(
1066
+ issue(
1067
+ 'MISSING_THOROUGH_GATHER_LEDGER_INPUT',
1068
+ `Thorough compilation omitted complete gather dossier inputs: ${missing.join(', ')}`,
1069
+ '$.inputArtifacts',
1070
+ ),
1071
+ );
1072
+ }
1073
+ }
1074
+
1075
+ function collectCompletePasses(
1076
+ artifactsById,
1077
+ runId,
1078
+ routing,
1079
+ conditionalState,
1080
+ ) {
1081
+ const gatherLaneOwners = new Map();
1082
+ const conditionalLanes = new Map();
1083
+ for (const wave of routing?.waves ?? []) {
1084
+ if (wave.mode === 'gather' || wave.mode === 'redundant-gather') {
1085
+ for (const lane of wave.lanes ?? []) {
1086
+ gatherLaneOwners.set(lane.laneId, {
1087
+ waveId: wave.waveId,
1088
+ mode: wave.mode,
1089
+ });
1090
+ }
1091
+ }
1092
+ if (!wave.conditional) continue;
1093
+ for (const lane of wave.lanes ?? [])
1094
+ conditionalLanes.set(lane.laneId, wave);
1095
+ }
794
1096
  const passes = new Map();
795
1097
  for (const [id, { value }] of artifactsById) {
796
1098
  if (value.runId !== runId || !artifactIsComplete(value)) continue;
1099
+ const conditionalWave = conditionalLanes.get(artifactLaneId(value));
1100
+ if (
1101
+ conditionalWave &&
1102
+ !conditionalState.activeWaveIds.has(conditionalWave.waveId)
1103
+ ) {
1104
+ continue;
1105
+ }
1106
+ if (value.kind === 'recon.raw-dossier' && value.mode === 'gather') {
1107
+ if (!routing) {
1108
+ const ids = passes.get('gather') ?? [];
1109
+ ids.push(id);
1110
+ passes.set('gather', ids);
1111
+ continue;
1112
+ }
1113
+ const owner = gatherLaneOwners.get(value.laneId);
1114
+ if (owner?.waveId === value.waveId) {
1115
+ const ids = passes.get(owner.mode) ?? [];
1116
+ ids.push(id);
1117
+ passes.set(owner.mode, ids);
1118
+ }
1119
+ continue;
1120
+ }
797
1121
  for (const [mode, contract] of Object.entries(passContracts)) {
798
1122
  if (
799
1123
  value.kind === contract.kind &&
@@ -806,13 +1130,16 @@ function collectCompletePasses(artifactsById, runId) {
806
1130
  }
807
1131
  }
808
1132
  }
809
- const gatherLanes = new Set(
810
- (passes.get('gather') ?? []).map(
811
- (id) => artifactsById.get(id).value.laneId,
812
- ),
813
- );
814
- if (gatherLanes.size >= 2)
815
- passes.set('redundant-gather', passes.get('gather'));
1133
+ if (!routing) {
1134
+ const gatherLanes = new Set(
1135
+ (passes.get('gather') ?? []).map(
1136
+ (id) => artifactsById.get(id).value.laneId,
1137
+ ),
1138
+ );
1139
+ if (gatherLanes.size >= 2) {
1140
+ passes.set('redundant-gather', passes.get('gather'));
1141
+ }
1142
+ }
816
1143
  for (const ids of passes.values()) ids.sort();
817
1144
  return passes;
818
1145
  }
@@ -827,12 +1154,62 @@ function deriveAchievedProfile(passes) {
827
1154
  return achieved;
828
1155
  }
829
1156
 
830
- function validatePassOutcomes(manifest, passes, errors) {
1157
+ function reconcilePassOutcomes(
1158
+ manifest,
1159
+ passes,
1160
+ artifactsById,
1161
+ routing,
1162
+ errors,
1163
+ ) {
1164
+ const wavesByLane = new Map();
1165
+ for (const wave of routing?.waves ?? []) {
1166
+ for (const lane of wave.lanes ?? []) wavesByLane.set(lane.laneId, wave);
1167
+ }
1168
+ for (const [mode, artifactIds] of passes) {
1169
+ const completeLanes = artifactIds
1170
+ .map((id) => {
1171
+ const value = artifactsById.get(id)?.value;
1172
+ const laneId = artifactLaneId(value);
1173
+ const wave = wavesByLane.get(laneId);
1174
+ return wave && laneId ? { wave, laneId } : null;
1175
+ })
1176
+ .filter(Boolean);
1177
+ const contradictoryGap = (manifest.gaps ?? []).find((gap) =>
1178
+ completeLanes.some(({ wave, laneId }) => {
1179
+ if (gapHasLaneIdentity(gap)) {
1180
+ return gapNamesConditionalLane(gap, wave.waveId, laneId);
1181
+ }
1182
+ return (
1183
+ wave.mode === mode && legacyGapNamesSingletonWave(gap, wave, routing)
1184
+ );
1185
+ }),
1186
+ );
1187
+ if (!contradictoryGap) continue;
1188
+ passes.delete(mode);
1189
+ errors.push(
1190
+ issue(
1191
+ 'CONTRADICTORY_PASS_OUTCOME',
1192
+ `Complete ${mode} artifacts contradict material ${contradictoryGap.code} outcome evidence`,
1193
+ `pass:${mode}`,
1194
+ ),
1195
+ );
1196
+ }
1197
+ }
1198
+
1199
+ function validatePassOutcomes(manifest, passes, routing, errors) {
831
1200
  for (const mode of requiredPasses[manifest.run.requestedProfile] ?? []) {
832
1201
  if (passes.has(mode)) continue;
833
- const hasOutcomeEvidence = (manifest.gaps ?? []).some((gap) =>
834
- gapNamesMode(gap, mode),
835
- );
1202
+ const hasOutcomeEvidence = (manifest.gaps ?? []).some((gap) => {
1203
+ if (!gapHasLaneIdentity(gap)) return gapNamesMode(gap, mode);
1204
+ const wave = (routing?.waves ?? []).find(
1205
+ (candidate) => candidate.waveId === gap.waveId,
1206
+ );
1207
+ return (
1208
+ wave?.mode === mode &&
1209
+ wave.lanes?.some((lane) => lane.laneId === gap.laneId) &&
1210
+ gapNamesConditionalLane(gap, gap.waveId, gap.laneId)
1211
+ );
1212
+ });
836
1213
  if (!hasOutcomeEvidence) {
837
1214
  errors.push(
838
1215
  issue(
@@ -1902,8 +2279,10 @@ export async function compileValidatedRun(packetDirectory) {
1902
2279
  'claims.json',
1903
2280
  );
1904
2281
 
1905
- if (manifest) errors.push(...validateArtifactShape(manifest).errors);
2282
+ const manifestShape = manifest ? validateArtifactShape(manifest) : null;
2283
+ if (manifestShape) errors.push(...manifestShape.errors);
1906
2284
  if (ledger) errors.push(...validateArtifactShape(ledger).errors);
2285
+ let routing = null;
1907
2286
 
1908
2287
  if (manifest && manifest.request?.outputPath !== packetRoot) {
1909
2288
  errors.push(
@@ -1920,25 +2299,7 @@ export async function compileValidatedRun(packetDirectory) {
1920
2299
  }
1921
2300
 
1922
2301
  if (isObject(manifest?.execution?.approval)) {
1923
- const expected = hashCanonicalJson(
1924
- approvalFingerprintInput(manifest.execution),
1925
- );
1926
- if (manifest.execution.approval.fingerprint !== expected) {
1927
- errors.push(
1928
- issue(
1929
- 'APPROVAL_FINGERPRINT_MISMATCH',
1930
- 'Approved execution envelope no longer matches its approval fingerprint',
1931
- '$.execution.approval.fingerprint',
1932
- ),
1933
- );
1934
- }
1935
- } else if (manifest) {
1936
- errors.push(
1937
- issue(
1938
- 'MISSING_APPROVAL_ENVELOPE',
1939
- 'Manifest lacks approval-bound execution evidence',
1940
- ),
1941
- );
2302
+ if (manifestShape?.valid) routing = normalizeManifestRouting(manifest);
1942
2303
  }
1943
2304
 
1944
2305
  const { artifactsByPath, artifactsById, validatedByteDigests } =
@@ -1982,7 +2343,24 @@ export async function compileValidatedRun(packetDirectory) {
1982
2343
  );
1983
2344
 
1984
2345
  const exactEvidence = new Set();
1985
- if (manifest && ledger) {
2346
+ const manifestCoreUsable =
2347
+ Array.isArray(manifest?.sources) &&
2348
+ Array.isArray(manifest?.artifacts) &&
2349
+ Array.isArray(manifest?.gaps) &&
2350
+ Array.isArray(manifest?.conditionOutcomes) &&
2351
+ isObject(manifest?.run) &&
2352
+ typeof manifest.run.id === 'string';
2353
+ const ledgerCoreUsable =
2354
+ Array.isArray(ledger?.inputArtifacts) &&
2355
+ Array.isArray(ledger?.evidence) &&
2356
+ Array.isArray(ledger?.claims) &&
2357
+ Array.isArray(ledger?.unresolvedQuestions) &&
2358
+ Array.isArray(ledger?.transitions) &&
2359
+ isObject(ledger?.synthesis) &&
2360
+ Array.isArray(ledger.synthesis.keyClaimIds) &&
2361
+ Array.isArray(ledger.synthesis.caveats) &&
2362
+ Array.isArray(ledger.synthesis.unresolvedQuestionIds);
2363
+ if (manifest && ledger && manifestCoreUsable && ledgerCoreUsable) {
1986
2364
  validateDerivedSourceGaps(manifest, ledger, errors);
1987
2365
  const sources = new Map(
1988
2366
  manifest.sources.map((source) => [source.id, source]),
@@ -2047,10 +2425,35 @@ export async function compileValidatedRun(packetDirectory) {
2047
2425
  exactEvidence.add(evidence.id);
2048
2426
  }
2049
2427
  }
2050
- validateApprovedLanes(manifest, artifactsById, errors);
2051
- const passes = collectCompletePasses(artifactsById, manifest.run.id);
2428
+ let conditionalState = {
2429
+ activeWaveIds: new Set(),
2430
+ inactiveWaveIds: new Set(),
2431
+ };
2432
+ if (routing) {
2433
+ conditionalState = validateConditionalRouting(
2434
+ manifest,
2435
+ routing,
2436
+ artifactsById,
2437
+ artifactsByPath,
2438
+ errors,
2439
+ );
2440
+ validateApprovedLanes(
2441
+ manifest,
2442
+ routing,
2443
+ artifactsById,
2444
+ conditionalState,
2445
+ errors,
2446
+ );
2447
+ }
2448
+ const passes = collectCompletePasses(
2449
+ artifactsById,
2450
+ manifest.run.id,
2451
+ routing,
2452
+ conditionalState,
2453
+ );
2454
+ reconcilePassOutcomes(manifest, passes, artifactsById, routing, errors);
2052
2455
  const achievedProfile = deriveAchievedProfile(passes);
2053
- validatePassOutcomes(manifest, passes, errors);
2456
+ validatePassOutcomes(manifest, passes, routing, errors);
2054
2457
  const assuranceReviewIds = collectAssuranceReviewIds(
2055
2458
  artifactsById,
2056
2459
  manifest.run.id,
@@ -2064,6 +2467,18 @@ export async function compileValidatedRun(packetDirectory) {
2064
2467
  reconciliationRequired,
2065
2468
  errors,
2066
2469
  );
2470
+ if (
2471
+ routing &&
2472
+ (!reconciliationRequired || reconciliationContext.priorLedger)
2473
+ ) {
2474
+ validateThoroughGatherLedgerInputs(
2475
+ manifest,
2476
+ reconciliationContext.priorLedger ?? ledger,
2477
+ routing,
2478
+ artifactsById,
2479
+ errors,
2480
+ );
2481
+ }
2067
2482
  validateReviewBindings(
2068
2483
  manifest,
2069
2484
  ledger,
@@ -2099,6 +2514,7 @@ export async function compileValidatedRun(packetDirectory) {
2099
2514
  let validatedRun = null;
2100
2515
  if (
2101
2516
  packetRootIdentity &&
2517
+ routing &&
2102
2518
  isObject(manifest.run) &&
2103
2519
  Array.isArray(ledger.claims) &&
2104
2520
  Array.isArray(ledger.evidence)
@@ -2108,6 +2524,7 @@ export async function compileValidatedRun(packetDirectory) {
2108
2524
  filesystemIdentities,
2109
2525
  canonicalByteDigests,
2110
2526
  manifest,
2527
+ routing,
2111
2528
  ledger,
2112
2529
  artifactsById,
2113
2530
  exactEvidence,