@open-agent-toolkit/cli 0.2.54 → 0.2.56
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/bundle-metadata.json +1 -1
- package/assets/docs/cli-utilities/configuration.md +12 -2
- package/assets/docs/docs-tooling/commands.md +36 -5
- package/assets/docs/reference/oat-directory-structure.md +56 -4
- package/assets/docs/workflows/skills/recon.md +28 -12
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/oat-dispatch-subagents/SKILL.md +4 -105
- package/assets/skills/oat-dispatch-subagents/references/record-schema.md +0 -226
- package/assets/skills/oat-project-autonomous/references/gate-inventory.md +2 -2
- package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +2 -2
- package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +2 -2
- package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +2 -2
- package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +2 -2
- package/assets/skills/recon/SKILL.md +76 -38
- package/assets/skills/recon/references/packet-contract.md +138 -105
- package/assets/skills/recon/scripts/lib/contracts.mjs +181 -674
- package/assets/skills/recon/scripts/lib/validated-run.mjs +10 -16
- package/assets/skills/recon/scripts/render-packet.mjs +7 -20
- package/assets/skills/recon/scripts/validate-packet.mjs +177 -344
- package/dist/commands/config/index.d.ts +1 -0
- package/dist/commands/config/index.d.ts.map +1 -1
- package/dist/commands/config/index.js +51 -4
- package/dist/commands/docs/index-generate/generator.d.ts +43 -1
- package/dist/commands/docs/index-generate/generator.d.ts.map +1 -1
- package/dist/commands/docs/index-generate/generator.js +169 -3
- package/dist/commands/docs/index-generate/index.d.ts +8 -3
- package/dist/commands/docs/index-generate/index.d.ts.map +1 -1
- package/dist/commands/docs/index-generate/index.js +340 -24
- package/dist/commands/docs/init/scaffold.d.ts +11 -1
- package/dist/commands/docs/init/scaffold.d.ts.map +1 -1
- package/dist/commands/docs/init/scaffold.js +11 -2
- package/dist/config/oat-config.d.ts +14 -0
- package/dist/config/oat-config.d.ts.map +1 -1
- package/dist/config/oat-config.js +61 -0
- package/dist/fs/assets.d.ts +33 -4
- package/dist/fs/assets.d.ts.map +1 -1
- package/dist/fs/assets.js +127 -12
- package/package.json +2 -2
|
@@ -4,13 +4,9 @@ import { readFile, rm } from 'node:fs/promises';
|
|
|
4
4
|
import { join, resolve } from 'node:path';
|
|
5
5
|
import { pathToFileURL } from 'node:url';
|
|
6
6
|
|
|
7
|
+
import { hashCanonicalJson, hashFile, sha256 } from './lib/canonical-json.mjs';
|
|
7
8
|
import {
|
|
8
|
-
|
|
9
|
-
hashCanonicalJson,
|
|
10
|
-
hashFile,
|
|
11
|
-
sha256,
|
|
12
|
-
} from './lib/canonical-json.mjs';
|
|
13
|
-
import {
|
|
9
|
+
approvalFingerprintInput,
|
|
14
10
|
isDigest,
|
|
15
11
|
isObject,
|
|
16
12
|
issue,
|
|
@@ -25,13 +21,11 @@ import {
|
|
|
25
21
|
} from './lib/safe-path.mjs';
|
|
26
22
|
import { createValidatedRun } from './lib/validated-run.mjs';
|
|
27
23
|
|
|
28
|
-
const
|
|
29
|
-
quick: ['map', 'gather'
|
|
24
|
+
const requiredPasses = {
|
|
25
|
+
quick: ['map', 'gather'],
|
|
30
26
|
standard: [
|
|
31
27
|
'map',
|
|
32
28
|
'gather',
|
|
33
|
-
'compile',
|
|
34
|
-
'locator-validation',
|
|
35
29
|
'semantic-verification',
|
|
36
30
|
'adversarial',
|
|
37
31
|
'coverage',
|
|
@@ -40,8 +34,6 @@ const requiredStages = {
|
|
|
40
34
|
thorough: [
|
|
41
35
|
'map',
|
|
42
36
|
'gather',
|
|
43
|
-
'compile',
|
|
44
|
-
'locator-validation',
|
|
45
37
|
'semantic-verification',
|
|
46
38
|
'adversarial',
|
|
47
39
|
'coverage',
|
|
@@ -664,11 +656,9 @@ async function reopenEvidence(
|
|
|
664
656
|
return true;
|
|
665
657
|
}
|
|
666
658
|
|
|
667
|
-
const
|
|
659
|
+
const passContracts = {
|
|
668
660
|
map: { kind: 'recon.raw-dossier', mode: 'map' },
|
|
669
661
|
gather: { kind: 'recon.raw-dossier', mode: 'gather' },
|
|
670
|
-
compile: { kind: 'recon.raw-dossier', mode: 'compile' },
|
|
671
|
-
'locator-validation': { kind: 'recon.raw-dossier', mode: 'verify' },
|
|
672
662
|
'semantic-verification': {
|
|
673
663
|
kind: 'recon.review-result',
|
|
674
664
|
reviewKind: 'semantic',
|
|
@@ -679,7 +669,6 @@ const stageArtifactContract = {
|
|
|
679
669
|
kind: 'recon.review-result',
|
|
680
670
|
reviewKind: 'reconciliation',
|
|
681
671
|
},
|
|
682
|
-
'redundant-gather': { kind: 'recon.raw-dossier', mode: 'gather' },
|
|
683
672
|
'redundant-verification': {
|
|
684
673
|
kind: 'recon.review-result',
|
|
685
674
|
reviewKind: 'redundant-verification',
|
|
@@ -690,259 +679,185 @@ const stageArtifactContract = {
|
|
|
690
679
|
},
|
|
691
680
|
};
|
|
692
681
|
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
)
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
artifact.kind !== contract.kind ||
|
|
710
|
-
artifact.runId !== runId ||
|
|
711
|
-
(contract.mode && artifact.mode !== contract.mode) ||
|
|
712
|
-
(contract.reviewKind && artifact.reviewKind !== contract.reviewKind) ||
|
|
713
|
-
(artifact.laneId ?? artifact.reviewerLane) !== stage.laneId ||
|
|
714
|
-
('status' in artifact && artifact.status !== 'complete') ||
|
|
715
|
-
('outcome' in artifact && artifact.outcome !== 'complete')
|
|
716
|
-
) {
|
|
717
|
-
return false;
|
|
718
|
-
}
|
|
719
|
-
if (
|
|
720
|
-
!Array.isArray(stage.dispatchReceiptIds) ||
|
|
721
|
-
stage.dispatchReceiptIds.length !== 4
|
|
722
|
-
) {
|
|
723
|
-
return false;
|
|
724
|
-
}
|
|
725
|
-
const receipts = stage.dispatchReceiptIds.map(
|
|
726
|
-
(id) => artifactsById.get(id)?.value,
|
|
727
|
-
);
|
|
728
|
-
const approvedReceipt = receipts.find(
|
|
729
|
-
(receipt) => receipt?.state === 'approved',
|
|
730
|
-
);
|
|
731
|
-
const acceptedReceipt = receipts.find(
|
|
732
|
-
(receipt) => receipt?.state === 'accepted',
|
|
733
|
-
);
|
|
734
|
-
const completedReceipt = receipts.find(
|
|
735
|
-
(receipt) => receipt?.state === 'completed',
|
|
736
|
-
);
|
|
737
|
-
const approvalTime = Date.parse(approvalExecution?.approvedAt);
|
|
738
|
-
const recheckTime = Date.parse(
|
|
739
|
-
approvalExecution?.catalogRecheck?.observed_at,
|
|
740
|
-
);
|
|
741
|
-
const acceptedTime = Date.parse(
|
|
742
|
-
acceptedReceipt?.launchAcceptance?.acceptedAt,
|
|
743
|
-
);
|
|
744
|
-
const completedTime = Date.parse(
|
|
745
|
-
completedReceipt?.terminalOutcome?.completedAt,
|
|
682
|
+
const assuranceReviewKinds = new Set([
|
|
683
|
+
'semantic',
|
|
684
|
+
'adversarial',
|
|
685
|
+
'coverage',
|
|
686
|
+
'redundant-verification',
|
|
687
|
+
'contradiction-resolution',
|
|
688
|
+
]);
|
|
689
|
+
|
|
690
|
+
// A gap names a pass only when the mode appears as a whole token; `gather`
|
|
691
|
+
// must not be satisfied by a `redundant-gather` gap.
|
|
692
|
+
function gapNamesMode(gap, mode) {
|
|
693
|
+
return (
|
|
694
|
+
(gap.code === 'PASS_FAILED' || gap.code === 'PASS_OMITTED') &&
|
|
695
|
+
gap.material === true &&
|
|
696
|
+
typeof gap.message === 'string' &&
|
|
697
|
+
new RegExp(`(?:^|[^a-z-])${mode}(?:$|[^a-z-])`).test(gap.message)
|
|
746
698
|
);
|
|
747
|
-
const originalCatalog = approvalProjection?.catalog_observation;
|
|
748
|
-
if (
|
|
749
|
-
receipts.some(
|
|
750
|
-
(receipt) =>
|
|
751
|
-
receipt?.kind !== 'recon.dispatch-receipt' ||
|
|
752
|
-
receipt.runId !== runId ||
|
|
753
|
-
receipt.stageId !== stage.id ||
|
|
754
|
-
receipt.laneId !== stage.laneId ||
|
|
755
|
-
!receipt.approvalProjection ||
|
|
756
|
-
typeof receipt.approvalCanonicalJson !== 'string' ||
|
|
757
|
-
receipt.approvalFingerprint !== approvalFingerprint ||
|
|
758
|
-
receipt.approvalCanonicalJson !== canonicalJson(approvalProjection) ||
|
|
759
|
-
hashCanonicalJson(receipt.approvalProjection) !== approvalFingerprint ||
|
|
760
|
-
receipt.approvalCanonicalJson !==
|
|
761
|
-
canonicalJson(receipt.approvalProjection) ||
|
|
762
|
-
canonicalJson(receipt.approvalProjection) !==
|
|
763
|
-
canonicalJson(approvalProjection) ||
|
|
764
|
-
(receipt.state !== 'prepared' &&
|
|
765
|
-
(receipt.approvedAt !== approvalExecution.approvedAt ||
|
|
766
|
-
!approvalExecution.approvalEvidence ||
|
|
767
|
-
!receipt.approvalEvidence ||
|
|
768
|
-
canonicalJson(receipt.approvalEvidence) !==
|
|
769
|
-
canonicalJson(approvalExecution.approvalEvidence))) ||
|
|
770
|
-
(['accepted', 'completed'].includes(receipt.state) &&
|
|
771
|
-
(!approvalExecution.catalogRecheck ||
|
|
772
|
-
!receipt.catalogRecheck ||
|
|
773
|
-
canonicalJson(receipt.catalogRecheck) !==
|
|
774
|
-
canonicalJson(approvalExecution.catalogRecheck))),
|
|
775
|
-
) ||
|
|
776
|
-
!receipts.some((receipt) => receipt.state === 'prepared') ||
|
|
777
|
-
!approvedReceipt ||
|
|
778
|
-
!acceptedReceipt ||
|
|
779
|
-
!completedReceipt ||
|
|
780
|
-
JSON.stringify(completedReceipt.artifactIds) !==
|
|
781
|
-
JSON.stringify(stage.artifactIds) ||
|
|
782
|
-
!acceptedReceipt.launchAcceptance ||
|
|
783
|
-
!completedReceipt.launchAcceptance ||
|
|
784
|
-
canonicalJson(acceptedReceipt.launchAcceptance) !==
|
|
785
|
-
canonicalJson(completedReceipt.launchAcceptance) ||
|
|
786
|
-
!originalCatalog ||
|
|
787
|
-
!approvalExecution.catalogRecheck ||
|
|
788
|
-
approvalExecution.catalogRecheck.id === originalCatalog.id ||
|
|
789
|
-
!Number.isFinite(approvalTime) ||
|
|
790
|
-
!Number.isFinite(recheckTime) ||
|
|
791
|
-
!Number.isFinite(acceptedTime) ||
|
|
792
|
-
!Number.isFinite(completedTime) ||
|
|
793
|
-
recheckTime <= approvalTime ||
|
|
794
|
-
recheckTime >= acceptedTime ||
|
|
795
|
-
completedTime < acceptedTime
|
|
796
|
-
) {
|
|
797
|
-
return false;
|
|
798
|
-
}
|
|
799
|
-
return true;
|
|
800
699
|
}
|
|
801
700
|
|
|
802
|
-
function
|
|
803
|
-
return (
|
|
804
|
-
(
|
|
805
|
-
|
|
806
|
-
.map((lane) => ({ waveId: wave.wave_id, laneId: lane.lane_id })),
|
|
701
|
+
function artifactIsComplete(artifact) {
|
|
702
|
+
return (
|
|
703
|
+
(!('status' in artifact) || artifact.status === 'complete') &&
|
|
704
|
+
(!('outcome' in artifact) || artifact.outcome === 'complete')
|
|
807
705
|
);
|
|
808
706
|
}
|
|
809
707
|
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
const
|
|
830
|
-
const
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
708
|
+
const reviewWaveMode = {
|
|
709
|
+
semantic: 'semantic-verification',
|
|
710
|
+
adversarial: 'adversarial',
|
|
711
|
+
coverage: 'coverage',
|
|
712
|
+
reconciliation: 'reconciliation',
|
|
713
|
+
'redundant-verification': 'redundant-verification',
|
|
714
|
+
'contradiction-resolution': 'contradiction-resolution',
|
|
715
|
+
};
|
|
716
|
+
|
|
717
|
+
function laneWaveMatches(wave, value) {
|
|
718
|
+
if (value.kind === 'recon.raw-dossier') {
|
|
719
|
+
return wave.mode === 'redundant-gather'
|
|
720
|
+
? value.mode === 'gather'
|
|
721
|
+
: wave.mode === value.mode;
|
|
722
|
+
}
|
|
723
|
+
return reviewWaveMode[value.reviewKind] === wave.mode;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
function validateApprovedLanes(manifest, artifactsById, errors) {
|
|
727
|
+
const lanes = new Map();
|
|
728
|
+
for (const wave of manifest.execution?.waves ?? []) {
|
|
729
|
+
for (const lane of wave.lanes ?? []) {
|
|
730
|
+
lanes.set(lane.laneId, { wave, lane });
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
const written = new Set();
|
|
734
|
+
for (const [id, { reference, value }] of artifactsById) {
|
|
735
|
+
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;
|
|
742
|
+
if (laneId === null) continue;
|
|
743
|
+
const approved = lanes.get(laneId);
|
|
744
|
+
if (
|
|
745
|
+
!approved ||
|
|
746
|
+
(value.kind === 'recon.raw-dossier' &&
|
|
747
|
+
approved.wave.waveId !== value.waveId) ||
|
|
748
|
+
!laneWaveMatches(approved.wave, value)
|
|
749
|
+
) {
|
|
834
750
|
errors.push(
|
|
835
751
|
issue(
|
|
836
|
-
'
|
|
837
|
-
`
|
|
838
|
-
|
|
752
|
+
'UNAPPROVED_LANE',
|
|
753
|
+
`Artifact ${id} was not written by an approved lane of a matching wave`,
|
|
754
|
+
id,
|
|
839
755
|
),
|
|
840
756
|
);
|
|
841
757
|
continue;
|
|
842
758
|
}
|
|
843
|
-
|
|
759
|
+
const root = approved.lane.writeRoot;
|
|
760
|
+
if (reference.path !== root && !reference.path.startsWith(`${root}/`)) {
|
|
844
761
|
errors.push(
|
|
845
762
|
issue(
|
|
846
|
-
'
|
|
847
|
-
`
|
|
848
|
-
|
|
763
|
+
'LANE_WRITE_PATH_VIOLATION',
|
|
764
|
+
`Artifact ${id} is outside its approved lane write root ${root}`,
|
|
765
|
+
id,
|
|
849
766
|
),
|
|
850
767
|
);
|
|
768
|
+
}
|
|
769
|
+
// Only a complete artifact settles a lane; a failed or partial artifact
|
|
770
|
+
// still needs a material outcome gap.
|
|
771
|
+
if (artifactIsComplete(value)) written.add(laneId);
|
|
772
|
+
}
|
|
773
|
+
for (const [laneId, { wave }] of lanes) {
|
|
774
|
+
// The canonical ledger is the compile outcome; it carries no lane identity.
|
|
775
|
+
if (wave.conditional || wave.mode === 'compile' || written.has(laneId)) {
|
|
851
776
|
continue;
|
|
852
777
|
}
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
stage,
|
|
856
|
-
artifactsById,
|
|
857
|
-
runId,
|
|
858
|
-
approvalFingerprint,
|
|
859
|
-
manifest.execution,
|
|
778
|
+
const hasOutcomeEvidence = (manifest.gaps ?? []).some((gap) =>
|
|
779
|
+
gapNamesMode(gap, wave.mode),
|
|
860
780
|
);
|
|
861
|
-
if (
|
|
781
|
+
if (!hasOutcomeEvidence) {
|
|
862
782
|
errors.push(
|
|
863
783
|
issue(
|
|
864
|
-
'
|
|
865
|
-
`
|
|
866
|
-
|
|
784
|
+
'MISSING_LANE_OUTCOME',
|
|
785
|
+
`Approved lane ${laneId} has neither a result nor a material ${wave.mode} outcome gap`,
|
|
786
|
+
`lane:${laneId}`,
|
|
867
787
|
),
|
|
868
788
|
);
|
|
869
789
|
}
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
)
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
`Stage ${stage.id} lacks material ${expectedGapCode} diagnostics`,
|
|
887
|
-
stage.id,
|
|
888
|
-
),
|
|
889
|
-
);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
function collectCompletePasses(artifactsById, runId) {
|
|
794
|
+
const passes = new Map();
|
|
795
|
+
for (const [id, { value }] of artifactsById) {
|
|
796
|
+
if (value.runId !== runId || !artifactIsComplete(value)) continue;
|
|
797
|
+
for (const [mode, contract] of Object.entries(passContracts)) {
|
|
798
|
+
if (
|
|
799
|
+
value.kind === contract.kind &&
|
|
800
|
+
(contract.mode ? value.mode === contract.mode : true) &&
|
|
801
|
+
(contract.reviewKind ? value.reviewKind === contract.reviewKind : true)
|
|
802
|
+
) {
|
|
803
|
+
const ids = passes.get(mode) ?? [];
|
|
804
|
+
ids.push(id);
|
|
805
|
+
passes.set(mode, ids);
|
|
890
806
|
}
|
|
891
807
|
}
|
|
892
|
-
if (artifactIsComplete) {
|
|
893
|
-
const ids = completeArtifactIdsByMode.get(stage.mode) ?? [];
|
|
894
|
-
ids.push(stage.artifactIds[0]);
|
|
895
|
-
completeArtifactIdsByMode.set(stage.mode, ids);
|
|
896
|
-
}
|
|
897
808
|
}
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
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'));
|
|
816
|
+
for (const ids of passes.values()) ids.sort();
|
|
817
|
+
return passes;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
function deriveAchievedProfile(passes) {
|
|
821
|
+
let achieved = null;
|
|
822
|
+
for (const profile of profiles) {
|
|
823
|
+
if (requiredPasses[profile].every((mode) => passes.has(mode))) {
|
|
824
|
+
achieved = profile;
|
|
907
825
|
}
|
|
908
826
|
}
|
|
909
|
-
|
|
910
|
-
|
|
827
|
+
return achieved;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
function validatePassOutcomes(manifest, passes, errors) {
|
|
831
|
+
for (const mode of requiredPasses[manifest.run.requestedProfile] ?? []) {
|
|
832
|
+
if (passes.has(mode)) continue;
|
|
833
|
+
const hasOutcomeEvidence = (manifest.gaps ?? []).some((gap) =>
|
|
834
|
+
gapNamesMode(gap, mode),
|
|
835
|
+
);
|
|
836
|
+
if (!hasOutcomeEvidence) {
|
|
911
837
|
errors.push(
|
|
912
838
|
issue(
|
|
913
|
-
'
|
|
914
|
-
`Requested ${manifest.run.requestedProfile} profile lacks
|
|
915
|
-
`
|
|
839
|
+
'MISSING_PASS_OUTCOME_EVIDENCE',
|
|
840
|
+
`Requested ${manifest.run.requestedProfile} profile lacks a complete ${mode} result and a material PASS_FAILED or PASS_OMITTED gap naming it`,
|
|
841
|
+
`pass:${mode}`,
|
|
916
842
|
),
|
|
917
843
|
);
|
|
918
844
|
}
|
|
919
845
|
}
|
|
920
|
-
return { requiredLanes, stageByLane, completeArtifactIdsByMode };
|
|
921
846
|
}
|
|
922
847
|
|
|
923
|
-
function
|
|
924
|
-
|
|
925
|
-
for (const
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
)
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
return (
|
|
935
|
-
stage &&
|
|
936
|
-
topology.completeArtifactIdsByMode
|
|
937
|
-
.get(mode)
|
|
938
|
-
?.includes(stage.artifactIds[0])
|
|
939
|
-
);
|
|
940
|
-
})
|
|
941
|
-
);
|
|
942
|
-
});
|
|
943
|
-
if (complete) achieved = profile;
|
|
848
|
+
function collectAssuranceReviewIds(artifactsById, runId) {
|
|
849
|
+
const ids = new Set();
|
|
850
|
+
for (const [id, { value }] of artifactsById) {
|
|
851
|
+
if (
|
|
852
|
+
value.kind === 'recon.review-result' &&
|
|
853
|
+
value.runId === runId &&
|
|
854
|
+
assuranceReviewKinds.has(value.reviewKind) &&
|
|
855
|
+
artifactIsComplete(value)
|
|
856
|
+
) {
|
|
857
|
+
ids.add(id);
|
|
858
|
+
}
|
|
944
859
|
}
|
|
945
|
-
return
|
|
860
|
+
return ids;
|
|
946
861
|
}
|
|
947
862
|
|
|
948
863
|
function sameReference(left, right) {
|
|
@@ -952,12 +867,11 @@ function sameReference(left, right) {
|
|
|
952
867
|
function resolveTerminalReconciliation(
|
|
953
868
|
artifactsById,
|
|
954
869
|
artifactsByPath,
|
|
955
|
-
|
|
870
|
+
passes,
|
|
956
871
|
reconciliationRequired,
|
|
957
872
|
errors,
|
|
958
873
|
) {
|
|
959
|
-
const terminalId =
|
|
960
|
-
topology.completeArtifactIdsByMode.get('reconciliation')?.[0];
|
|
874
|
+
const terminalId = passes.get('reconciliation')?.[0];
|
|
961
875
|
const reconciliationResults = [...artifactsById.values()].filter(
|
|
962
876
|
({ value }) =>
|
|
963
877
|
value.kind === 'recon.review-result' &&
|
|
@@ -972,7 +886,7 @@ function resolveTerminalReconciliation(
|
|
|
972
886
|
errors.push(
|
|
973
887
|
issue(
|
|
974
888
|
'SHADOW_RECONCILIATION',
|
|
975
|
-
'The packet must contain exactly its one
|
|
889
|
+
'The packet must contain exactly its one complete terminal reconciliation',
|
|
976
890
|
terminalId ?? '$.artifacts',
|
|
977
891
|
),
|
|
978
892
|
);
|
|
@@ -1165,47 +1079,13 @@ function validateReviewBindings(
|
|
|
1165
1079
|
}
|
|
1166
1080
|
}
|
|
1167
1081
|
|
|
1168
|
-
const assuranceStageModeByReviewKind = new Map([
|
|
1169
|
-
['semantic', 'semantic-verification'],
|
|
1170
|
-
['adversarial', 'adversarial'],
|
|
1171
|
-
['coverage', 'coverage'],
|
|
1172
|
-
['redundant-verification', 'redundant-verification'],
|
|
1173
|
-
['contradiction-resolution', 'contradiction-resolution'],
|
|
1174
|
-
]);
|
|
1175
|
-
|
|
1176
|
-
function validateReceiptedAssuranceReviews(artifactsById, topology, errors) {
|
|
1177
|
-
const receipted = new Set();
|
|
1178
|
-
for (const [reviewKind, mode] of assuranceStageModeByReviewKind) {
|
|
1179
|
-
for (const id of topology.completeArtifactIdsByMode.get(mode) ?? []) {
|
|
1180
|
-
const artifact = artifactsById.get(id)?.value;
|
|
1181
|
-
if (artifact?.reviewKind === reviewKind) receipted.add(id);
|
|
1182
|
-
}
|
|
1183
|
-
}
|
|
1184
|
-
for (const { value } of artifactsById.values()) {
|
|
1185
|
-
if (
|
|
1186
|
-
value.kind === 'recon.review-result' &&
|
|
1187
|
-
assuranceStageModeByReviewKind.has(value.reviewKind) &&
|
|
1188
|
-
!receipted.has(value.id)
|
|
1189
|
-
) {
|
|
1190
|
-
errors.push(
|
|
1191
|
-
issue(
|
|
1192
|
-
'UNRECEIPTED_ASSURANCE_REVIEW',
|
|
1193
|
-
`Assurance result ${value.id} is not the exact artifact of a completed approved stage`,
|
|
1194
|
-
value.id,
|
|
1195
|
-
),
|
|
1196
|
-
);
|
|
1197
|
-
}
|
|
1198
|
-
}
|
|
1199
|
-
return receipted;
|
|
1200
|
-
}
|
|
1201
|
-
|
|
1202
1082
|
function validateReconciliation(
|
|
1203
1083
|
manifest,
|
|
1204
1084
|
ledger,
|
|
1205
1085
|
artifactsById,
|
|
1206
1086
|
reconciliationContext,
|
|
1207
|
-
|
|
1208
|
-
|
|
1087
|
+
passes,
|
|
1088
|
+
assuranceReviewIds,
|
|
1209
1089
|
exactEvidence,
|
|
1210
1090
|
reconciliationRequired,
|
|
1211
1091
|
errors,
|
|
@@ -1295,14 +1175,14 @@ function validateReconciliation(
|
|
|
1295
1175
|
if (
|
|
1296
1176
|
!authorization ||
|
|
1297
1177
|
authorization.disposition !== 'rejected' ||
|
|
1298
|
-
!
|
|
1178
|
+
!assuranceReviewIds.has(authorization.reviewId) ||
|
|
1299
1179
|
!reconciliation.incorporatedReviewIds.includes(authorization.reviewId) ||
|
|
1300
1180
|
disposition?.disposition !== 'rejected'
|
|
1301
1181
|
) {
|
|
1302
1182
|
errors.push(
|
|
1303
1183
|
issue(
|
|
1304
1184
|
'UNAUTHORIZED_CLAIM_REMOVAL',
|
|
1305
|
-
`Claim ${claimId} lacks a
|
|
1185
|
+
`Claim ${claimId} lacks a complete typed review disposition authorizing removal`,
|
|
1306
1186
|
claimId,
|
|
1307
1187
|
),
|
|
1308
1188
|
);
|
|
@@ -1520,8 +1400,8 @@ function validateReconciliation(
|
|
|
1520
1400
|
}
|
|
1521
1401
|
const incorporated = new Set(reconciliation.incorporatedReviewIds ?? []);
|
|
1522
1402
|
if (
|
|
1523
|
-
|
|
1524
|
-
[...
|
|
1403
|
+
assuranceReviewIds.size !== incorporated.size ||
|
|
1404
|
+
[...assuranceReviewIds].some((id) => !incorporated.has(id))
|
|
1525
1405
|
) {
|
|
1526
1406
|
errors.push(
|
|
1527
1407
|
issue(
|
|
@@ -1532,9 +1412,7 @@ function validateReconciliation(
|
|
|
1532
1412
|
);
|
|
1533
1413
|
}
|
|
1534
1414
|
|
|
1535
|
-
const coverageResults = (
|
|
1536
|
-
topology.completeArtifactIdsByMode.get('coverage') ?? []
|
|
1537
|
-
)
|
|
1415
|
+
const coverageResults = (passes.get('coverage') ?? [])
|
|
1538
1416
|
.map((id) => artifactsById.get(id)?.value)
|
|
1539
1417
|
.filter(Boolean);
|
|
1540
1418
|
const coverageDispositions = new Map(
|
|
@@ -1678,7 +1556,7 @@ function validateDerivedSourceGaps(manifest, ledger, errors) {
|
|
|
1678
1556
|
function validateAssurance(validatedRun, errors) {
|
|
1679
1557
|
const { manifest, ledger, achievedProfile } = validatedRun;
|
|
1680
1558
|
const exactEvidence = new Set(validatedRun.exactEvidenceIds);
|
|
1681
|
-
const
|
|
1559
|
+
const assuranceReviewIds = new Set(validatedRun.assuranceReviewIds);
|
|
1682
1560
|
const artifactsById = new Map(
|
|
1683
1561
|
validatedRun.artifacts.map((artifact) => [artifact.id, artifact]),
|
|
1684
1562
|
);
|
|
@@ -1784,11 +1662,11 @@ function validateAssurance(validatedRun, errors) {
|
|
|
1784
1662
|
);
|
|
1785
1663
|
continue;
|
|
1786
1664
|
}
|
|
1787
|
-
if (!
|
|
1665
|
+
if (!assuranceReviewIds.has(reviewId)) {
|
|
1788
1666
|
errors.push(
|
|
1789
1667
|
issue(
|
|
1790
|
-
'
|
|
1791
|
-
`Review ${reviewId} is not
|
|
1668
|
+
'INCOMPLETE_INDEPENDENT_REVIEW',
|
|
1669
|
+
`Review ${reviewId} is not a complete same-run assurance result`,
|
|
1792
1670
|
claim.id,
|
|
1793
1671
|
),
|
|
1794
1672
|
);
|
|
@@ -2041,56 +1919,16 @@ export async function compileValidatedRun(packetDirectory) {
|
|
|
2041
1919
|
errors.push(issue('RUN_ID_MISMATCH', 'Manifest and ledger run IDs differ'));
|
|
2042
1920
|
}
|
|
2043
1921
|
|
|
2044
|
-
if (manifest?.execution?.
|
|
2045
|
-
const
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
canonicalJson(projection) !== manifest.execution.approvalCanonicalJson
|
|
2050
|
-
) {
|
|
1922
|
+
if (isObject(manifest?.execution?.approval)) {
|
|
1923
|
+
const expected = hashCanonicalJson(
|
|
1924
|
+
approvalFingerprintInput(manifest.execution),
|
|
1925
|
+
);
|
|
1926
|
+
if (manifest.execution.approval.fingerprint !== expected) {
|
|
2051
1927
|
errors.push(
|
|
2052
1928
|
issue(
|
|
2053
1929
|
'APPROVAL_FINGERPRINT_MISMATCH',
|
|
2054
|
-
'
|
|
2055
|
-
'$.execution.
|
|
2056
|
-
),
|
|
2057
|
-
);
|
|
2058
|
-
}
|
|
2059
|
-
if (
|
|
2060
|
-
manifest.execution.approvalEvidence?.fingerprint !== expected ||
|
|
2061
|
-
manifest.execution.approvalEvidence?.type !== 'explicit-user-approval'
|
|
2062
|
-
) {
|
|
2063
|
-
errors.push(
|
|
2064
|
-
issue(
|
|
2065
|
-
'APPROVAL_EVIDENCE_MISMATCH',
|
|
2066
|
-
'Approval evidence does not bind the canonical prepared projection',
|
|
2067
|
-
'$.execution.approvalEvidence',
|
|
2068
|
-
),
|
|
2069
|
-
);
|
|
2070
|
-
}
|
|
2071
|
-
const observation = projection.catalog_observation;
|
|
2072
|
-
const recheck = manifest.execution.catalogRecheck;
|
|
2073
|
-
if (
|
|
2074
|
-
!recheck ||
|
|
2075
|
-
recheck.source !== observation?.source ||
|
|
2076
|
-
recheck.dispatch_context !== observation?.dispatch_context ||
|
|
2077
|
-
recheck.relevant_catalog_fingerprint !==
|
|
2078
|
-
observation?.relevant_catalog_fingerprint
|
|
2079
|
-
) {
|
|
2080
|
-
errors.push(
|
|
2081
|
-
issue(
|
|
2082
|
-
'CATALOG_RECHECK_MISMATCH',
|
|
2083
|
-
'Catalog recheck does not retain the approved live catalog identity',
|
|
2084
|
-
'$.execution.catalogRecheck',
|
|
2085
|
-
),
|
|
2086
|
-
);
|
|
2087
|
-
}
|
|
2088
|
-
if (projection.run_id !== manifest.run?.id) {
|
|
2089
|
-
errors.push(
|
|
2090
|
-
issue(
|
|
2091
|
-
'RUN_ID_MISMATCH',
|
|
2092
|
-
'Prepared projection and packet manifest run IDs differ',
|
|
2093
|
-
'$.execution.approvalProjection.run_id',
|
|
1930
|
+
'Approved execution envelope no longer matches its approval fingerprint',
|
|
1931
|
+
'$.execution.approval.fingerprint',
|
|
2094
1932
|
),
|
|
2095
1933
|
);
|
|
2096
1934
|
}
|
|
@@ -2209,25 +2047,20 @@ export async function compileValidatedRun(packetDirectory) {
|
|
|
2209
2047
|
exactEvidence.add(evidence.id);
|
|
2210
2048
|
}
|
|
2211
2049
|
}
|
|
2212
|
-
|
|
2213
|
-
|
|
2050
|
+
validateApprovedLanes(manifest, artifactsById, errors);
|
|
2051
|
+
const passes = collectCompletePasses(artifactsById, manifest.run.id);
|
|
2052
|
+
const achievedProfile = deriveAchievedProfile(passes);
|
|
2053
|
+
validatePassOutcomes(manifest, passes, errors);
|
|
2054
|
+
const assuranceReviewIds = collectAssuranceReviewIds(
|
|
2214
2055
|
artifactsById,
|
|
2215
2056
|
manifest.run.id,
|
|
2216
|
-
manifest.execution.approvalFingerprint,
|
|
2217
|
-
errors,
|
|
2218
|
-
);
|
|
2219
|
-
const achievedProfile = deriveAchievedProfile(topology);
|
|
2220
|
-
const receiptedReviewIds = validateReceiptedAssuranceReviews(
|
|
2221
|
-
artifactsById,
|
|
2222
|
-
topology,
|
|
2223
|
-
errors,
|
|
2224
2057
|
);
|
|
2225
2058
|
const reconciliationRequired =
|
|
2226
2059
|
achievedProfile === 'standard' || achievedProfile === 'thorough';
|
|
2227
2060
|
const reconciliationContext = resolveTerminalReconciliation(
|
|
2228
2061
|
artifactsById,
|
|
2229
2062
|
artifactsByPath,
|
|
2230
|
-
|
|
2063
|
+
passes,
|
|
2231
2064
|
reconciliationRequired,
|
|
2232
2065
|
errors,
|
|
2233
2066
|
);
|
|
@@ -2244,8 +2077,8 @@ export async function compileValidatedRun(packetDirectory) {
|
|
|
2244
2077
|
ledger,
|
|
2245
2078
|
artifactsById,
|
|
2246
2079
|
reconciliationContext,
|
|
2247
|
-
|
|
2248
|
-
|
|
2080
|
+
passes,
|
|
2081
|
+
assuranceReviewIds,
|
|
2249
2082
|
exactEvidence,
|
|
2250
2083
|
reconciliationRequired,
|
|
2251
2084
|
errors,
|
|
@@ -2278,9 +2111,9 @@ export async function compileValidatedRun(packetDirectory) {
|
|
|
2278
2111
|
ledger,
|
|
2279
2112
|
artifactsById,
|
|
2280
2113
|
exactEvidence,
|
|
2281
|
-
|
|
2114
|
+
passes,
|
|
2282
2115
|
achievedProfile,
|
|
2283
|
-
|
|
2116
|
+
assuranceReviewIds,
|
|
2284
2117
|
reconciliationContext,
|
|
2285
2118
|
});
|
|
2286
2119
|
validateAssurance(validatedRun, errors);
|