@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.
- package/assets/agents/oat-reviewer.md +4 -3
- package/assets/agents/recon-worker.md +42 -8
- package/assets/bundle-metadata.json +1 -1
- package/assets/docs/workflows/skills/recon.md +114 -23
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/oat-project-review-receive/SKILL.md +14 -1
- package/assets/skills/recon/SKILL.md +103 -40
- package/assets/skills/recon/references/packet-contract.md +150 -67
- package/assets/skills/recon/references/profiles.md +38 -16
- package/assets/skills/recon/references/worker-contract.md +39 -8
- package/assets/skills/recon/scripts/lib/contracts.mjs +703 -55
- package/assets/skills/recon/scripts/lib/routing.mjs +588 -0
- package/assets/skills/recon/scripts/lib/validated-run.mjs +2 -0
- package/assets/skills/recon/scripts/prepare-routing.mjs +88 -0
- package/assets/skills/recon/scripts/render-packet.mjs +69 -1
- package/assets/skills/recon/scripts/validate-packet.mjs +473 -56
- package/assets/skills/subagent-orchestration/SKILL.md +18 -9
- package/assets/skills/subagent-orchestration/references/model-selection-principles.md +17 -8
- package/package.json +2 -2
|
@@ -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
|
|
152
|
-
|
|
150
|
+
for (const reference of Array.isArray(manifest?.artifacts)
|
|
151
|
+
? manifest.artifacts
|
|
152
|
+
: [])
|
|
153
153
|
references.push(reference);
|
|
154
|
-
for (const
|
|
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
|
|
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
|
|
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
|
|
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 (
|
|
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
|
|
779
|
-
|
|
1011
|
+
const outcomeGap = (manifest.gaps ?? []).find((gap) =>
|
|
1012
|
+
gapNamesApprovedLane(gap, wave, laneId, routing),
|
|
780
1013
|
);
|
|
781
|
-
if (!
|
|
1014
|
+
if (!outcomeGap) {
|
|
782
1015
|
errors.push(
|
|
783
1016
|
issue(
|
|
784
1017
|
'MISSING_LANE_OUTCOME',
|
|
785
|
-
|
|
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
|
|
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
|
-
|
|
810
|
-
|
|
811
|
-
(
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2051
|
-
|
|
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,
|