@disclosureos/records 2.0.0-beta.1 → 2.0.0-beta.3
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/CHANGELOG.md +12 -0
- package/README.md +23 -0
- package/dist/experimental/v2/index.d.ts +80789 -1
- package/dist/experimental/v2/index.js +1696 -49
- package/dist/experimental/v2/index.js.map +1 -1
- package/package.json +6 -2
- package/schema/experimental/claim-history-0.3.0.schema.json +2274 -0
- package/schema/experimental/claim-history-0.4.0.schema.json +2726 -0
- package/schema/experimental/research-entities-0.1.0.schema.json +7436 -0
- package/schema/experimental/research-entities-0.2.0.schema.json +16679 -0
|
@@ -126,9 +126,9 @@ function parsePrimitiveRecord(input) {
|
|
|
126
126
|
unchecked.add(ref2);
|
|
127
127
|
continue;
|
|
128
128
|
}
|
|
129
|
-
const
|
|
130
|
-
if (!
|
|
131
|
-
else if (
|
|
129
|
+
const target4 = assertions.get(ref2.slice("assertion:".length));
|
|
130
|
+
if (!target4) issues.push({ code: "REF.LOCAL_RESOLUTION", stage: "semantic", pointer: `/${field}/sourceRefs/${index}`, message: "Local assertion does not exist." });
|
|
131
|
+
else if (target4.field !== field) issues.push({ code: "REF.FIELD_MISMATCH", stage: "semantic", pointer: `/${field}/sourceRefs/${index}`, message: "Assertion concerns a different field." });
|
|
132
132
|
}
|
|
133
133
|
const selection = value.selection;
|
|
134
134
|
if (!selection) continue;
|
|
@@ -137,13 +137,13 @@ function parsePrimitiveRecord(input) {
|
|
|
137
137
|
if (!Number.isFinite(instantParts(selection.evaluatedAt).milliseconds)) issues.push({ code: "TIME.INSTANT", stage: "semantic", pointer: `${path}/evaluatedAt`, message: "Selection timestamp cannot be resolved with its declared UTC offset." });
|
|
138
138
|
if (!selection.consideredAssertionRefs.includes(selection.assertionRef)) issues.push({ code: "SELECTION.INPUTS", stage: "semantic", pointer: `${path}/assertionRef`, message: "Selected assertion must be among the considered inputs." });
|
|
139
139
|
if (!value.sourceRefs.includes(selection.assertionRef)) issues.push({ code: "SELECTION.SOURCE_REF", stage: "semantic", pointer: `/${field}/sourceRefs`, message: "Field provenance must cite the selected assertion." });
|
|
140
|
-
const
|
|
140
|
+
const refs2 = /* @__PURE__ */ new Set();
|
|
141
141
|
for (const [index, ref2] of selection.consideredAssertionRefs.entries()) {
|
|
142
|
-
if (
|
|
143
|
-
|
|
144
|
-
const
|
|
145
|
-
if (!
|
|
146
|
-
else if (
|
|
142
|
+
if (refs2.has(ref2)) issues.push({ code: "REF.UNIQUE_ID", stage: "semantic", pointer: `${path}/consideredAssertionRefs/${index}`, message: "Considered assertion is repeated." });
|
|
143
|
+
refs2.add(ref2);
|
|
144
|
+
const target4 = assertions.get(ref2.slice("assertion:".length));
|
|
145
|
+
if (!target4) issues.push({ code: "REF.LOCAL_RESOLUTION", stage: "semantic", pointer: `${path}/consideredAssertionRefs/${index}`, message: "Considered assertion does not exist." });
|
|
146
|
+
else if (target4.field !== field) issues.push({ code: "REF.FIELD_MISMATCH", stage: "semantic", pointer: `${path}/consideredAssertionRefs/${index}`, message: "Considered assertion concerns a different field." });
|
|
147
147
|
}
|
|
148
148
|
const selected = assertions.get(selection.assertionRef.slice("assertion:".length));
|
|
149
149
|
if (!selected) issues.push({ code: "REF.LOCAL_RESOLUTION", stage: "semantic", pointer: `${path}/assertionRef`, message: "Selected assertion does not exist." });
|
|
@@ -313,9 +313,9 @@ function parseExperimentalObservation(input) {
|
|
|
313
313
|
if (owner) graph.get(owner)?.add(ref2);
|
|
314
314
|
return true;
|
|
315
315
|
}
|
|
316
|
-
function references(
|
|
316
|
+
function references(refs2, path, owner) {
|
|
317
317
|
const seen = /* @__PURE__ */ new Set();
|
|
318
|
-
for (const [i, ref2] of
|
|
318
|
+
for (const [i, ref2] of refs2.entries()) {
|
|
319
319
|
if (seen.has(ref2)) problem("REF.UNIQUE_ID", `${path}/${i}`, "Reference is repeated.");
|
|
320
320
|
seen.add(ref2);
|
|
321
321
|
resolve(ref2, `${path}/${i}`, owner);
|
|
@@ -365,15 +365,15 @@ function parseExperimentalObservation(input) {
|
|
|
365
365
|
const assertion = assertions.get(key(ref2));
|
|
366
366
|
return assertion?.field === field && (assertion.field !== "measurement" || assertion.measurementId === measurementId);
|
|
367
367
|
}
|
|
368
|
-
function fieldReferences(
|
|
369
|
-
references(
|
|
370
|
-
for (const [i, ref2] of
|
|
368
|
+
function fieldReferences(refs2, field, path, owner, measurementId) {
|
|
369
|
+
references(refs2, path, owner);
|
|
370
|
+
for (const [i, ref2] of refs2.entries()) {
|
|
371
371
|
if (ref2.startsWith("assertion:") && assertions.has(key(ref2)) && !matchesField(ref2, field, measurementId)) problem("REF.FIELD_MISMATCH", `${path}/${i}`, "Assertion concerns a different field or measurement.");
|
|
372
372
|
}
|
|
373
373
|
}
|
|
374
|
-
function selection(value, selectedValue,
|
|
374
|
+
function selection(value, selectedValue, refs2, field, path, owner, measurementId) {
|
|
375
375
|
if (!value) {
|
|
376
|
-
if (
|
|
376
|
+
if (refs2.some((ref2) => ref2.startsWith("assertion:"))) problem("SELECTION.REQUIRED", `${path}/selection`, "A curated value citing local assertions requires an explicit selection record.");
|
|
377
377
|
return;
|
|
378
378
|
}
|
|
379
379
|
const selectionPath = `${path}/selection`;
|
|
@@ -382,10 +382,10 @@ function parseExperimentalObservation(input) {
|
|
|
382
382
|
fieldReferences(value.consideredAssertionRefs, field, `${selectionPath}/consideredAssertionRefs`, owner, measurementId);
|
|
383
383
|
resolve(value.assertionRef, `${selectionPath}/assertionRef`, owner);
|
|
384
384
|
if (!value.consideredAssertionRefs.includes(value.assertionRef)) problem("SELECTION.INPUTS", `${selectionPath}/assertionRef`, "Selected assertion must be among considered inputs.");
|
|
385
|
-
if (!
|
|
386
|
-
const
|
|
387
|
-
if (
|
|
388
|
-
else if (
|
|
385
|
+
if (!refs2.includes(value.assertionRef)) problem("SELECTION.SOURCE_REF", `${path}/sourceRefs`, "Field must cite the selected assertion.");
|
|
386
|
+
const target4 = assertions.get(key(value.assertionRef));
|
|
387
|
+
if (target4 && !matchesField(value.assertionRef, field, measurementId)) problem("REF.FIELD_MISMATCH", `${selectionPath}/assertionRef`, "Selected assertion concerns a different field or measurement.");
|
|
388
|
+
else if (target4 && JSON.stringify(target4.value) !== JSON.stringify(selectedValue)) problem("SELECTION.VALUE_MISMATCH", `${path}/value`, "Selection must copy the cited assertion value exactly; processing is declared separately.");
|
|
389
389
|
}
|
|
390
390
|
instant(data.createdAt, "/createdAt");
|
|
391
391
|
instant(data.updatedAt, "/updatedAt");
|
|
@@ -457,10 +457,10 @@ function parseExperimentalObservation(input) {
|
|
|
457
457
|
}
|
|
458
458
|
if (queue.length !== graph.size) problem("PROCESS.CYCLE", "/processing", "Local processing/provenance dependencies contain a cycle.");
|
|
459
459
|
const checks = { structural: "passed", semantic: issues.length ? "failed" : "passed", profile: "not_checked", external: "not_checked" };
|
|
460
|
-
const uncheckedRefs = [...sources.keys()].map((
|
|
461
|
-
[...products.keys()].map((
|
|
462
|
-
[...methods.keys()].map((
|
|
463
|
-
[...frames.keys()].map((
|
|
460
|
+
const uncheckedRefs = [...sources.keys()].map((id10) => `source:${id10}`).concat(
|
|
461
|
+
[...products.keys()].map((id10) => `product:${id10}`),
|
|
462
|
+
[...methods.keys()].map((id10) => `method:${id10}`),
|
|
463
|
+
[...frames.keys()].map((id10) => `frame:${id10}`)
|
|
464
464
|
);
|
|
465
465
|
return issues.length ? { success: false, issues, checks, uncheckedRefs } : { success: true, data, issues, checks, uncheckedRefs };
|
|
466
466
|
}
|
|
@@ -561,9 +561,9 @@ function parseExperimentalClaimHistory(input) {
|
|
|
561
561
|
if (!available.has(ref2)) problem("REF.LOCAL_RESOLUTION", path, `Undeclared local reference: ${ref2}`);
|
|
562
562
|
else if (owner && claims.has(ref2)) graph.get(owner)?.add(ref2);
|
|
563
563
|
}
|
|
564
|
-
function
|
|
564
|
+
function refs2(values4, path, owner) {
|
|
565
565
|
const seen = /* @__PURE__ */ new Set();
|
|
566
|
-
for (const [i, ref2] of
|
|
566
|
+
for (const [i, ref2] of values4.entries()) {
|
|
567
567
|
if (seen.has(ref2)) problem("REF.UNIQUE_ID", `${path}/${i}`, "Reference is repeated.");
|
|
568
568
|
seen.add(ref2);
|
|
569
569
|
resolve(ref2, `${path}/${i}`, owner);
|
|
@@ -575,7 +575,7 @@ function parseExperimentalClaimHistory(input) {
|
|
|
575
575
|
instant(claim.recordedAt, `${path}/recordedAt`);
|
|
576
576
|
if (claim.subject.kind === "measurement") resolve(`measurement:${claim.subject.measurementId}`, `${path}/subject/measurementId`);
|
|
577
577
|
if (claim.supersedes) {
|
|
578
|
-
|
|
578
|
+
refs2(claim.supersedes, `${path}/supersedes`, owner);
|
|
579
579
|
for (const [j, ref2] of claim.supersedes.entries()) {
|
|
580
580
|
const previous = claims.get(ref2);
|
|
581
581
|
if (!previous) continue;
|
|
@@ -600,7 +600,7 @@ function parseExperimentalClaimHistory(input) {
|
|
|
600
600
|
const locator = claim.provenance.locator;
|
|
601
601
|
if (locator?.kind === "time_range" && locator.endSeconds < locator.startSeconds) problem("TIME.INTERVAL", `${path}/provenance/locator/endSeconds`, "Locator end precedes its start.");
|
|
602
602
|
} else {
|
|
603
|
-
|
|
603
|
+
refs2(claim.inputRefs, `${path}/inputRefs`, owner);
|
|
604
604
|
if (claim.status === "assessed") {
|
|
605
605
|
instant(claim.evaluatedAt, `${path}/evaluatedAt`);
|
|
606
606
|
if (comparePoints(point(claim.evaluatedAt), point(claim.recordedAt)) > 0) problem("CLAIM.EVALUATION_ORDER", `${path}/evaluatedAt`, "Evaluation cannot occur after the assessment was recorded.");
|
|
@@ -1438,8 +1438,8 @@ function observationContextJsonSchema() {
|
|
|
1438
1438
|
}
|
|
1439
1439
|
|
|
1440
1440
|
// src/experimental/v2/context.ts
|
|
1441
|
-
function contextAssertions(
|
|
1442
|
-
return Object.entries(
|
|
1441
|
+
function contextAssertions(entity4) {
|
|
1442
|
+
return Object.entries(entity4.fields).flatMap(
|
|
1443
1443
|
([field, assertions]) => (assertions ?? []).map((assertion) => ({
|
|
1444
1444
|
field,
|
|
1445
1445
|
assertion
|
|
@@ -1453,7 +1453,7 @@ function parseObservationContext(input) {
|
|
|
1453
1453
|
rulesetVersion: "0.1.0"
|
|
1454
1454
|
};
|
|
1455
1455
|
const issues = [];
|
|
1456
|
-
const error = (code,
|
|
1456
|
+
const error = (code, pointer6, message) => issues.push({ code, stage: "semantic", pointer: pointer6, message });
|
|
1457
1457
|
if (!parsed.success)
|
|
1458
1458
|
return {
|
|
1459
1459
|
success: false,
|
|
@@ -1475,12 +1475,12 @@ function parseObservationContext(input) {
|
|
|
1475
1475
|
const data = parsed.data;
|
|
1476
1476
|
const entities = /* @__PURE__ */ new Map();
|
|
1477
1477
|
const relativeGraph = /* @__PURE__ */ new Map();
|
|
1478
|
-
for (const [i,
|
|
1479
|
-
const key2 = `${
|
|
1478
|
+
for (const [i, entity4] of data.entities.entries()) {
|
|
1479
|
+
const key2 = `${entity4.kind}:${entity4.id}`;
|
|
1480
1480
|
if (entities.has(key2))
|
|
1481
1481
|
error("REF.UNIQUE_ID", `/entities/${i}/id`, "Duplicate entity identity.");
|
|
1482
|
-
entities.set(key2,
|
|
1483
|
-
if (
|
|
1482
|
+
entities.set(key2, entity4);
|
|
1483
|
+
if (entity4.kind === "temporal") relativeGraph.set(entity4.id, /* @__PURE__ */ new Set());
|
|
1484
1484
|
}
|
|
1485
1485
|
function time(value, path) {
|
|
1486
1486
|
const found = [];
|
|
@@ -1493,9 +1493,9 @@ function parseObservationContext(input) {
|
|
|
1493
1493
|
);
|
|
1494
1494
|
const unchecked = /* @__PURE__ */ new Set([data.observationRef.sha256]);
|
|
1495
1495
|
if (data.acquisitionRef) unchecked.add(data.acquisitionRef.sha256);
|
|
1496
|
-
for (const [i,
|
|
1496
|
+
for (const [i, entity4] of data.entities.entries()) {
|
|
1497
1497
|
const path = `/entities/${i}`;
|
|
1498
|
-
const assertions = contextAssertions(
|
|
1498
|
+
const assertions = contextAssertions(entity4);
|
|
1499
1499
|
const ids = /* @__PURE__ */ new Set();
|
|
1500
1500
|
for (const { field, assertion } of assertions) {
|
|
1501
1501
|
const p = `${path}/fields/${field}`;
|
|
@@ -1528,8 +1528,8 @@ function parseObservationContext(input) {
|
|
|
1528
1528
|
"Context entity reference does not resolve."
|
|
1529
1529
|
);
|
|
1530
1530
|
if (field === "placeIds") {
|
|
1531
|
-
for (const
|
|
1532
|
-
if (!entities.has(`place:${
|
|
1531
|
+
for (const id10 of content.value)
|
|
1532
|
+
if (!entities.has(`place:${id10}`))
|
|
1533
1533
|
error(
|
|
1534
1534
|
"REF.LOCAL_RESOLUTION",
|
|
1535
1535
|
p,
|
|
@@ -1552,7 +1552,7 @@ function parseObservationContext(input) {
|
|
|
1552
1552
|
p,
|
|
1553
1553
|
"Relative date anchor does not resolve."
|
|
1554
1554
|
);
|
|
1555
|
-
relativeGraph.get(
|
|
1555
|
+
relativeGraph.get(entity4.id)?.add(relative2.anchorTemporalId);
|
|
1556
1556
|
if (relative2.relation === "during" && relative2.offset)
|
|
1557
1557
|
error(
|
|
1558
1558
|
"TIME.RELATIVE_OFFSET",
|
|
@@ -1608,7 +1608,7 @@ function parseObservationContext(input) {
|
|
|
1608
1608
|
}
|
|
1609
1609
|
}
|
|
1610
1610
|
const selectedFields = /* @__PURE__ */ new Set();
|
|
1611
|
-
for (const selection of
|
|
1611
|
+
for (const selection of entity4.selections ?? []) {
|
|
1612
1612
|
const p = `${path}/selections`;
|
|
1613
1613
|
if (selectedFields.has(selection.field))
|
|
1614
1614
|
error(
|
|
@@ -1625,9 +1625,9 @@ function parseObservationContext(input) {
|
|
|
1625
1625
|
);
|
|
1626
1626
|
if (new Set(selection.consideredAssertionIds).size !== selection.consideredAssertionIds.length)
|
|
1627
1627
|
error("REF.UNIQUE_ID", p, "Considered assertions are repeated.");
|
|
1628
|
-
for (const
|
|
1628
|
+
for (const id10 of selection.consideredAssertionIds)
|
|
1629
1629
|
if (!assertions.some(
|
|
1630
|
-
(a) => a.field === selection.field && a.assertion.id ===
|
|
1630
|
+
(a) => a.field === selection.field && a.assertion.id === id10
|
|
1631
1631
|
))
|
|
1632
1632
|
error(
|
|
1633
1633
|
"REF.FIELD_MISMATCH",
|
|
@@ -1661,7 +1661,7 @@ function parseObservationContext(input) {
|
|
|
1661
1661
|
unchecked.add(selection.methodRef);
|
|
1662
1662
|
}
|
|
1663
1663
|
const measurements = /* @__PURE__ */ new Set();
|
|
1664
|
-
for (const link of
|
|
1664
|
+
for (const link of entity4.measurements ?? []) {
|
|
1665
1665
|
if (measurements.has(link.id))
|
|
1666
1666
|
error(
|
|
1667
1667
|
"REF.UNIQUE_ID",
|
|
@@ -1882,9 +1882,9 @@ function parseContextClaimHistory(input) {
|
|
|
1882
1882
|
);
|
|
1883
1883
|
else if (owner && claims.has(ref2)) graph.get(owner)?.add(ref2);
|
|
1884
1884
|
}
|
|
1885
|
-
function
|
|
1885
|
+
function refs2(values4, path, owner) {
|
|
1886
1886
|
const seen = /* @__PURE__ */ new Set();
|
|
1887
|
-
for (const [i, ref2] of
|
|
1887
|
+
for (const [i, ref2] of values4.entries()) {
|
|
1888
1888
|
if (seen.has(ref2))
|
|
1889
1889
|
problem("REF.UNIQUE_ID", `${path}/${i}`, "Reference is repeated.");
|
|
1890
1890
|
seen.add(ref2);
|
|
@@ -1927,7 +1927,7 @@ function parseContextClaimHistory(input) {
|
|
|
1927
1927
|
`${path}/subject/measurementId`
|
|
1928
1928
|
);
|
|
1929
1929
|
if (claim.supersedes) {
|
|
1930
|
-
|
|
1930
|
+
refs2(claim.supersedes, `${path}/supersedes`, owner);
|
|
1931
1931
|
for (const [j, ref2] of claim.supersedes.entries()) {
|
|
1932
1932
|
const previous = claims.get(ref2);
|
|
1933
1933
|
if (!previous) continue;
|
|
@@ -1973,7 +1973,7 @@ function parseContextClaimHistory(input) {
|
|
|
1973
1973
|
"Locator end precedes its start."
|
|
1974
1974
|
);
|
|
1975
1975
|
} else {
|
|
1976
|
-
|
|
1976
|
+
refs2(claim.inputRefs, `${path}/inputRefs`, owner);
|
|
1977
1977
|
if (claim.status === "assessed") {
|
|
1978
1978
|
instant(claim.evaluatedAt, `${path}/evaluatedAt`);
|
|
1979
1979
|
if (comparePoints(point2(claim.evaluatedAt), point2(claim.recordedAt)) > 0)
|
|
@@ -2041,7 +2041,1654 @@ function parseContextClaimHistory(input) {
|
|
|
2041
2041
|
)
|
|
2042
2042
|
};
|
|
2043
2043
|
}
|
|
2044
|
+
var WitnessCategorySchema = z.enum([
|
|
2045
|
+
"military_pilot",
|
|
2046
|
+
"military_aircrew",
|
|
2047
|
+
"military_ground",
|
|
2048
|
+
"military_naval",
|
|
2049
|
+
"military_radar_operator",
|
|
2050
|
+
"military_other",
|
|
2051
|
+
"commercial_pilot",
|
|
2052
|
+
"commercial_aircrew",
|
|
2053
|
+
"private_pilot",
|
|
2054
|
+
"air_traffic_controller",
|
|
2055
|
+
"aviation_other",
|
|
2056
|
+
"government_official",
|
|
2057
|
+
"elected_official",
|
|
2058
|
+
"intelligence_officer",
|
|
2059
|
+
"law_enforcement",
|
|
2060
|
+
"scientist",
|
|
2061
|
+
"astronomer",
|
|
2062
|
+
"meteorologist",
|
|
2063
|
+
"engineer",
|
|
2064
|
+
"medical_professional",
|
|
2065
|
+
"maritime",
|
|
2066
|
+
"civilian",
|
|
2067
|
+
"civilian_group",
|
|
2068
|
+
"anonymous",
|
|
2069
|
+
"unknown"
|
|
2070
|
+
]).describe("Professional/role category of a witness.");
|
|
2071
|
+
var CredibilityFactorSchema = z.enum([
|
|
2072
|
+
"professional_training",
|
|
2073
|
+
"official_capacity",
|
|
2074
|
+
"sworn_statement",
|
|
2075
|
+
"contemporaneous_record",
|
|
2076
|
+
"corroborating_witnesses",
|
|
2077
|
+
"corroborating_evidence",
|
|
2078
|
+
"consistent_account",
|
|
2079
|
+
"no_apparent_motive",
|
|
2080
|
+
"reputation_risk",
|
|
2081
|
+
"security_clearance"
|
|
2082
|
+
]).describe("A factor strengthening witness credibility.");
|
|
2083
|
+
var CredibilityDetractorSchema = z.enum([
|
|
2084
|
+
"anonymous",
|
|
2085
|
+
"inconsistent_account",
|
|
2086
|
+
"delayed_report",
|
|
2087
|
+
"known_fabricator",
|
|
2088
|
+
"financial_motive",
|
|
2089
|
+
"single_witness",
|
|
2090
|
+
"impaired_state",
|
|
2091
|
+
"recovered_memory"
|
|
2092
|
+
]).describe("A factor weakening witness credibility.");
|
|
2093
|
+
var WitnessCredibilitySchema = z.object({
|
|
2094
|
+
// The former `CredibilityRating` was byte-identical to the qualitative
|
|
2095
|
+
// `ConfidenceLevel`; it is now the single shared scale (DRY #1).
|
|
2096
|
+
rating: ConfidenceLevelSchema.optional(),
|
|
2097
|
+
factors: z.array(CredibilityFactorSchema).optional(),
|
|
2098
|
+
detractors: z.array(CredibilityDetractorSchema).optional(),
|
|
2099
|
+
isProfessionalObserver: z.boolean().optional().describe("Whether the witness is a trained observer (pilot, astronomer, military)."),
|
|
2100
|
+
hasAviationExperience: z.boolean().optional().describe("Whether the witness has aviation experience."),
|
|
2101
|
+
hasMilitaryExperience: z.boolean().optional().describe("Whether the witness has military experience."),
|
|
2102
|
+
hasScientificBackground: z.boolean().optional().describe("Whether the witness has a scientific background."),
|
|
2103
|
+
hasPriorUAPKnowledge: z.boolean().optional().describe("Whether the witness had prior knowledge of or interest in UAP before the observation."),
|
|
2104
|
+
wasSkepticPrior: z.boolean().optional().describe("Whether the witness was a skeptic before the observation."),
|
|
2105
|
+
underOathTestimony: z.boolean().optional().describe("Whether the testimony was given under oath."),
|
|
2106
|
+
polygraphAdministered: z.boolean().optional().describe("Whether a polygraph was administered."),
|
|
2107
|
+
polygraphPassed: z.boolean().optional().describe("Whether the witness passed the polygraph."),
|
|
2108
|
+
consistentTestimony: z.boolean().optional().describe("Whether the account has remained consistent across retellings."),
|
|
2109
|
+
willingToBeIdentified: z.boolean().optional().describe("Whether the witness is willing to be publicly identified."),
|
|
2110
|
+
notes: z.string().optional().describe("Free-text context about the credibility assessment.")
|
|
2111
|
+
}).meta({ id: "WitnessCredibility" }).describe("Structured assessment of a witness's credibility (rating, factors, detractors).");
|
|
2112
|
+
|
|
2113
|
+
// src/extensions/testimony/witness.ts
|
|
2114
|
+
var SecurityClearanceSchema = z.enum(["none", "confidential", "secret", "top_secret", "sci", "unknown"]).describe("Security clearance level held by a witness.");
|
|
2115
|
+
var WitnessSchema = z.object({
|
|
2116
|
+
witnessId: z.string().describe("Stable unique identifier for this witness."),
|
|
2117
|
+
category: WitnessCategorySchema,
|
|
2118
|
+
anonymous: z.boolean().describe("Whether the witness\u2019s identity is withheld."),
|
|
2119
|
+
publicName: z.string().optional().describe("Witness\u2019s name as publicly known, when not anonymous."),
|
|
2120
|
+
pseudonym: z.string().optional().describe("Pseudonym used when the witness is anonymous."),
|
|
2121
|
+
role: z.string().optional().describe('Witness\u2019s role at the time (e.g. "F/A-18F pilot", "radar operator").'),
|
|
2122
|
+
organization: z.string().optional().describe("Organization the witness belonged to at the time."),
|
|
2123
|
+
experienceYears: z.number().optional().describe("Years of relevant professional experience."),
|
|
2124
|
+
securityClearance: SecurityClearanceSchema.optional(),
|
|
2125
|
+
credibility: WitnessCredibilitySchema.optional(),
|
|
2126
|
+
statementSummary: z.string().optional().describe("Brief summary of what the witness reported."),
|
|
2127
|
+
interviewAvailable: z.boolean().optional().describe("Whether a recorded interview with the witness exists."),
|
|
2128
|
+
writtenReportAvailable: z.boolean().optional().describe("Whether a written report from the witness exists.")
|
|
2129
|
+
}).meta({ id: "Witness" }).describe("A person who reported or corroborated an observation.");
|
|
2130
|
+
var TemporalCertaintySchema = z.enum(["exact", "approximate", "estimated", "unknown"]).describe("Confidence in a date or time value \u2014 the single certainty scale for all temporal values.");
|
|
2131
|
+
var ChainOfCustodySchema = z.enum([
|
|
2132
|
+
"verified",
|
|
2133
|
+
"partial",
|
|
2134
|
+
"unverified",
|
|
2135
|
+
"government_held",
|
|
2136
|
+
"private_held",
|
|
2137
|
+
"lost",
|
|
2138
|
+
"destroyed",
|
|
2139
|
+
"classified",
|
|
2140
|
+
"unknown"
|
|
2141
|
+
]).describe("Custody status of an evidence item.");
|
|
2142
|
+
var CustodyActionSchema = z.enum([
|
|
2143
|
+
"creation",
|
|
2144
|
+
"collection",
|
|
2145
|
+
"transfer",
|
|
2146
|
+
"storage",
|
|
2147
|
+
"analysis",
|
|
2148
|
+
"release",
|
|
2149
|
+
"classification",
|
|
2150
|
+
"destruction",
|
|
2151
|
+
"loss",
|
|
2152
|
+
"recovery",
|
|
2153
|
+
"other"
|
|
2154
|
+
]).describe("An action recorded in a chain-of-custody entry.");
|
|
2155
|
+
var CustodyEntrySchema = z.object({
|
|
2156
|
+
entryId: z.string().describe("Stable unique identifier for this custody entry."),
|
|
2157
|
+
date: z.string().describe("When the custody event occurred (ISO date)."),
|
|
2158
|
+
action: CustodyActionSchema,
|
|
2159
|
+
from: z.string().optional().describe("Party that held the evidence before this event."),
|
|
2160
|
+
to: z.string().optional().describe("Party that held the evidence after this event."),
|
|
2161
|
+
location: z.string().optional().describe("Where the custody event took place."),
|
|
2162
|
+
reference: z.string().optional().describe("Document or record substantiating the event (e.g. transfer receipt, log entry)."),
|
|
2163
|
+
notes: z.string().optional().describe("Free-text context about the event.")
|
|
2164
|
+
}).meta({ id: "CustodyEntry" }).describe("A single custody event for an evidence item.");
|
|
2165
|
+
z.object({
|
|
2166
|
+
evidenceId: z.string().describe("Identifier of the evidence item this custody history belongs to."),
|
|
2167
|
+
status: ChainOfCustodySchema,
|
|
2168
|
+
entries: z.array(CustodyEntrySchema)
|
|
2169
|
+
}).meta({ id: "ChainOfCustodyRecord" }).describe("The full custody history for a piece of evidence.");
|
|
2170
|
+
|
|
2171
|
+
// src/extensions/testimony/statement.ts
|
|
2172
|
+
var TestimonyContextSchema = z.enum([
|
|
2173
|
+
"sworn_affidavit",
|
|
2174
|
+
"congressional_testimony",
|
|
2175
|
+
"deposition",
|
|
2176
|
+
"official_interview",
|
|
2177
|
+
"written_statement",
|
|
2178
|
+
"recorded_interview",
|
|
2179
|
+
"informal_account",
|
|
2180
|
+
"anonymous_tip",
|
|
2181
|
+
"whistleblower_disclosure"
|
|
2182
|
+
]).describe("The setting in which testimony was given.");
|
|
2183
|
+
var TestimonyDateSchema = z.object({
|
|
2184
|
+
date: z.string().describe("When the testimony was given (ISO date)."),
|
|
2185
|
+
certainty: TemporalCertaintySchema
|
|
2186
|
+
}).meta({ id: "TestimonyDate" }).describe("When testimony was given, with its certainty.");
|
|
2187
|
+
z.object({
|
|
2188
|
+
statementId: z.string().describe('Stable id, addressable by a claim `evidenceRef` ("testimony:<statementId>").'),
|
|
2189
|
+
witness: WitnessSchema,
|
|
2190
|
+
context: TestimonyContextSchema,
|
|
2191
|
+
date: TestimonyDateSchema,
|
|
2192
|
+
content: z.string().optional().describe("Full text of the statement, when available."),
|
|
2193
|
+
summary: z.string().optional().describe("Brief summary of what the statement asserts."),
|
|
2194
|
+
sourceDocument: SourceReferenceSchema.optional(),
|
|
2195
|
+
chainOfCustody: ChainOfCustodySchema.optional(),
|
|
2196
|
+
additionalEvidenceIds: z.array(z.string()).optional().describe("Ids of other evidence items the statement references or supports.")
|
|
2197
|
+
}).meta({ id: "TestimonyStatement" }).describe("A single witness statement, its context, and supporting documentation.");
|
|
2198
|
+
|
|
2199
|
+
// src/experimental/v2/research-entities-schema.ts
|
|
2200
|
+
var text6 = z.string().min(1).regex(/\S/);
|
|
2201
|
+
var id5 = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]*(?![\s\S])/);
|
|
2202
|
+
var sourceRef3 = z.string().regex(/^(source|product):[A-Za-z0-9][A-Za-z0-9._-]*(?![\s\S])/);
|
|
2203
|
+
var provenance3 = SourceProvenanceSchema.extend({ sourceRef: sourceRef3 });
|
|
2204
|
+
var RESEARCH_ENTITIES_SCHEMA_ID = "urn:disclosureos:experimental:research-entities:0.1.0";
|
|
2205
|
+
function values2(schema) {
|
|
2206
|
+
return z.array(
|
|
2207
|
+
z.strictObject({
|
|
2208
|
+
id: id5.describe(
|
|
2209
|
+
"Assertion identity within this entity, independent of array order."
|
|
2210
|
+
),
|
|
2211
|
+
content: z.union([
|
|
2212
|
+
z.strictObject({
|
|
2213
|
+
state: z.literal("known"),
|
|
2214
|
+
value: schema,
|
|
2215
|
+
provenance: provenance3,
|
|
2216
|
+
originalWording: text6.optional()
|
|
2217
|
+
}),
|
|
2218
|
+
z.strictObject({
|
|
2219
|
+
state: z.literal("approximate"),
|
|
2220
|
+
value: schema,
|
|
2221
|
+
precision: text6,
|
|
2222
|
+
provenance: provenance3,
|
|
2223
|
+
originalWording: text6.optional()
|
|
2224
|
+
}),
|
|
2225
|
+
z.strictObject({
|
|
2226
|
+
state: z.literal("unknown"),
|
|
2227
|
+
reason: z.enum(["not_recorded", "not_collected", "unavailable"])
|
|
2228
|
+
}),
|
|
2229
|
+
z.strictObject({ state: z.literal("redacted"), reason: text6 }),
|
|
2230
|
+
z.strictObject({
|
|
2231
|
+
state: z.literal("unmapped"),
|
|
2232
|
+
originalWording: text6,
|
|
2233
|
+
provenance: provenance3
|
|
2234
|
+
})
|
|
2235
|
+
])
|
|
2236
|
+
})
|
|
2237
|
+
).min(1).optional();
|
|
2238
|
+
}
|
|
2239
|
+
var timeContextReference = ContextReferenceSchema.extend({
|
|
2240
|
+
target: z.union([
|
|
2241
|
+
ContextTargetSchema.options[0],
|
|
2242
|
+
ContextTargetSchema.options[5]
|
|
2243
|
+
])
|
|
2244
|
+
}).describe(
|
|
2245
|
+
"Temporal or event context for a qualification, not an arbitrary object attribute."
|
|
2246
|
+
);
|
|
2247
|
+
var relevantTime = z.union([
|
|
2248
|
+
z.strictObject({ kind: z.literal("calendar"), value: EventTimeValueSchema }),
|
|
2249
|
+
z.strictObject({
|
|
2250
|
+
kind: z.literal("context"),
|
|
2251
|
+
reference: timeContextReference
|
|
2252
|
+
}),
|
|
2253
|
+
z.strictObject({ kind: z.literal("unknown"), reason: text6 })
|
|
2254
|
+
]);
|
|
2255
|
+
var qualification = (value) => z.strictObject({ value, relevantTime });
|
|
2256
|
+
var ResearchWitnessFields = {
|
|
2257
|
+
publicIdentity: values2(
|
|
2258
|
+
z.discriminatedUnion("kind", [
|
|
2259
|
+
z.strictObject({ kind: z.literal("public_name"), display: text6 }),
|
|
2260
|
+
z.strictObject({ kind: z.literal("pseudonym"), display: text6 }),
|
|
2261
|
+
z.strictObject({ kind: z.literal("withheld"), display: text6 })
|
|
2262
|
+
])
|
|
2263
|
+
),
|
|
2264
|
+
anonymous: values2(z.boolean()),
|
|
2265
|
+
willingToBeIdentified: values2(z.boolean()),
|
|
2266
|
+
category: values2(qualification(WitnessCategorySchema)),
|
|
2267
|
+
role: values2(qualification(text6)),
|
|
2268
|
+
organization: values2(qualification(text6)),
|
|
2269
|
+
experienceYears: values2(qualification(z.number().min(0))),
|
|
2270
|
+
securityClearance: values2(qualification(SecurityClearanceSchema)),
|
|
2271
|
+
isProfessionalObserver: values2(qualification(z.boolean())),
|
|
2272
|
+
hasAviationExperience: values2(qualification(z.boolean())),
|
|
2273
|
+
hasMilitaryExperience: values2(qualification(z.boolean())),
|
|
2274
|
+
hasScientificBackground: values2(qualification(z.boolean())),
|
|
2275
|
+
hasPriorUAPKnowledge: values2(qualification(z.boolean())),
|
|
2276
|
+
wasSkepticPrior: values2(qualification(z.boolean())),
|
|
2277
|
+
description: values2(text6)
|
|
2278
|
+
};
|
|
2279
|
+
var ResearchAccountFields = {
|
|
2280
|
+
speakerWitnessIds: values2(z.array(id5).min(1)),
|
|
2281
|
+
recorder: values2(text6),
|
|
2282
|
+
context: values2(TestimonyContextSchema),
|
|
2283
|
+
recordedTime: values2(EventTimeValueSchema),
|
|
2284
|
+
dateCertainty: values2(
|
|
2285
|
+
z.enum(["exact", "approximate", "estimated", "unknown"])
|
|
2286
|
+
),
|
|
2287
|
+
eventContext: values2(ContextReferenceSchema),
|
|
2288
|
+
content: values2(text6),
|
|
2289
|
+
summary: values2(z.strictObject({ text: text6, summarizedBy: text6 })),
|
|
2290
|
+
sourceDocument: values2(sourceRef3),
|
|
2291
|
+
interviewAvailable: values2(z.boolean()),
|
|
2292
|
+
writtenReportAvailable: values2(z.boolean()),
|
|
2293
|
+
underOath: values2(z.boolean()),
|
|
2294
|
+
custodyStatus: values2(ChainOfCustodySchema),
|
|
2295
|
+
supportRefs: values2(z.array(sourceRef3).min(1))
|
|
2296
|
+
};
|
|
2297
|
+
var ResearchProcedureFields = {
|
|
2298
|
+
procedureType: values2(z.literal("polygraph")),
|
|
2299
|
+
witnessId: values2(id5),
|
|
2300
|
+
accountId: values2(id5),
|
|
2301
|
+
administered: values2(z.boolean()),
|
|
2302
|
+
passed: values2(z.boolean()),
|
|
2303
|
+
performedAt: values2(EventTimeValueSchema),
|
|
2304
|
+
context: values2(text6),
|
|
2305
|
+
sourceDocument: values2(sourceRef3)
|
|
2306
|
+
};
|
|
2307
|
+
var ResearchWitnessGroupFields = {
|
|
2308
|
+
witnessIds: values2(z.array(id5).min(1)),
|
|
2309
|
+
reportedCount: values2(z.number().int().min(0)),
|
|
2310
|
+
categories: values2(z.array(WitnessCategorySchema).min(1)),
|
|
2311
|
+
militaryWitnesses: values2(z.boolean()),
|
|
2312
|
+
aviationWitnesses: values2(z.boolean()),
|
|
2313
|
+
professionalObservers: values2(z.boolean()),
|
|
2314
|
+
descriptions: values2(z.array(text6).min(1))
|
|
2315
|
+
};
|
|
2316
|
+
for (const fields of [
|
|
2317
|
+
ResearchWitnessFields,
|
|
2318
|
+
ResearchAccountFields,
|
|
2319
|
+
ResearchProcedureFields,
|
|
2320
|
+
ResearchWitnessGroupFields
|
|
2321
|
+
])
|
|
2322
|
+
for (const [key2, schema] of Object.entries(fields))
|
|
2323
|
+
fields[key2] = schema.describe(
|
|
2324
|
+
`Sourced ${key2.replace(/([a-z])([A-Z])/g, "$1 $2").toLowerCase()} assertions. Alternatives and unknowns are retained; supplied does not mean verified.`
|
|
2325
|
+
);
|
|
2326
|
+
function entity2(kind, fields) {
|
|
2327
|
+
return z.strictObject({
|
|
2328
|
+
kind: z.literal(kind),
|
|
2329
|
+
id: id5,
|
|
2330
|
+
fields: z.strictObject(fields)
|
|
2331
|
+
});
|
|
2332
|
+
}
|
|
2333
|
+
var ResearchEntitySchema = z.discriminatedUnion("kind", [
|
|
2334
|
+
entity2("witness", ResearchWitnessFields),
|
|
2335
|
+
entity2("account", ResearchAccountFields),
|
|
2336
|
+
entity2("procedure", ResearchProcedureFields),
|
|
2337
|
+
entity2("witness_group", ResearchWitnessGroupFields)
|
|
2338
|
+
]);
|
|
2339
|
+
var ResearchEntitiesSchema = z.strictObject({
|
|
2340
|
+
kind: z.literal("research_entities"),
|
|
2341
|
+
schemaVersion: z.literal("0.1.0"),
|
|
2342
|
+
id: id5,
|
|
2343
|
+
observationRef: ObservationSnapshotRefSchema,
|
|
2344
|
+
contextRefs: z.array(
|
|
2345
|
+
DocumentSnapshotRefSchema.extend({
|
|
2346
|
+
schemaId: z.literal(OBSERVATION_CONTEXT_SCHEMA_ID)
|
|
2347
|
+
})
|
|
2348
|
+
),
|
|
2349
|
+
recordedAt: instantString,
|
|
2350
|
+
recordedBy: text6,
|
|
2351
|
+
entities: z.array(ResearchEntitySchema).min(1)
|
|
2352
|
+
}).describe(
|
|
2353
|
+
"Public witness, account, procedure and aggregate declarations. Private identity mappings and contact fields belong outside this exchange document. No consent or credibility is inferred."
|
|
2354
|
+
);
|
|
2355
|
+
function target2(kind, fields) {
|
|
2356
|
+
return z.strictObject({
|
|
2357
|
+
kind: z.literal(kind),
|
|
2358
|
+
id: id5,
|
|
2359
|
+
field: z.enum(Object.keys(fields)).optional()
|
|
2360
|
+
});
|
|
2361
|
+
}
|
|
2362
|
+
var ResearchEntityTargetSchema = z.discriminatedUnion("kind", [
|
|
2363
|
+
target2("witness", ResearchWitnessFields),
|
|
2364
|
+
target2("account", ResearchAccountFields),
|
|
2365
|
+
target2("procedure", ResearchProcedureFields),
|
|
2366
|
+
target2("witness_group", ResearchWitnessGroupFields)
|
|
2367
|
+
]);
|
|
2368
|
+
var ResearchEntityReferenceSchema = z.strictObject({
|
|
2369
|
+
document: DocumentSnapshotRefSchema.extend({
|
|
2370
|
+
schemaId: z.literal(RESEARCH_ENTITIES_SCHEMA_ID)
|
|
2371
|
+
}),
|
|
2372
|
+
target: ResearchEntityTargetSchema
|
|
2373
|
+
});
|
|
2374
|
+
var ResearchEntityAssertionReferenceSchema = ResearchEntityReferenceSchema.extend({ assertionId: id5 });
|
|
2375
|
+
function researchEntitiesJsonSchema() {
|
|
2376
|
+
return {
|
|
2377
|
+
...z.toJSONSchema(ResearchEntitiesSchema, {
|
|
2378
|
+
target: "draft-2020-12",
|
|
2379
|
+
reused: "ref"
|
|
2380
|
+
}),
|
|
2381
|
+
$id: RESEARCH_ENTITIES_SCHEMA_ID
|
|
2382
|
+
};
|
|
2383
|
+
}
|
|
2384
|
+
|
|
2385
|
+
// src/experimental/v2/entity-document-validation.ts
|
|
2386
|
+
function researchEntityAssertions(entity4) {
|
|
2387
|
+
return Object.entries(entity4.fields).flatMap(
|
|
2388
|
+
([field, list]) => (list ?? []).map((assertion) => ({
|
|
2389
|
+
field,
|
|
2390
|
+
assertion
|
|
2391
|
+
}))
|
|
2392
|
+
);
|
|
2393
|
+
}
|
|
2394
|
+
function researchEntityContextReferences(entity4) {
|
|
2395
|
+
return researchEntityAssertions(entity4).flatMap(({ field, assertion }) => {
|
|
2396
|
+
const value = assertion.content.value;
|
|
2397
|
+
if (field === "eventContext" && value)
|
|
2398
|
+
return [value];
|
|
2399
|
+
return value?.relevantTime?.kind === "context" ? [value.relevantTime.reference] : [];
|
|
2400
|
+
});
|
|
2401
|
+
}
|
|
2402
|
+
function parseEntityDocument(input, schema, contract) {
|
|
2403
|
+
const parsed = schema.safeParse(input);
|
|
2404
|
+
if (!parsed.success)
|
|
2405
|
+
return {
|
|
2406
|
+
success: false,
|
|
2407
|
+
contract,
|
|
2408
|
+
checks: {
|
|
2409
|
+
structural: "failed",
|
|
2410
|
+
semantic: "not_checked",
|
|
2411
|
+
profile: "not_checked",
|
|
2412
|
+
external: "not_checked"
|
|
2413
|
+
},
|
|
2414
|
+
uncheckedRefs: [],
|
|
2415
|
+
issues: parsed.error.issues.map((i) => ({
|
|
2416
|
+
code: "STRUCT.VALUE",
|
|
2417
|
+
stage: "structural",
|
|
2418
|
+
pointer: "/" + i.path.join("/"),
|
|
2419
|
+
message: i.message
|
|
2420
|
+
}))
|
|
2421
|
+
};
|
|
2422
|
+
const data = parsed.data, issues = [];
|
|
2423
|
+
const problem = (code, pointer6, message) => issues.push({ code, stage: "semantic", pointer: pointer6, message });
|
|
2424
|
+
const snapshots = new Set(data.contextRefs.map((r) => JSON.stringify(r)));
|
|
2425
|
+
if (snapshots.size !== data.contextRefs.length)
|
|
2426
|
+
problem(
|
|
2427
|
+
"REF.UNIQUE_ID",
|
|
2428
|
+
"/contextRefs",
|
|
2429
|
+
"Context snapshot references repeat."
|
|
2430
|
+
);
|
|
2431
|
+
const unchecked = /* @__PURE__ */ new Set([
|
|
2432
|
+
data.observationRef.sha256,
|
|
2433
|
+
...data.contextRefs.map((r) => r.sha256)
|
|
2434
|
+
]);
|
|
2435
|
+
const identities = /* @__PURE__ */ new Set();
|
|
2436
|
+
for (const [i, e] of data.entities.entries()) {
|
|
2437
|
+
const key2 = `${e.kind}:${e.id}`;
|
|
2438
|
+
if (identities.has(key2))
|
|
2439
|
+
problem(
|
|
2440
|
+
"REF.UNIQUE_ID",
|
|
2441
|
+
`/entities/${i}/id`,
|
|
2442
|
+
"Entity identity repeats within its kind."
|
|
2443
|
+
);
|
|
2444
|
+
identities.add(key2);
|
|
2445
|
+
}
|
|
2446
|
+
function time(v, p) {
|
|
2447
|
+
const found = [];
|
|
2448
|
+
checkTime(v, p, found);
|
|
2449
|
+
found.forEach((i) => problem(i.code, i.pointer, i.message));
|
|
2450
|
+
}
|
|
2451
|
+
time(
|
|
2452
|
+
{ kind: "instant", value: data.recordedAt},
|
|
2453
|
+
"/recordedAt"
|
|
2454
|
+
);
|
|
2455
|
+
for (const [i, e] of data.entities.entries()) {
|
|
2456
|
+
const ep = `/entities/${i}`, ids = /* @__PURE__ */ new Set();
|
|
2457
|
+
for (const { field, assertion: a } of researchEntityAssertions(e)) {
|
|
2458
|
+
const p = `${ep}/fields/${field}`;
|
|
2459
|
+
if (ids.has(a.id))
|
|
2460
|
+
problem("REF.UNIQUE_ID", p, "Assertion IDs repeat within this entity.");
|
|
2461
|
+
ids.add(a.id);
|
|
2462
|
+
const prov = a.content.provenance;
|
|
2463
|
+
if (prov) {
|
|
2464
|
+
unchecked.add(prov.sourceRef);
|
|
2465
|
+
if (prov.locator?.kind === "time_range" && prov.locator.endSeconds < prov.locator.startSeconds)
|
|
2466
|
+
problem("TIME.INTERVAL", p, "Source locator end precedes its start.");
|
|
2467
|
+
}
|
|
2468
|
+
const value = a.content.value;
|
|
2469
|
+
if (value === void 0) continue;
|
|
2470
|
+
if (field === "recordedTime" || field === "performedAt")
|
|
2471
|
+
time(value, p);
|
|
2472
|
+
const relevant = value.relevantTime;
|
|
2473
|
+
if (relevant?.kind === "calendar") time(relevant.value, p);
|
|
2474
|
+
const targetKind = field === "speakerWitnessIds" || field === "witnessIds" || field === "witnessId" ? "witness" : field === "accountId" ? "account" : void 0;
|
|
2475
|
+
if (targetKind) {
|
|
2476
|
+
const refs2 = Array.isArray(value) ? value : [value];
|
|
2477
|
+
if (new Set(refs2).size !== refs2.length)
|
|
2478
|
+
problem("REF.UNIQUE_ID", p, "Typed local links repeat.");
|
|
2479
|
+
for (const id10 of refs2)
|
|
2480
|
+
if (!identities.has(`${targetKind}:${id10}`))
|
|
2481
|
+
problem("REF.ENTITY", p, "Typed local entity does not exist.");
|
|
2482
|
+
}
|
|
2483
|
+
if (field === "sourceDocument") unchecked.add(value);
|
|
2484
|
+
if (field === "supportRefs") {
|
|
2485
|
+
const refs2 = value;
|
|
2486
|
+
if (new Set(refs2).size !== refs2.length)
|
|
2487
|
+
problem("REF.UNIQUE_ID", p, "Support references repeat.");
|
|
2488
|
+
refs2.forEach((r) => unchecked.add(r));
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2491
|
+
for (const ref2 of researchEntityContextReferences(e))
|
|
2492
|
+
if (!snapshots.has(JSON.stringify(ref2.document)))
|
|
2493
|
+
problem(
|
|
2494
|
+
"REF.LOCAL_RESOLUTION",
|
|
2495
|
+
ep,
|
|
2496
|
+
"Context reference must select a declared snapshot."
|
|
2497
|
+
);
|
|
2498
|
+
}
|
|
2499
|
+
const validation = {
|
|
2500
|
+
contract,
|
|
2501
|
+
issues,
|
|
2502
|
+
uncheckedRefs: [...unchecked],
|
|
2503
|
+
checks: {
|
|
2504
|
+
structural: "passed",
|
|
2505
|
+
semantic: issues.length ? "failed" : "passed",
|
|
2506
|
+
profile: "not_checked",
|
|
2507
|
+
external: "not_checked"
|
|
2508
|
+
}
|
|
2509
|
+
};
|
|
2510
|
+
return issues.length ? { ...validation, success: false } : { ...validation, success: true, data };
|
|
2511
|
+
}
|
|
2512
|
+
|
|
2513
|
+
// src/experimental/v2/research-entities.ts
|
|
2514
|
+
function parseResearchEntities(input) {
|
|
2515
|
+
return parseEntityDocument(input, ResearchEntitiesSchema, {
|
|
2516
|
+
schemaId: RESEARCH_ENTITIES_SCHEMA_ID,
|
|
2517
|
+
rulesetVersion: "0.1.0"
|
|
2518
|
+
});
|
|
2519
|
+
}
|
|
2520
|
+
var text7 = z.string().min(1).regex(/\S/, "must contain non-whitespace text");
|
|
2521
|
+
var id6 = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]*(?![\s\S])/);
|
|
2522
|
+
var claimRef3 = z.string().regex(/^claim:[A-Za-z0-9][A-Za-z0-9._-]*(?![\s\S])/);
|
|
2523
|
+
var inputRef3 = z.string().regex(
|
|
2524
|
+
/^(?:claim|source|product|assertion|measurement):[A-Za-z0-9][A-Za-z0-9._-]*(?![\s\S])/
|
|
2525
|
+
);
|
|
2526
|
+
var ResearchClaimSubjectSchema = z.discriminatedUnion("kind", [
|
|
2527
|
+
z.strictObject({ kind: z.literal("observation") }),
|
|
2528
|
+
z.strictObject({
|
|
2529
|
+
kind: z.literal("entity"),
|
|
2530
|
+
reference: ResearchEntityReferenceSchema
|
|
2531
|
+
}),
|
|
2532
|
+
z.strictObject({
|
|
2533
|
+
kind: z.literal("context"),
|
|
2534
|
+
reference: ContextReferenceSchema
|
|
2535
|
+
}),
|
|
2536
|
+
z.strictObject({ kind: z.literal("measurement"), measurementId: id6 })
|
|
2537
|
+
]);
|
|
2538
|
+
var WitnessReviewSchema = z.strictObject({
|
|
2539
|
+
rating: ConfidenceLevelSchema.optional(),
|
|
2540
|
+
factors: z.array(CredibilityFactorSchema).optional().describe(
|
|
2541
|
+
"Reviewer-selected factor labels; no built-in weighting or validation of their truth."
|
|
2542
|
+
),
|
|
2543
|
+
detractors: z.array(CredibilityDetractorSchema).optional().describe(
|
|
2544
|
+
"Retained vocabulary for attributable reviews. Anonymity never adds a penalty automatically."
|
|
2545
|
+
),
|
|
2546
|
+
consistentTestimony: z.enum(["consistent", "inconsistent", "unknown"]).optional(),
|
|
2547
|
+
multipleIndependent: z.enum(["independent", "not_independent", "unknown"]).optional(),
|
|
2548
|
+
notes: text7.optional()
|
|
2549
|
+
}).describe(
|
|
2550
|
+
"Attributable interpretation, separate from sourced qualifications; requires explicit inputs."
|
|
2551
|
+
);
|
|
2552
|
+
var common3 = {
|
|
2553
|
+
id: id6,
|
|
2554
|
+
recordedAt: instantString,
|
|
2555
|
+
topic: text7,
|
|
2556
|
+
subject: ResearchClaimSubjectSchema,
|
|
2557
|
+
supersedes: z.array(claimRef3).min(1).optional()
|
|
2558
|
+
};
|
|
2559
|
+
var ResearchSourceStatementSchema = z.strictObject({
|
|
2560
|
+
...common3,
|
|
2561
|
+
kind: z.literal("source_assertion"),
|
|
2562
|
+
text: text7,
|
|
2563
|
+
reportedLevel: text7.optional(),
|
|
2564
|
+
provenance: ObservationAssertionSchema.options[0].shape.provenance
|
|
2565
|
+
});
|
|
2566
|
+
var assessment3 = {
|
|
2567
|
+
...common3,
|
|
2568
|
+
kind: z.literal("assessment"),
|
|
2569
|
+
inputRefs: z.array(inputRef3),
|
|
2570
|
+
contextInputRefs: z.array(ContextAssertionReferenceSchema).optional(),
|
|
2571
|
+
entityInputRefs: z.array(ResearchEntityAssertionReferenceSchema).optional(),
|
|
2572
|
+
rationale: text7
|
|
2573
|
+
};
|
|
2574
|
+
var ResearchUnassessedClaimSchema = z.strictObject({
|
|
2575
|
+
...assessment3,
|
|
2576
|
+
status: z.literal("unassessed")
|
|
2577
|
+
});
|
|
2578
|
+
var ResearchAssessedClaimSchema = z.strictObject({
|
|
2579
|
+
...assessment3,
|
|
2580
|
+
status: z.literal("assessed"),
|
|
2581
|
+
outcome: z.enum(["reported", "confirmed", "absent", "inconclusive"]),
|
|
2582
|
+
evaluatedBy: text7,
|
|
2583
|
+
evaluatedAt: instantString,
|
|
2584
|
+
methodRef: z.string().regex(/^method:[A-Za-z0-9][A-Za-z0-9._-]*(?![\s\S])/),
|
|
2585
|
+
methodVersion: text7,
|
|
2586
|
+
confidence: z.number().min(0).max(1).optional(),
|
|
2587
|
+
witnessReview: WitnessReviewSchema.optional()
|
|
2588
|
+
});
|
|
2589
|
+
var ResearchHistoricalClaimSchema = z.union([
|
|
2590
|
+
ResearchSourceStatementSchema,
|
|
2591
|
+
ResearchUnassessedClaimSchema,
|
|
2592
|
+
ResearchAssessedClaimSchema
|
|
2593
|
+
]);
|
|
2594
|
+
var RESEARCH_CLAIM_HISTORY_SCHEMA_ID = "urn:disclosureos:experimental:claim-history:0.3.0";
|
|
2595
|
+
var RESEARCH_CLAIM_HISTORY_RULESET_VERSION = "0.3.0";
|
|
2596
|
+
var ResearchClaimHistorySchema = z.strictObject({
|
|
2597
|
+
kind: z.literal("claim_history"),
|
|
2598
|
+
schemaVersion: z.literal("0.3.0"),
|
|
2599
|
+
id: id6,
|
|
2600
|
+
contextRefs: z.array(
|
|
2601
|
+
DocumentSnapshotRefSchema.extend({
|
|
2602
|
+
schemaId: z.literal(OBSERVATION_CONTEXT_SCHEMA_ID)
|
|
2603
|
+
})
|
|
2604
|
+
),
|
|
2605
|
+
entityRefs: z.array(
|
|
2606
|
+
DocumentSnapshotRefSchema.extend({
|
|
2607
|
+
schemaId: z.literal(RESEARCH_ENTITIES_SCHEMA_ID)
|
|
2608
|
+
})
|
|
2609
|
+
),
|
|
2610
|
+
observation: ExperimentalObservationSchema,
|
|
2611
|
+
claims: z.array(ResearchHistoricalClaimSchema),
|
|
2612
|
+
extensions: ExperimentalObservationSchema.shape.extensions
|
|
2613
|
+
});
|
|
2614
|
+
function researchClaimHistoryJsonSchema() {
|
|
2615
|
+
return {
|
|
2616
|
+
...z.toJSONSchema(ResearchClaimHistorySchema, {
|
|
2617
|
+
target: "draft-2020-12",
|
|
2618
|
+
reused: "ref"
|
|
2619
|
+
}),
|
|
2620
|
+
$id: RESEARCH_CLAIM_HISTORY_SCHEMA_ID
|
|
2621
|
+
};
|
|
2622
|
+
}
|
|
2623
|
+
|
|
2624
|
+
// src/experimental/v2/entity-claim-history-validation.ts
|
|
2625
|
+
var point3 = (value) => ({
|
|
2626
|
+
kind: "instant",
|
|
2627
|
+
value,
|
|
2628
|
+
timeScale: "UTC"
|
|
2629
|
+
});
|
|
2630
|
+
var pointer5 = (path) => path.map((p) => `/${String(p).replace(/~/g, "~0").replace(/\//g, "~1")}`).join("");
|
|
2631
|
+
function parseEntityClaimHistory(input, schema, contract) {
|
|
2632
|
+
const parsed = schema.safeParse(input);
|
|
2633
|
+
if (!parsed.success)
|
|
2634
|
+
return {
|
|
2635
|
+
success: false,
|
|
2636
|
+
contract,
|
|
2637
|
+
checks: {
|
|
2638
|
+
structural: "failed",
|
|
2639
|
+
semantic: "not_checked",
|
|
2640
|
+
profile: "not_checked",
|
|
2641
|
+
external: "not_checked"
|
|
2642
|
+
},
|
|
2643
|
+
uncheckedRefs: [],
|
|
2644
|
+
issues: parsed.error.issues.map((issue) => ({
|
|
2645
|
+
code: "STRUCT.VALUE",
|
|
2646
|
+
stage: "structural",
|
|
2647
|
+
severity: "error",
|
|
2648
|
+
pointer: pointer5(issue.path),
|
|
2649
|
+
message: issue.message
|
|
2650
|
+
}))
|
|
2651
|
+
};
|
|
2652
|
+
const data = parsed.data;
|
|
2653
|
+
const observationResult = parseExperimentalObservation(data.observation);
|
|
2654
|
+
const issues = observationResult.issues.map(
|
|
2655
|
+
(issue) => ({ ...issue, pointer: `/observation${issue.pointer}` })
|
|
2656
|
+
);
|
|
2657
|
+
const problem = (code, path, message) => issues.push({
|
|
2658
|
+
code,
|
|
2659
|
+
stage: "semantic",
|
|
2660
|
+
severity: "error",
|
|
2661
|
+
pointer: path,
|
|
2662
|
+
message
|
|
2663
|
+
});
|
|
2664
|
+
const claims = /* @__PURE__ */ new Map();
|
|
2665
|
+
const graph = /* @__PURE__ */ new Map();
|
|
2666
|
+
const superseded = /* @__PURE__ */ new Set();
|
|
2667
|
+
const sources = new Map(
|
|
2668
|
+
data.observation.sources.map((item) => [`source:${item.id}`, item])
|
|
2669
|
+
);
|
|
2670
|
+
const products = new Map(
|
|
2671
|
+
data.observation.products.map((item) => [`product:${item.id}`, item])
|
|
2672
|
+
);
|
|
2673
|
+
const methods = new Map(
|
|
2674
|
+
data.observation.methods.map((item) => [`method:${item.id}`, item])
|
|
2675
|
+
);
|
|
2676
|
+
const measurements = new Set(
|
|
2677
|
+
data.observation.measurements.map((item) => `measurement:${item.id}`)
|
|
2678
|
+
);
|
|
2679
|
+
const available = /* @__PURE__ */ new Set([
|
|
2680
|
+
...sources.keys(),
|
|
2681
|
+
...products.keys(),
|
|
2682
|
+
...measurements,
|
|
2683
|
+
...data.observation.assertions.map((item) => `assertion:${item.id}`)
|
|
2684
|
+
]);
|
|
2685
|
+
for (const [i, claim] of data.claims.entries()) {
|
|
2686
|
+
const ref2 = `claim:${claim.id}`;
|
|
2687
|
+
if (claims.has(ref2))
|
|
2688
|
+
problem(
|
|
2689
|
+
"REF.UNIQUE_ID",
|
|
2690
|
+
`/claims/${i}/id`,
|
|
2691
|
+
"Duplicate claim ID; each revision must have its own ID."
|
|
2692
|
+
);
|
|
2693
|
+
else {
|
|
2694
|
+
claims.set(ref2, claim);
|
|
2695
|
+
graph.set(ref2, /* @__PURE__ */ new Set());
|
|
2696
|
+
available.add(ref2);
|
|
2697
|
+
}
|
|
2698
|
+
}
|
|
2699
|
+
function instant(value, path) {
|
|
2700
|
+
const local = [];
|
|
2701
|
+
checkTime(point3(value), path, local);
|
|
2702
|
+
for (const issue of local)
|
|
2703
|
+
problem(issue.code, issue.pointer, issue.message);
|
|
2704
|
+
}
|
|
2705
|
+
function resolve(ref2, path, owner) {
|
|
2706
|
+
if (!available.has(ref2))
|
|
2707
|
+
problem(
|
|
2708
|
+
"REF.LOCAL_RESOLUTION",
|
|
2709
|
+
path,
|
|
2710
|
+
`Undeclared local reference: ${ref2}`
|
|
2711
|
+
);
|
|
2712
|
+
else if (owner && claims.has(ref2)) graph.get(owner)?.add(ref2);
|
|
2713
|
+
}
|
|
2714
|
+
function refs2(values4, path, owner) {
|
|
2715
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2716
|
+
for (const [i, ref2] of values4.entries()) {
|
|
2717
|
+
if (seen.has(ref2))
|
|
2718
|
+
problem("REF.UNIQUE_ID", `${path}/${i}`, "Reference is repeated.");
|
|
2719
|
+
seen.add(ref2);
|
|
2720
|
+
resolve(ref2, `${path}/${i}`, owner);
|
|
2721
|
+
}
|
|
2722
|
+
}
|
|
2723
|
+
const snapshots = new Set(data.contextRefs.map((r) => JSON.stringify(r)));
|
|
2724
|
+
if (snapshots.size !== data.contextRefs.length)
|
|
2725
|
+
problem(
|
|
2726
|
+
"REF.UNIQUE_ID",
|
|
2727
|
+
"/contextRefs",
|
|
2728
|
+
"Context snapshot references repeat."
|
|
2729
|
+
);
|
|
2730
|
+
for (const [i, claim] of data.claims.entries()) {
|
|
2731
|
+
const contextRefs = [
|
|
2732
|
+
...claim.subject.kind === "context" ? [claim.subject.reference] : [],
|
|
2733
|
+
...claim.kind === "assessment" ? claim.contextInputRefs ?? [] : []
|
|
2734
|
+
];
|
|
2735
|
+
for (const ref2 of contextRefs)
|
|
2736
|
+
if (!snapshots.has(JSON.stringify(ref2.document)))
|
|
2737
|
+
problem(
|
|
2738
|
+
"REF.LOCAL_RESOLUTION",
|
|
2739
|
+
`/claims/${i}`,
|
|
2740
|
+
"Context reference must select a declared snapshot."
|
|
2741
|
+
);
|
|
2742
|
+
if (claim.kind === "assessment" && new Set((claim.contextInputRefs ?? []).map((r) => JSON.stringify(r))).size !== (claim.contextInputRefs ?? []).length)
|
|
2743
|
+
problem(
|
|
2744
|
+
"REF.UNIQUE_ID",
|
|
2745
|
+
`/claims/${i}/contextInputRefs`,
|
|
2746
|
+
"Context inputs repeat."
|
|
2747
|
+
);
|
|
2748
|
+
}
|
|
2749
|
+
const entitySnapshots = new Set(
|
|
2750
|
+
data.entityRefs.map((r) => JSON.stringify(r))
|
|
2751
|
+
);
|
|
2752
|
+
if (entitySnapshots.size !== data.entityRefs.length)
|
|
2753
|
+
problem("REF.UNIQUE_ID", "/entityRefs", "Entity snapshots repeat.");
|
|
2754
|
+
for (const [i, claim] of data.claims.entries()) {
|
|
2755
|
+
const refs3 = [
|
|
2756
|
+
...claim.subject.kind === "entity" ? [claim.subject.reference] : [],
|
|
2757
|
+
...claim.kind === "assessment" ? claim.entityInputRefs ?? [] : []
|
|
2758
|
+
];
|
|
2759
|
+
for (const ref2 of refs3)
|
|
2760
|
+
if (!entitySnapshots.has(JSON.stringify(ref2.document)))
|
|
2761
|
+
problem(
|
|
2762
|
+
"REF.LOCAL_RESOLUTION",
|
|
2763
|
+
`/claims/${i}`,
|
|
2764
|
+
"Entity reference must select a declared snapshot."
|
|
2765
|
+
);
|
|
2766
|
+
if (claim.kind === "assessment") {
|
|
2767
|
+
if (new Set((claim.entityInputRefs ?? []).map((r) => JSON.stringify(r))).size !== (claim.entityInputRefs ?? []).length)
|
|
2768
|
+
problem(
|
|
2769
|
+
"REF.UNIQUE_ID",
|
|
2770
|
+
`/claims/${i}/entityInputRefs`,
|
|
2771
|
+
"Entity inputs repeat."
|
|
2772
|
+
);
|
|
2773
|
+
if (claim.status === "assessed" && claim.witnessReview) {
|
|
2774
|
+
if (claim.subject.kind !== "entity" || !["witness", "account", "witness_group", "procedure"].includes(
|
|
2775
|
+
claim.subject.reference.target.kind
|
|
2776
|
+
))
|
|
2777
|
+
problem(
|
|
2778
|
+
"CLAIM.SUPERSESSION_SCOPE",
|
|
2779
|
+
`/claims/${i}/witnessReview`,
|
|
2780
|
+
"Witness review requires a typed research-entity subject."
|
|
2781
|
+
);
|
|
2782
|
+
if (claim.inputRefs.length + (claim.entityInputRefs?.length ?? 0) + (claim.contextInputRefs?.length ?? 0) + ("editionInputRefs" in claim ? claim.editionInputRefs?.length ?? 0 : 0) === 0)
|
|
2783
|
+
problem(
|
|
2784
|
+
"REF.LOCAL_RESOLUTION",
|
|
2785
|
+
`/claims/${i}/witnessReview`,
|
|
2786
|
+
"Witness review requires explicit inputs."
|
|
2787
|
+
);
|
|
2788
|
+
}
|
|
2789
|
+
}
|
|
2790
|
+
}
|
|
2791
|
+
for (const [i, claim] of data.claims.entries()) {
|
|
2792
|
+
const path = `/claims/${i}`;
|
|
2793
|
+
const owner = `claim:${claim.id}`;
|
|
2794
|
+
instant(claim.recordedAt, `${path}/recordedAt`);
|
|
2795
|
+
if (claim.subject.kind === "measurement")
|
|
2796
|
+
resolve(
|
|
2797
|
+
`measurement:${claim.subject.measurementId}`,
|
|
2798
|
+
`${path}/subject/measurementId`
|
|
2799
|
+
);
|
|
2800
|
+
if (claim.supersedes) {
|
|
2801
|
+
refs2(claim.supersedes, `${path}/supersedes`, owner);
|
|
2802
|
+
for (const [j, ref2] of claim.supersedes.entries()) {
|
|
2803
|
+
const previous = claims.get(ref2);
|
|
2804
|
+
if (!previous) continue;
|
|
2805
|
+
superseded.add(ref2);
|
|
2806
|
+
if (previous.kind !== claim.kind || previous.topic !== claim.topic || JSON.stringify(previous.subject) !== JSON.stringify(claim.subject)) {
|
|
2807
|
+
problem(
|
|
2808
|
+
"CLAIM.SUPERSESSION_SCOPE",
|
|
2809
|
+
`${path}/supersedes/${j}`,
|
|
2810
|
+
"A revision must retain the claim kind, topic, and subject. Record a different claim separately."
|
|
2811
|
+
);
|
|
2812
|
+
}
|
|
2813
|
+
if (previous.kind === "assessment" && previous.status === "assessed" && claim.kind === "assessment" && (claim.status !== "assessed" || previous.evaluatedBy !== claim.evaluatedBy)) {
|
|
2814
|
+
problem(
|
|
2815
|
+
"CLAIM.REVISION_ATTRIBUTION",
|
|
2816
|
+
`${path}/supersedes/${j}`,
|
|
2817
|
+
"An assessed revision must retain its evaluator; other evaluators record independent assessments."
|
|
2818
|
+
);
|
|
2819
|
+
}
|
|
2820
|
+
if (comparePoints(point3(previous.recordedAt), point3(claim.recordedAt)) > 0) {
|
|
2821
|
+
problem(
|
|
2822
|
+
"CLAIM.REVISION_ORDER",
|
|
2823
|
+
`${path}/recordedAt`,
|
|
2824
|
+
"A revision cannot be recorded before the statement it supersedes."
|
|
2825
|
+
);
|
|
2826
|
+
}
|
|
2827
|
+
}
|
|
2828
|
+
}
|
|
2829
|
+
if (claim.kind === "source_assertion") {
|
|
2830
|
+
resolve(claim.provenance.sourceRef, `${path}/provenance/sourceRef`);
|
|
2831
|
+
const source = sources.get(claim.provenance.sourceRef) ?? products.get(claim.provenance.sourceRef);
|
|
2832
|
+
if (source?.digest && claim.provenance.sourceDigest && source.digest.value !== claim.provenance.sourceDigest.value) {
|
|
2833
|
+
problem(
|
|
2834
|
+
"SOURCE.DIGEST_MISMATCH",
|
|
2835
|
+
`${path}/provenance/sourceDigest`,
|
|
2836
|
+
"Digest differs from the inventory declaration; neither is verified against bytes."
|
|
2837
|
+
);
|
|
2838
|
+
}
|
|
2839
|
+
const locator = claim.provenance.locator;
|
|
2840
|
+
if (locator?.kind === "time_range" && locator.endSeconds < locator.startSeconds)
|
|
2841
|
+
problem(
|
|
2842
|
+
"TIME.INTERVAL",
|
|
2843
|
+
`${path}/provenance/locator/endSeconds`,
|
|
2844
|
+
"Locator end precedes its start."
|
|
2845
|
+
);
|
|
2846
|
+
} else {
|
|
2847
|
+
refs2(claim.inputRefs, `${path}/inputRefs`, owner);
|
|
2848
|
+
if (claim.status === "assessed") {
|
|
2849
|
+
instant(claim.evaluatedAt, `${path}/evaluatedAt`);
|
|
2850
|
+
if (comparePoints(point3(claim.evaluatedAt), point3(claim.recordedAt)) > 0)
|
|
2851
|
+
problem(
|
|
2852
|
+
"CLAIM.EVALUATION_ORDER",
|
|
2853
|
+
`${path}/evaluatedAt`,
|
|
2854
|
+
"Evaluation cannot occur after the assessment was recorded."
|
|
2855
|
+
);
|
|
2856
|
+
const method = methods.get(claim.methodRef);
|
|
2857
|
+
if (!method)
|
|
2858
|
+
problem(
|
|
2859
|
+
"REF.LOCAL_RESOLUTION",
|
|
2860
|
+
`${path}/methodRef`,
|
|
2861
|
+
"Assessment method is not declared in the Observation inventory."
|
|
2862
|
+
);
|
|
2863
|
+
else if (method.version !== claim.methodVersion)
|
|
2864
|
+
problem(
|
|
2865
|
+
"METHOD.VERSION_MISMATCH",
|
|
2866
|
+
`${path}/methodVersion`,
|
|
2867
|
+
"Assessment method version differs from its inventory declaration."
|
|
2868
|
+
);
|
|
2869
|
+
}
|
|
2870
|
+
}
|
|
2871
|
+
}
|
|
2872
|
+
const indegrees = new Map([...graph.keys()].map((ref2) => [ref2, 0]));
|
|
2873
|
+
for (const edges of graph.values())
|
|
2874
|
+
for (const ref2 of edges) indegrees.set(ref2, indegrees.get(ref2) + 1);
|
|
2875
|
+
const queue = [...indegrees].filter(([, degree]) => degree === 0).map(([ref2]) => ref2);
|
|
2876
|
+
for (let i = 0; i < queue.length; i++)
|
|
2877
|
+
for (const ref2 of graph.get(queue[i]) ?? []) {
|
|
2878
|
+
const degree = indegrees.get(ref2) - 1;
|
|
2879
|
+
indegrees.set(ref2, degree);
|
|
2880
|
+
if (degree === 0) queue.push(ref2);
|
|
2881
|
+
}
|
|
2882
|
+
if (queue.length !== graph.size)
|
|
2883
|
+
problem(
|
|
2884
|
+
"CLAIM.DEPENDENCY_CYCLE",
|
|
2885
|
+
"/claims",
|
|
2886
|
+
"Claim inputs and supersession must form an acyclic history."
|
|
2887
|
+
);
|
|
2888
|
+
const validation = {
|
|
2889
|
+
contract,
|
|
2890
|
+
issues,
|
|
2891
|
+
uncheckedRefs: [
|
|
2892
|
+
...data.contextRefs.map((r) => r.sha256),
|
|
2893
|
+
...data.entityRefs.map((r) => r.sha256),
|
|
2894
|
+
...observationResult.uncheckedRefs,
|
|
2895
|
+
...data.claims.flatMap((c) => [
|
|
2896
|
+
...c.subject.kind === "context" ? [c.subject.reference.document.sha256] : [],
|
|
2897
|
+
...c.kind === "assessment" ? (c.contextInputRefs ?? []).map((r) => r.document.sha256) : []
|
|
2898
|
+
])
|
|
2899
|
+
],
|
|
2900
|
+
checks: {
|
|
2901
|
+
structural: "passed",
|
|
2902
|
+
semantic: issues.length ? "failed" : "passed",
|
|
2903
|
+
profile: "not_checked",
|
|
2904
|
+
external: "not_checked"
|
|
2905
|
+
}
|
|
2906
|
+
};
|
|
2907
|
+
return issues.length ? { ...validation, success: false } : {
|
|
2908
|
+
...validation,
|
|
2909
|
+
success: true,
|
|
2910
|
+
data,
|
|
2911
|
+
currentClaimRefs: [...claims.keys()].filter(
|
|
2912
|
+
(ref2) => !superseded.has(ref2)
|
|
2913
|
+
)
|
|
2914
|
+
};
|
|
2915
|
+
}
|
|
2916
|
+
|
|
2917
|
+
// src/experimental/v2/research-claim-history.ts
|
|
2918
|
+
function parseResearchClaimHistory(input) {
|
|
2919
|
+
return parseEntityClaimHistory(input, ResearchClaimHistorySchema, {
|
|
2920
|
+
kind: "claim_history",
|
|
2921
|
+
schemaId: RESEARCH_CLAIM_HISTORY_SCHEMA_ID,
|
|
2922
|
+
rulesetVersion: RESEARCH_CLAIM_HISTORY_RULESET_VERSION
|
|
2923
|
+
});
|
|
2924
|
+
}
|
|
2925
|
+
var text8 = z.string().min(1).regex(/\S/);
|
|
2926
|
+
var id7 = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]*(?![\s\S])/);
|
|
2927
|
+
var sourceRef4 = z.string().regex(/^(source|product):[A-Za-z0-9][A-Za-z0-9._-]*(?![\s\S])/);
|
|
2928
|
+
var provenance4 = SourceProvenanceSchema.extend({ sourceRef: sourceRef4 });
|
|
2929
|
+
function values3(schema) {
|
|
2930
|
+
return z.array(
|
|
2931
|
+
z.strictObject({
|
|
2932
|
+
id: id7.describe(
|
|
2933
|
+
"Assertion identity within this entity, independent of array order."
|
|
2934
|
+
),
|
|
2935
|
+
content: z.union([
|
|
2936
|
+
z.strictObject({
|
|
2937
|
+
state: z.literal("known"),
|
|
2938
|
+
value: schema,
|
|
2939
|
+
provenance: provenance4,
|
|
2940
|
+
originalWording: text8.optional()
|
|
2941
|
+
}),
|
|
2942
|
+
z.strictObject({
|
|
2943
|
+
state: z.literal("approximate"),
|
|
2944
|
+
value: schema,
|
|
2945
|
+
precision: text8,
|
|
2946
|
+
provenance: provenance4,
|
|
2947
|
+
originalWording: text8.optional()
|
|
2948
|
+
}),
|
|
2949
|
+
z.strictObject({
|
|
2950
|
+
state: z.literal("unknown"),
|
|
2951
|
+
reason: z.enum(["not_recorded", "not_collected", "unavailable"])
|
|
2952
|
+
}),
|
|
2953
|
+
z.strictObject({ state: z.literal("redacted"), reason: text8 }),
|
|
2954
|
+
z.strictObject({
|
|
2955
|
+
state: z.literal("unmapped"),
|
|
2956
|
+
originalWording: text8,
|
|
2957
|
+
provenance: provenance4
|
|
2958
|
+
})
|
|
2959
|
+
])
|
|
2960
|
+
})
|
|
2961
|
+
).min(1).optional();
|
|
2962
|
+
}
|
|
2963
|
+
function entity3(kind, fields) {
|
|
2964
|
+
return z.strictObject({
|
|
2965
|
+
kind: z.literal(kind),
|
|
2966
|
+
id: id7,
|
|
2967
|
+
fields: z.strictObject(fields)
|
|
2968
|
+
});
|
|
2969
|
+
}
|
|
2970
|
+
function target3(kind, fields) {
|
|
2971
|
+
return z.strictObject({
|
|
2972
|
+
kind: z.literal(kind),
|
|
2973
|
+
id: id7,
|
|
2974
|
+
field: z.enum(Object.keys(fields)).optional()
|
|
2975
|
+
});
|
|
2976
|
+
}
|
|
2977
|
+
var DocumentTypeSchema = z.enum([
|
|
2978
|
+
"foia_release",
|
|
2979
|
+
"official_report",
|
|
2980
|
+
"congressional_record",
|
|
2981
|
+
"military_memo",
|
|
2982
|
+
"intelligence_assessment",
|
|
2983
|
+
"inspector_general",
|
|
2984
|
+
"presidential_directive",
|
|
2985
|
+
"executive_order",
|
|
2986
|
+
"declassified_cable",
|
|
2987
|
+
"investigation_file",
|
|
2988
|
+
"other"
|
|
2989
|
+
]).describe("Kind of government/official document.");
|
|
2990
|
+
z.object({
|
|
2991
|
+
documentType: DocumentTypeSchema,
|
|
2992
|
+
classificationLevel: z.string().optional().describe('Current classification of the document (e.g. "UNCLASSIFIED", "SECRET//NOFORN").'),
|
|
2993
|
+
originatingAgency: z.string().optional().describe("Agency that produced the document."),
|
|
2994
|
+
controlNumbers: z.array(z.string()).optional().describe("Agency control or tracking numbers stamped on the document."),
|
|
2995
|
+
foiaRequestNumber: z.string().optional().describe("FOIA request number that produced the document."),
|
|
2996
|
+
declassificationDate: z.string().optional().describe("When the document was declassified (ISO date)."),
|
|
2997
|
+
originalClassification: z.string().optional().describe("Classification the document carried when created."),
|
|
2998
|
+
pageCount: z.number().optional().describe("Number of pages in the document."),
|
|
2999
|
+
redactionLevel: z.enum(["none", "partial", "heavy", "unknown"]).optional().describe("How much of the document is redacted.")
|
|
3000
|
+
}).meta({ id: "DocumentMetadata" }).describe("Provenance metadata for a source document (classification, agency, redaction).");
|
|
3001
|
+
var IdentifierSystemSchema = z.enum([
|
|
3002
|
+
"nuforc",
|
|
3003
|
+
"mufon",
|
|
3004
|
+
"geipan",
|
|
3005
|
+
"cufos",
|
|
3006
|
+
"nicap",
|
|
3007
|
+
"bluebook",
|
|
3008
|
+
"nara",
|
|
3009
|
+
"foia",
|
|
3010
|
+
"aaro",
|
|
3011
|
+
"congress",
|
|
3012
|
+
"doi",
|
|
3013
|
+
"arxiv",
|
|
3014
|
+
"orcid",
|
|
3015
|
+
"pubmed",
|
|
3016
|
+
"isbn",
|
|
3017
|
+
"issn",
|
|
3018
|
+
"youtube",
|
|
3019
|
+
"vimeo",
|
|
3020
|
+
"internet_archive",
|
|
3021
|
+
"wayback",
|
|
3022
|
+
"url",
|
|
3023
|
+
"uri",
|
|
3024
|
+
"custom"
|
|
3025
|
+
]).describe("External identifier namespace (reporting centers, archives, academic registries, media hosts).");
|
|
3026
|
+
var ExternalIdentifierSchema = z.object({
|
|
3027
|
+
system: IdentifierSystemSchema,
|
|
3028
|
+
systemName: z.string().optional().describe('Display name of the system, required when `system` is "custom".'),
|
|
3029
|
+
value: z.string().describe("The identifier value within the external system (e.g. a case number)."),
|
|
3030
|
+
url: z.string().optional().describe("Direct link to the record in the external system."),
|
|
3031
|
+
verified: z.boolean().optional().describe("Whether the identifier has been confirmed to point at this observation."),
|
|
3032
|
+
verifiedDate: z.string().optional().describe("When the identifier was last verified (ISO date)."),
|
|
3033
|
+
accessible: z.boolean().optional().describe("Whether the external record is currently reachable."),
|
|
3034
|
+
accessCheckedDate: z.string().optional().describe("When accessibility was last checked (ISO date)."),
|
|
3035
|
+
notes: z.string().optional().describe("Free-text context about the cross-reference.")
|
|
3036
|
+
}).meta({ id: "ExternalIdentifier" }).describe("A reference to this observation in an external system or archive.");
|
|
3037
|
+
z.object({
|
|
3038
|
+
primary: ExternalIdentifierSchema.optional(),
|
|
3039
|
+
identifiers: z.array(ExternalIdentifierSchema)
|
|
3040
|
+
}).meta({ id: "CrossReferences" }).describe("External identifiers cross-referencing this observation, with an optional primary.");
|
|
3041
|
+
|
|
3042
|
+
// src/experimental/v2/archival-entities-schema.ts
|
|
3043
|
+
var text9 = z.string().min(1).regex(/\S/);
|
|
3044
|
+
var id8 = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]*(?![\s\S])/);
|
|
3045
|
+
var refs = z.array(id8).min(1);
|
|
3046
|
+
var url = z.url();
|
|
3047
|
+
var ArtifactSnapshotRefSchema = z.strictObject({
|
|
3048
|
+
sourceRef: z.string().regex(/^(source|product):[A-Za-z0-9][A-Za-z0-9._-]*(?![\s\S])/),
|
|
3049
|
+
digest: z.strictObject({
|
|
3050
|
+
algorithm: z.literal("sha256"),
|
|
3051
|
+
value: z.string().regex(/^[a-f0-9]{64}(?![\s\S])/)
|
|
3052
|
+
})
|
|
3053
|
+
}).describe(
|
|
3054
|
+
"One exact artifact in the scoped observation inventory. A URL, title or legacy hash cannot substitute for this identity."
|
|
3055
|
+
);
|
|
3056
|
+
var SourceEditionFields = {
|
|
3057
|
+
title: values3(text9),
|
|
3058
|
+
type: values3(SourceTypeSchema),
|
|
3059
|
+
author: values3(text9),
|
|
3060
|
+
organization: values3(text9),
|
|
3061
|
+
issuedAt: values3(EventTimeValueSchema),
|
|
3062
|
+
archiveLocation: values3(text9),
|
|
3063
|
+
primaryDisplay: values3(z.boolean()),
|
|
3064
|
+
originalUrl: values3(url),
|
|
3065
|
+
archiveUrl: values3(url),
|
|
3066
|
+
reference: values3(text9),
|
|
3067
|
+
isPrimarySource: values3(
|
|
3068
|
+
z.strictObject({ value: z.boolean(), context: text9 })
|
|
3069
|
+
),
|
|
3070
|
+
documentType: values3(DocumentTypeSchema),
|
|
3071
|
+
originatingAgency: values3(text9),
|
|
3072
|
+
controlNumbers: values3(z.array(text9).min(1)),
|
|
3073
|
+
foiaRequestNumber: values3(text9),
|
|
3074
|
+
originalClassification: values3(text9),
|
|
3075
|
+
classificationLevel: values3(text9),
|
|
3076
|
+
pageCount: values3(z.number().int().min(1)),
|
|
3077
|
+
redactionLevel: values3(z.enum(["none", "partial", "heavy", "unknown"])),
|
|
3078
|
+
redactionDescription: values3(text9),
|
|
3079
|
+
redactionPercent: values3(z.number().min(0).max(100)),
|
|
3080
|
+
notes: values3(text9)
|
|
3081
|
+
};
|
|
3082
|
+
var DocumentEventFields = {
|
|
3083
|
+
eventType: values3(
|
|
3084
|
+
z.enum(["creation", "release", "classification", "declassification"])
|
|
3085
|
+
),
|
|
3086
|
+
occurredAt: values3(EventTimeValueSchema),
|
|
3087
|
+
authority: values3(text9),
|
|
3088
|
+
reference: values3(text9),
|
|
3089
|
+
marking: values3(text9),
|
|
3090
|
+
notes: values3(text9)
|
|
3091
|
+
};
|
|
3092
|
+
var declaredHash = z.discriminatedUnion("algorithm", [
|
|
3093
|
+
z.strictObject({
|
|
3094
|
+
algorithm: z.literal("sha256"),
|
|
3095
|
+
value: z.string().regex(/^[a-fA-F0-9]{64}(?![\s\S])/)
|
|
3096
|
+
}),
|
|
3097
|
+
z.strictObject({
|
|
3098
|
+
algorithm: z.literal("sha512"),
|
|
3099
|
+
value: z.string().regex(/^[a-fA-F0-9]{128}(?![\s\S])/)
|
|
3100
|
+
}),
|
|
3101
|
+
z.strictObject({
|
|
3102
|
+
algorithm: z.literal("sha1"),
|
|
3103
|
+
value: z.string().regex(/^[a-fA-F0-9]{40}(?![\s\S])/)
|
|
3104
|
+
}),
|
|
3105
|
+
z.strictObject({
|
|
3106
|
+
algorithm: z.literal("md5"),
|
|
3107
|
+
value: z.string().regex(/^[a-fA-F0-9]{32}(?![\s\S])/)
|
|
3108
|
+
})
|
|
3109
|
+
]);
|
|
3110
|
+
var DigitalArtifactFields = {
|
|
3111
|
+
declaredHashes: values3(
|
|
3112
|
+
z.array(
|
|
3113
|
+
z.strictObject({
|
|
3114
|
+
hash: declaredHash,
|
|
3115
|
+
appliesTo: z.union([
|
|
3116
|
+
z.strictObject({
|
|
3117
|
+
kind: z.literal("artifact"),
|
|
3118
|
+
reference: ArtifactSnapshotRefSchema
|
|
3119
|
+
}),
|
|
3120
|
+
z.strictObject({
|
|
3121
|
+
kind: z.literal("unavailable_original"),
|
|
3122
|
+
description: text9
|
|
3123
|
+
})
|
|
3124
|
+
])
|
|
3125
|
+
})
|
|
3126
|
+
).min(1)
|
|
3127
|
+
),
|
|
3128
|
+
device: values3(text9),
|
|
3129
|
+
manufacturer: values3(text9),
|
|
3130
|
+
model: values3(text9),
|
|
3131
|
+
software: values3(text9),
|
|
3132
|
+
originalFilename: values3(text9),
|
|
3133
|
+
originalCreationDate: values3(EventTimeValueSchema),
|
|
3134
|
+
gpsCoordinates: values3(
|
|
3135
|
+
z.strictObject({
|
|
3136
|
+
latitude: z.number().min(-90).max(90),
|
|
3137
|
+
longitude: z.number().min(-180).max(180),
|
|
3138
|
+
altitude: z.strictObject({
|
|
3139
|
+
value: z.number(),
|
|
3140
|
+
unit: z.union([text9, z.null()]),
|
|
3141
|
+
datum: text9.optional()
|
|
3142
|
+
}).optional()
|
|
3143
|
+
})
|
|
3144
|
+
),
|
|
3145
|
+
metadataPreserved: values3(z.boolean()),
|
|
3146
|
+
metadataStripped: values3(z.boolean()),
|
|
3147
|
+
strippedReason: values3(text9),
|
|
3148
|
+
custodyStatus: values3(ChainOfCustodySchema),
|
|
3149
|
+
provenanceChainDescription: values3(z.array(text9).min(1)),
|
|
3150
|
+
derivedFromArtifactIds: values3(refs),
|
|
3151
|
+
lastUpdated: values3(EventTimeValueSchema),
|
|
3152
|
+
notes: values3(text9)
|
|
3153
|
+
};
|
|
3154
|
+
var DigitalCustodyFields = {
|
|
3155
|
+
action: values3(CustodyActionSchema),
|
|
3156
|
+
occurredAt: values3(EventTimeValueSchema),
|
|
3157
|
+
from: values3(text9),
|
|
3158
|
+
to: values3(text9),
|
|
3159
|
+
location: values3(text9),
|
|
3160
|
+
reference: values3(text9),
|
|
3161
|
+
notes: values3(text9)
|
|
3162
|
+
};
|
|
3163
|
+
var ExternalIdentifierFields = {
|
|
3164
|
+
value: values3(
|
|
3165
|
+
z.strictObject({
|
|
3166
|
+
system: IdentifierSystemSchema,
|
|
3167
|
+
systemName: text9.optional(),
|
|
3168
|
+
value: text9,
|
|
3169
|
+
url: url.optional(),
|
|
3170
|
+
notes: text9.optional()
|
|
3171
|
+
})
|
|
3172
|
+
),
|
|
3173
|
+
primaryDisplay: values3(z.boolean()),
|
|
3174
|
+
notes: values3(text9)
|
|
3175
|
+
};
|
|
3176
|
+
var IdentifierCheckFields = {
|
|
3177
|
+
checkedAt: values3(EventTimeValueSchema),
|
|
3178
|
+
agent: values3(text9),
|
|
3179
|
+
method: values3(text9),
|
|
3180
|
+
methodVersion: values3(text9),
|
|
3181
|
+
outcome: values3(
|
|
3182
|
+
z.discriminatedUnion("kind", [
|
|
3183
|
+
z.strictObject({
|
|
3184
|
+
kind: z.literal("access"),
|
|
3185
|
+
result: z.enum(["accessible", "inaccessible", "unknown"])
|
|
3186
|
+
}),
|
|
3187
|
+
z.strictObject({
|
|
3188
|
+
kind: z.literal("identity"),
|
|
3189
|
+
result: z.enum(["matches", "does_not_match", "unknown"])
|
|
3190
|
+
})
|
|
3191
|
+
])
|
|
3192
|
+
),
|
|
3193
|
+
notes: values3(text9)
|
|
3194
|
+
};
|
|
3195
|
+
for (const fields of [
|
|
3196
|
+
SourceEditionFields,
|
|
3197
|
+
DocumentEventFields,
|
|
3198
|
+
DigitalArtifactFields,
|
|
3199
|
+
DigitalCustodyFields,
|
|
3200
|
+
ExternalIdentifierFields,
|
|
3201
|
+
IdentifierCheckFields
|
|
3202
|
+
])
|
|
3203
|
+
for (const [key2, schema] of Object.entries(fields))
|
|
3204
|
+
fields[key2] = schema.describe(
|
|
3205
|
+
`Sourced ${key2.replace(/([a-z])([A-Z])/g, "$1 $2").toLowerCase()} assertions. Alternatives and missingness are retained; declarations are not independent verification.`
|
|
3206
|
+
);
|
|
3207
|
+
var edition = entity3("source_edition", SourceEditionFields).extend({
|
|
3208
|
+
artifact: ArtifactSnapshotRefSchema
|
|
3209
|
+
});
|
|
3210
|
+
var artifact = entity3("digital_artifact", DigitalArtifactFields).extend({
|
|
3211
|
+
artifact: ArtifactSnapshotRefSchema
|
|
3212
|
+
});
|
|
3213
|
+
var documentEvent = entity3("document_event", DocumentEventFields).extend({
|
|
3214
|
+
editionId: id8
|
|
3215
|
+
});
|
|
3216
|
+
var custody = entity3("digital_custody_action", DigitalCustodyFields).extend({
|
|
3217
|
+
artifactId: id8,
|
|
3218
|
+
predecessor: z.discriminatedUnion("kind", [
|
|
3219
|
+
z.strictObject({ kind: z.literal("action"), id: id8 }),
|
|
3220
|
+
z.strictObject({ kind: z.literal("start") }),
|
|
3221
|
+
z.strictObject({ kind: z.literal("unknown"), reason: text9 })
|
|
3222
|
+
]).describe(
|
|
3223
|
+
"Declared preceding action on the same artifact, a declared beginning, or an explicit gap. Array order carries no chronology."
|
|
3224
|
+
)
|
|
3225
|
+
});
|
|
3226
|
+
var identifier2 = entity3(
|
|
3227
|
+
"external_identifier",
|
|
3228
|
+
ExternalIdentifierFields
|
|
3229
|
+
).extend({
|
|
3230
|
+
subject: z.discriminatedUnion("kind", [
|
|
3231
|
+
z.strictObject({ kind: z.literal("observation") }),
|
|
3232
|
+
z.strictObject({ kind: z.literal("source_edition"), editionId: id8 })
|
|
3233
|
+
]).describe(
|
|
3234
|
+
"The scoped observation or one exact source edition. A catalog identifier is not automatically an event identifier."
|
|
3235
|
+
)
|
|
3236
|
+
});
|
|
3237
|
+
var check = entity3("identifier_check", IdentifierCheckFields).extend({
|
|
3238
|
+
identifierId: id8,
|
|
3239
|
+
identifierAssertionId: id8
|
|
3240
|
+
});
|
|
3241
|
+
var ArchivalEntitySchema = z.discriminatedUnion("kind", [
|
|
3242
|
+
...ResearchEntitySchema.options,
|
|
3243
|
+
edition,
|
|
3244
|
+
documentEvent,
|
|
3245
|
+
artifact,
|
|
3246
|
+
custody,
|
|
3247
|
+
identifier2,
|
|
3248
|
+
check
|
|
3249
|
+
]);
|
|
3250
|
+
var ARCHIVAL_ENTITIES_SCHEMA_ID = "urn:disclosureos:experimental:research-entities:0.2.0";
|
|
3251
|
+
var ArchivalEntitiesSchema = ResearchEntitiesSchema.extend({
|
|
3252
|
+
schemaVersion: z.literal("0.2.0"),
|
|
3253
|
+
entities: z.array(ArchivalEntitySchema).min(1)
|
|
3254
|
+
}).describe(
|
|
3255
|
+
"Public research declarations including exact source editions and digital artifact custody. No source authentication, public-release permission or complete custody is inferred."
|
|
3256
|
+
);
|
|
3257
|
+
var ArchivalEntityTargetSchema = z.discriminatedUnion("kind", [
|
|
3258
|
+
...ResearchEntityTargetSchema.options,
|
|
3259
|
+
target3("source_edition", SourceEditionFields),
|
|
3260
|
+
target3("document_event", DocumentEventFields),
|
|
3261
|
+
target3("digital_artifact", DigitalArtifactFields),
|
|
3262
|
+
target3("digital_custody_action", DigitalCustodyFields),
|
|
3263
|
+
target3("external_identifier", ExternalIdentifierFields),
|
|
3264
|
+
target3("identifier_check", IdentifierCheckFields)
|
|
3265
|
+
]);
|
|
3266
|
+
var ArchivalEntityReferenceSchema = z.strictObject({
|
|
3267
|
+
document: DocumentSnapshotRefSchema.extend({
|
|
3268
|
+
schemaId: z.literal(ARCHIVAL_ENTITIES_SCHEMA_ID)
|
|
3269
|
+
}),
|
|
3270
|
+
target: ArchivalEntityTargetSchema
|
|
3271
|
+
});
|
|
3272
|
+
var ArchivalEntityAssertionReferenceSchema = ArchivalEntityReferenceSchema.extend({ assertionId: id8 });
|
|
3273
|
+
function archivalEntitiesJsonSchema() {
|
|
3274
|
+
return {
|
|
3275
|
+
...z.toJSONSchema(ArchivalEntitiesSchema, {
|
|
3276
|
+
target: "draft-2020-12",
|
|
3277
|
+
reused: "ref"
|
|
3278
|
+
}),
|
|
3279
|
+
$id: ARCHIVAL_ENTITIES_SCHEMA_ID
|
|
3280
|
+
};
|
|
3281
|
+
}
|
|
3282
|
+
|
|
3283
|
+
// src/experimental/v2/archival-entities.ts
|
|
3284
|
+
function archivalArtifactReferences(entity4) {
|
|
3285
|
+
const direct = "artifact" in entity4 ? [entity4.artifact] : [];
|
|
3286
|
+
if (entity4.kind !== "digital_artifact") return direct;
|
|
3287
|
+
return [
|
|
3288
|
+
...direct,
|
|
3289
|
+
...(entity4.fields.declaredHashes ?? []).flatMap(
|
|
3290
|
+
(a) => "value" in a.content ? a.content.value.flatMap(
|
|
3291
|
+
(d) => d.appliesTo.kind === "artifact" ? [d.appliesTo.reference] : []
|
|
3292
|
+
) : []
|
|
3293
|
+
)
|
|
3294
|
+
];
|
|
3295
|
+
}
|
|
3296
|
+
function parseArchivalEntities(input) {
|
|
3297
|
+
const result = parseEntityDocument(input, ArchivalEntitiesSchema, {
|
|
3298
|
+
schemaId: ARCHIVAL_ENTITIES_SCHEMA_ID,
|
|
3299
|
+
rulesetVersion: "0.2.0"
|
|
3300
|
+
});
|
|
3301
|
+
if (!result.success) return result;
|
|
3302
|
+
const data = result.data, issues = [];
|
|
3303
|
+
const problem = (code, pointer6, message) => issues.push({ code, stage: "semantic", pointer: pointer6, message });
|
|
3304
|
+
const index = new Map(data.entities.map((e) => [`${e.kind}:${e.id}`, e]));
|
|
3305
|
+
const unchecked = new Set(result.uncheckedRefs);
|
|
3306
|
+
const derivation = /* @__PURE__ */ new Map(), custody2 = /* @__PURE__ */ new Map();
|
|
3307
|
+
const known = (entity4, field) => {
|
|
3308
|
+
const a = researchEntityAssertions(entity4).filter((a2) => a2.field === field);
|
|
3309
|
+
return a.length === 1 && a[0].assertion.content.state === "known" ? a[0].assertion.content.value : void 0;
|
|
3310
|
+
};
|
|
3311
|
+
for (const [i, e] of data.entities.entries()) {
|
|
3312
|
+
const p = `/entities/${i}`;
|
|
3313
|
+
const link = (kind, id10, field) => {
|
|
3314
|
+
const target4 = index.get(`${kind}:${id10}`);
|
|
3315
|
+
if (!target4)
|
|
3316
|
+
problem(
|
|
3317
|
+
"REF.ENTITY",
|
|
3318
|
+
`${p}/${field}`,
|
|
3319
|
+
"Typed local archival entity does not exist."
|
|
3320
|
+
);
|
|
3321
|
+
return target4;
|
|
3322
|
+
};
|
|
3323
|
+
for (const ref2 of archivalArtifactReferences(e))
|
|
3324
|
+
unchecked.add(ref2.sourceRef);
|
|
3325
|
+
if (e.kind === "document_event")
|
|
3326
|
+
link("source_edition", e.editionId, "editionId");
|
|
3327
|
+
if (e.kind === "external_identifier" && e.subject.kind === "source_edition")
|
|
3328
|
+
link("source_edition", e.subject.editionId, "subject/editionId");
|
|
3329
|
+
if (e.kind === "identifier_check") {
|
|
3330
|
+
const identifier3 = link(
|
|
3331
|
+
"external_identifier",
|
|
3332
|
+
e.identifierId,
|
|
3333
|
+
"identifierId"
|
|
3334
|
+
);
|
|
3335
|
+
if (identifier3?.kind === "external_identifier" && !identifier3.fields.value?.some((a) => a.id === e.identifierAssertionId))
|
|
3336
|
+
problem(
|
|
3337
|
+
"REF.ASSERTION",
|
|
3338
|
+
`${p}/identifierAssertionId`,
|
|
3339
|
+
"Identifier check must name an existing value assertion in the selected identifier revision."
|
|
3340
|
+
);
|
|
3341
|
+
}
|
|
3342
|
+
for (const { field, assertion } of researchEntityAssertions(e)) {
|
|
3343
|
+
const value = assertion.content.value;
|
|
3344
|
+
if (value === void 0) continue;
|
|
3345
|
+
if ([
|
|
3346
|
+
"issuedAt",
|
|
3347
|
+
"occurredAt",
|
|
3348
|
+
"originalCreationDate",
|
|
3349
|
+
"lastUpdated",
|
|
3350
|
+
"checkedAt"
|
|
3351
|
+
].includes(field)) {
|
|
3352
|
+
const found = [];
|
|
3353
|
+
checkTime(value, `${p}/fields/${field}`, found);
|
|
3354
|
+
found.forEach((v) => problem(v.code, v.pointer, v.message));
|
|
3355
|
+
}
|
|
3356
|
+
if (e.kind === "external_identifier" && field === "value") {
|
|
3357
|
+
const v = value;
|
|
3358
|
+
if (v.system === "custom" && !v.systemName)
|
|
3359
|
+
problem(
|
|
3360
|
+
"ARCHIVE.IDENTIFIER_SYSTEM",
|
|
3361
|
+
`${p}/fields/value`,
|
|
3362
|
+
"A custom identifier system needs a declared system name."
|
|
3363
|
+
);
|
|
3364
|
+
}
|
|
3365
|
+
}
|
|
3366
|
+
if (e.kind === "digital_artifact") {
|
|
3367
|
+
const parents = (e.fields.derivedFromArtifactIds ?? []).flatMap(
|
|
3368
|
+
(a) => "value" in a.content ? a.content.value : []
|
|
3369
|
+
);
|
|
3370
|
+
for (const a of e.fields.derivedFromArtifactIds ?? [])
|
|
3371
|
+
if ("value" in a.content && new Set(a.content.value).size !== a.content.value.length)
|
|
3372
|
+
problem(
|
|
3373
|
+
"REF.UNIQUE_ID",
|
|
3374
|
+
`${p}/fields/derivedFromArtifactIds`,
|
|
3375
|
+
"Parent artifact links repeat within an assertion."
|
|
3376
|
+
);
|
|
3377
|
+
parents.forEach(
|
|
3378
|
+
(id10) => link("digital_artifact", id10, "fields/derivedFromArtifactIds")
|
|
3379
|
+
);
|
|
3380
|
+
derivation.set(e.id, [...new Set(parents)]);
|
|
3381
|
+
}
|
|
3382
|
+
if (e.kind === "digital_custody_action") {
|
|
3383
|
+
link("digital_artifact", e.artifactId, "artifactId");
|
|
3384
|
+
custody2.set(
|
|
3385
|
+
e.id,
|
|
3386
|
+
e.predecessor.kind === "action" ? [e.predecessor.id] : []
|
|
3387
|
+
);
|
|
3388
|
+
if (e.predecessor.kind === "action") {
|
|
3389
|
+
const previous = link(
|
|
3390
|
+
"digital_custody_action",
|
|
3391
|
+
e.predecessor.id,
|
|
3392
|
+
"predecessor"
|
|
3393
|
+
);
|
|
3394
|
+
if (previous?.kind !== "digital_custody_action") continue;
|
|
3395
|
+
if (previous.artifactId !== e.artifactId)
|
|
3396
|
+
problem(
|
|
3397
|
+
"ARCHIVE.CUSTODY_SCOPE",
|
|
3398
|
+
`${p}/predecessor`,
|
|
3399
|
+
"A custody predecessor must concern the same exact artifact, not a different edition or derived file."
|
|
3400
|
+
);
|
|
3401
|
+
const from = known(e, "from"), to = known(previous, "to");
|
|
3402
|
+
if (typeof from === "string" && typeof to === "string" && from !== to)
|
|
3403
|
+
problem(
|
|
3404
|
+
"ARCHIVE.CUSTODY_HANDOFF",
|
|
3405
|
+
`${p}/fields/from`,
|
|
3406
|
+
"Explicit predecessor recipient and next sender differ. Missing or conflicting parties are not repaired automatically."
|
|
3407
|
+
);
|
|
3408
|
+
const now = known(e, "occurredAt");
|
|
3409
|
+
const before = known(previous, "occurredAt");
|
|
3410
|
+
if (now && before && now.kind !== "interval" && before.kind === now.kind) {
|
|
3411
|
+
if (comparePoints(before, now) > 0)
|
|
3412
|
+
problem(
|
|
3413
|
+
"ARCHIVE.CUSTODY_ORDER",
|
|
3414
|
+
`${p}/fields/occurredAt`,
|
|
3415
|
+
"Declared custody action precedes its explicit predecessor."
|
|
3416
|
+
);
|
|
3417
|
+
}
|
|
3418
|
+
}
|
|
3419
|
+
}
|
|
3420
|
+
}
|
|
3421
|
+
function acyclic(graph, code) {
|
|
3422
|
+
const indegree = new Map([...graph.keys()].map((id10) => [id10, 0]));
|
|
3423
|
+
for (const edges of graph.values())
|
|
3424
|
+
for (const id10 of edges)
|
|
3425
|
+
if (indegree.has(id10)) indegree.set(id10, indegree.get(id10) + 1);
|
|
3426
|
+
const queue = [...indegree].filter(([, n]) => n === 0).map(([id10]) => id10);
|
|
3427
|
+
for (let i = 0; i < queue.length; i++)
|
|
3428
|
+
for (const id10 of graph.get(queue[i]) ?? [])
|
|
3429
|
+
if (indegree.has(id10)) {
|
|
3430
|
+
const count = indegree.get(id10) - 1;
|
|
3431
|
+
indegree.set(id10, count);
|
|
3432
|
+
if (count === 0) queue.push(id10);
|
|
3433
|
+
}
|
|
3434
|
+
if (queue.length !== graph.size)
|
|
3435
|
+
problem(
|
|
3436
|
+
code,
|
|
3437
|
+
"/entities",
|
|
3438
|
+
"Declared archival dependency links must be acyclic."
|
|
3439
|
+
);
|
|
3440
|
+
}
|
|
3441
|
+
acyclic(derivation, "ARCHIVE.DERIVATION_CYCLE");
|
|
3442
|
+
acyclic(custody2, "ARCHIVE.CUSTODY_CYCLE");
|
|
3443
|
+
const validation = {
|
|
3444
|
+
contract: result.contract,
|
|
3445
|
+
issues,
|
|
3446
|
+
uncheckedRefs: [...unchecked],
|
|
3447
|
+
checks: {
|
|
3448
|
+
...result.checks,
|
|
3449
|
+
semantic: issues.length ? "failed" : "passed"
|
|
3450
|
+
}
|
|
3451
|
+
};
|
|
3452
|
+
return issues.length ? { ...validation, success: false } : { ...validation, success: true, data };
|
|
3453
|
+
}
|
|
3454
|
+
var HashAlgorithmSchema = z.enum(["sha256", "sha512", "sha1", "md5"]).describe("Cryptographic hash algorithm used to fingerprint a file.");
|
|
3455
|
+
var FileHashSchema = z.object({
|
|
3456
|
+
algorithm: HashAlgorithmSchema,
|
|
3457
|
+
value: z.string().describe("Hex-encoded hash digest of the file contents.")
|
|
3458
|
+
}).meta({ id: "FileHash" }).describe("A content hash of a file.");
|
|
3459
|
+
var VerificationMethodSchema = z.enum([
|
|
3460
|
+
"metadata_analysis",
|
|
3461
|
+
"forensic_analysis",
|
|
3462
|
+
"hash_verification",
|
|
3463
|
+
"chain_of_custody",
|
|
3464
|
+
"source_confirmation",
|
|
3465
|
+
"expert_review",
|
|
3466
|
+
"ai_analysis",
|
|
3467
|
+
"other"
|
|
3468
|
+
]).describe("Method used to verify media authenticity.");
|
|
3469
|
+
var VerificationResultSchema = z.enum([
|
|
3470
|
+
"authentic",
|
|
3471
|
+
"likely_authentic",
|
|
3472
|
+
"inconclusive",
|
|
3473
|
+
"likely_manipulated",
|
|
3474
|
+
"manipulated",
|
|
3475
|
+
"ai_generated",
|
|
3476
|
+
"deepfake",
|
|
3477
|
+
"composite",
|
|
3478
|
+
"unknown"
|
|
3479
|
+
]).describe("Outcome of an authenticity verification.");
|
|
3480
|
+
var ThirdPartyVerificationSchema = z.object({
|
|
3481
|
+
verifier: z.string().describe("Name of the person who performed the verification."),
|
|
3482
|
+
organization: z.string().optional().describe("Organization the verifier represents."),
|
|
3483
|
+
credentials: z.string().optional().describe('Verifier\u2019s relevant qualifications (e.g. "certified forensic video analyst").'),
|
|
3484
|
+
date: z.string().describe("When the verification was performed (ISO date)."),
|
|
3485
|
+
method: VerificationMethodSchema,
|
|
3486
|
+
methodDescription: z.string().optional().describe("Fuller description of how the verification was performed."),
|
|
3487
|
+
result: VerificationResultSchema,
|
|
3488
|
+
confidence: z.enum(["high", "medium", "low"]).optional().describe("Verifier\u2019s confidence in their own result."),
|
|
3489
|
+
findings: z.string().optional().describe("Summary of what the verification found."),
|
|
3490
|
+
reportUrl: z.string().optional().describe("Link to the full verification report.")
|
|
3491
|
+
}).meta({ id: "ThirdPartyVerification" }).describe("An independent authenticity assessment of media or a document.");
|
|
3492
|
+
var CaptureMetadataSchema = z.object({
|
|
3493
|
+
device: z.string().optional().describe('Capture device as recorded in metadata (e.g. "iPhone 14 Pro").'),
|
|
3494
|
+
manufacturer: z.string().optional().describe("Device manufacturer from metadata."),
|
|
3495
|
+
model: z.string().optional().describe("Device model from metadata."),
|
|
3496
|
+
software: z.string().optional().describe("Software that produced or processed the file."),
|
|
3497
|
+
originalFilename: z.string().optional().describe("Filename as originally captured, before any renaming."),
|
|
3498
|
+
originalCreationDate: z.string().optional().describe("Creation timestamp embedded in the file metadata."),
|
|
3499
|
+
gpsCoordinates: z.object({
|
|
3500
|
+
latitude: z.number(),
|
|
3501
|
+
longitude: z.number(),
|
|
3502
|
+
altitude: z.number().optional()
|
|
3503
|
+
}).optional().describe("GPS position embedded in the file metadata."),
|
|
3504
|
+
metadataPreserved: z.boolean().optional().describe("Whether the original metadata survives intact."),
|
|
3505
|
+
metadataStripped: z.boolean().optional().describe("Whether metadata was removed at some point."),
|
|
3506
|
+
strippedReason: z.string().optional().describe('Why metadata was stripped (e.g. "platform re-encoding", "privacy redaction").')
|
|
3507
|
+
}).meta({ id: "CaptureMetadata" }).describe("Capture/EXIF-style metadata extracted from a media file.");
|
|
3508
|
+
z.object({
|
|
3509
|
+
hash: FileHashSchema.optional(),
|
|
3510
|
+
additionalHashes: z.array(FileHashSchema).optional(),
|
|
3511
|
+
captureMetadata: CaptureMetadataSchema.optional(),
|
|
3512
|
+
verifications: z.array(ThirdPartyVerificationSchema).optional(),
|
|
3513
|
+
provenanceChain: z.array(z.string()).optional().describe('Ordered hops the file took from capture to here (e.g. "witness phone \u2192 NYT \u2192 public release").'),
|
|
3514
|
+
manipulationDetected: z.boolean().optional().describe("Whether any analysis found evidence of manipulation."),
|
|
3515
|
+
manipulationDescription: z.string().optional().describe("What manipulation was found, if any."),
|
|
3516
|
+
authenticityAssessment: VerificationResultSchema.optional(),
|
|
3517
|
+
authenticityConfidence: z.enum(["high", "medium", "low", "unassessed"]).optional().describe("Overall confidence in the authenticity assessment."),
|
|
3518
|
+
lastUpdated: z.string().optional().describe("When this provenance record was last updated (ISO timestamp)."),
|
|
3519
|
+
notes: z.string().optional().describe("Free-text context about the provenance trail.")
|
|
3520
|
+
}).meta({ id: "DigitalProvenance" }).describe("Digital authenticity trail for media: hashes, capture metadata, and verifications.");
|
|
3521
|
+
|
|
3522
|
+
// src/experimental/v2/archival-claim-history-schema.ts
|
|
3523
|
+
var text10 = z.string().min(1).regex(/\S/);
|
|
3524
|
+
var id9 = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]*(?![\s\S])/);
|
|
3525
|
+
var EditionCitationSchema = z.strictObject({
|
|
3526
|
+
document: DocumentSnapshotRefSchema.extend({
|
|
3527
|
+
schemaId: z.literal(ARCHIVAL_ENTITIES_SCHEMA_ID)
|
|
3528
|
+
}),
|
|
3529
|
+
editionId: id9,
|
|
3530
|
+
artifact: ArtifactSnapshotRefSchema,
|
|
3531
|
+
locator: SourceProvenanceSchema.shape.locator.unwrap()
|
|
3532
|
+
}).describe(
|
|
3533
|
+
"A citation bound to an exact edition declaration and artifact digest. Similar titles do not permit edition substitution."
|
|
3534
|
+
);
|
|
3535
|
+
var ArtifactReviewSchema = z.strictObject({
|
|
3536
|
+
verifier: text10.optional(),
|
|
3537
|
+
organization: text10.optional(),
|
|
3538
|
+
credentials: text10.optional(),
|
|
3539
|
+
reviewedAt: EventTimeValueSchema.optional(),
|
|
3540
|
+
verificationMethod: VerificationMethodSchema.optional(),
|
|
3541
|
+
methodDescription: text10.optional(),
|
|
3542
|
+
result: VerificationResultSchema.optional(),
|
|
3543
|
+
confidence: z.enum(["high", "medium", "low", "unassessed"]).optional(),
|
|
3544
|
+
sourceCredibility: SourceCredibilitySchema.optional(),
|
|
3545
|
+
findings: text10.optional(),
|
|
3546
|
+
reportRef: ArtifactSnapshotRefSchema.shape.sourceRef.optional(),
|
|
3547
|
+
reportUrl: z.url().optional(),
|
|
3548
|
+
manipulationDetected: z.boolean().optional(),
|
|
3549
|
+
manipulationDescription: text10.optional(),
|
|
3550
|
+
notes: text10.optional()
|
|
3551
|
+
}).describe(
|
|
3552
|
+
"An attributed authenticity or source-credibility review, not an automatic result of snapshot validation."
|
|
3553
|
+
);
|
|
3554
|
+
var ArchivalClaimSubjectSchema = z.discriminatedUnion("kind", [
|
|
3555
|
+
ResearchClaimSubjectSchema.options[0],
|
|
3556
|
+
z.strictObject({
|
|
3557
|
+
kind: z.literal("entity"),
|
|
3558
|
+
reference: ArchivalEntityReferenceSchema
|
|
3559
|
+
}),
|
|
3560
|
+
ResearchClaimSubjectSchema.options[2],
|
|
3561
|
+
ResearchClaimSubjectSchema.options[3]
|
|
3562
|
+
]);
|
|
3563
|
+
var subject = ArchivalClaimSubjectSchema;
|
|
3564
|
+
var ArchivalSourceStatementSchema = ResearchSourceStatementSchema.extend({
|
|
3565
|
+
subject,
|
|
3566
|
+
editionCitation: EditionCitationSchema.optional(),
|
|
3567
|
+
reportedArtifactReview: ArtifactReviewSchema.optional()
|
|
3568
|
+
});
|
|
3569
|
+
var assessment4 = {
|
|
3570
|
+
subject,
|
|
3571
|
+
entityInputRefs: z.array(ArchivalEntityAssertionReferenceSchema).optional(),
|
|
3572
|
+
editionInputRefs: z.array(EditionCitationSchema).optional()
|
|
3573
|
+
};
|
|
3574
|
+
var ArchivalUnassessedClaimSchema = ResearchUnassessedClaimSchema.extend(assessment4);
|
|
3575
|
+
var ArchivalAssessedClaimSchema = ResearchAssessedClaimSchema.extend({
|
|
3576
|
+
...assessment4,
|
|
3577
|
+
artifactReview: ArtifactReviewSchema.optional()
|
|
3578
|
+
});
|
|
3579
|
+
var ArchivalHistoricalClaimSchema = z.union([
|
|
3580
|
+
ArchivalSourceStatementSchema,
|
|
3581
|
+
ArchivalUnassessedClaimSchema,
|
|
3582
|
+
ArchivalAssessedClaimSchema
|
|
3583
|
+
]);
|
|
3584
|
+
var ARCHIVAL_CLAIM_HISTORY_SCHEMA_ID = "urn:disclosureos:experimental:claim-history:0.4.0";
|
|
3585
|
+
var ArchivalClaimHistorySchema = ResearchClaimHistorySchema.extend({
|
|
3586
|
+
schemaVersion: z.literal("0.4.0"),
|
|
3587
|
+
claims: z.array(ArchivalHistoricalClaimSchema),
|
|
3588
|
+
entityRefs: z.array(
|
|
3589
|
+
DocumentSnapshotRefSchema.extend({
|
|
3590
|
+
schemaId: z.literal(ARCHIVAL_ENTITIES_SCHEMA_ID)
|
|
3591
|
+
})
|
|
3592
|
+
)
|
|
3593
|
+
});
|
|
3594
|
+
function archivalClaimHistoryJsonSchema() {
|
|
3595
|
+
return {
|
|
3596
|
+
...z.toJSONSchema(ArchivalClaimHistorySchema, {
|
|
3597
|
+
target: "draft-2020-12",
|
|
3598
|
+
reused: "ref"
|
|
3599
|
+
}),
|
|
3600
|
+
$id: ARCHIVAL_CLAIM_HISTORY_SCHEMA_ID
|
|
3601
|
+
};
|
|
3602
|
+
}
|
|
3603
|
+
|
|
3604
|
+
// src/experimental/v2/archival-claim-history.ts
|
|
3605
|
+
function parseArchivalClaimHistory(input) {
|
|
3606
|
+
const result = parseEntityClaimHistory(input, ArchivalClaimHistorySchema, {
|
|
3607
|
+
kind: "claim_history",
|
|
3608
|
+
schemaId: ARCHIVAL_CLAIM_HISTORY_SCHEMA_ID,
|
|
3609
|
+
rulesetVersion: "0.4.0"
|
|
3610
|
+
});
|
|
3611
|
+
if (!result.success) return result;
|
|
3612
|
+
const data = result.data, issues = [];
|
|
3613
|
+
const problem = (code, pointer6, message) => issues.push({ code, stage: "semantic", pointer: pointer6, message });
|
|
3614
|
+
const snapshots = new Set(data.entityRefs.map((r) => JSON.stringify(r)));
|
|
3615
|
+
const sources = new Map([
|
|
3616
|
+
...data.observation.sources.map((s) => [`source:${s.id}`, s]),
|
|
3617
|
+
...data.observation.products.map((s) => [`product:${s.id}`, s])
|
|
3618
|
+
]);
|
|
3619
|
+
for (const [i, claim] of data.claims.entries()) {
|
|
3620
|
+
const p = `/claims/${i}`;
|
|
3621
|
+
const citations = claim.kind === "source_assertion" ? claim.editionCitation ? [claim.editionCitation] : [] : claim.editionInputRefs ?? [];
|
|
3622
|
+
if (new Set(citations.map((c) => JSON.stringify(c))).size !== citations.length)
|
|
3623
|
+
problem("REF.UNIQUE_ID", p, "Edition citations repeat.");
|
|
3624
|
+
for (const citation of citations) {
|
|
3625
|
+
if (!snapshots.has(JSON.stringify(citation.document)))
|
|
3626
|
+
problem(
|
|
3627
|
+
"REF.LOCAL_RESOLUTION",
|
|
3628
|
+
p,
|
|
3629
|
+
"Edition citation must select a declared entity snapshot."
|
|
3630
|
+
);
|
|
3631
|
+
const source = sources.get(citation.artifact.sourceRef);
|
|
3632
|
+
if (!source?.digest || source.digest.value !== citation.artifact.digest.value)
|
|
3633
|
+
problem(
|
|
3634
|
+
"ARCHIVE.ARTIFACT_IDENTITY",
|
|
3635
|
+
p,
|
|
3636
|
+
"Edition citation must match an inventory artifact's SHA-256 declaration."
|
|
3637
|
+
);
|
|
3638
|
+
if (citation.locator.kind === "time_range" && citation.locator.endSeconds < citation.locator.startSeconds)
|
|
3639
|
+
problem("TIME.INTERVAL", p, "Edition locator end precedes its start.");
|
|
3640
|
+
if (claim.kind === "source_assertion" && (claim.provenance.sourceRef !== citation.artifact.sourceRef || claim.provenance.sourceDigest && claim.provenance.sourceDigest.value !== citation.artifact.digest.value || JSON.stringify(claim.provenance.locator) !== JSON.stringify(citation.locator)))
|
|
3641
|
+
problem(
|
|
3642
|
+
"ARCHIVE.CITATION_SCOPE",
|
|
3643
|
+
p,
|
|
3644
|
+
"A source statement's provenance and edition citation must name the same artifact and locator."
|
|
3645
|
+
);
|
|
3646
|
+
}
|
|
3647
|
+
const review = claim.kind === "source_assertion" ? claim.reportedArtifactReview : claim.status === "assessed" ? claim.artifactReview : void 0;
|
|
3648
|
+
if (!review) continue;
|
|
3649
|
+
if (claim.subject.kind !== "entity" || !["source_edition", "digital_artifact"].includes(
|
|
3650
|
+
claim.subject.reference.target.kind
|
|
3651
|
+
))
|
|
3652
|
+
problem(
|
|
3653
|
+
"ARCHIVE.REVIEW_SUBJECT",
|
|
3654
|
+
p,
|
|
3655
|
+
"Artifact review requires an exact source-edition or digital-artifact subject."
|
|
3656
|
+
);
|
|
3657
|
+
if (claim.kind === "assessment" && claim.inputRefs.length + (claim.entityInputRefs?.length ?? 0) + (claim.contextInputRefs?.length ?? 0) + citations.length === 0)
|
|
3658
|
+
problem(
|
|
3659
|
+
"REF.LOCAL_RESOLUTION",
|
|
3660
|
+
p,
|
|
3661
|
+
"Artifact review requires explicit inputs."
|
|
3662
|
+
);
|
|
3663
|
+
if (review.reportRef && !sources.has(review.reportRef))
|
|
3664
|
+
problem(
|
|
3665
|
+
"REF.LOCAL_RESOLUTION",
|
|
3666
|
+
p,
|
|
3667
|
+
"Review report is absent from the observation inventory."
|
|
3668
|
+
);
|
|
3669
|
+
if (review.reviewedAt) {
|
|
3670
|
+
const found = [];
|
|
3671
|
+
checkTime(review.reviewedAt, `${p}/artifactReview/reviewedAt`, found);
|
|
3672
|
+
found.forEach((v) => problem(v.code, v.pointer, v.message));
|
|
3673
|
+
}
|
|
3674
|
+
}
|
|
3675
|
+
const validation = {
|
|
3676
|
+
contract: result.contract,
|
|
3677
|
+
issues,
|
|
3678
|
+
uncheckedRefs: result.uncheckedRefs,
|
|
3679
|
+
checks: {
|
|
3680
|
+
...result.checks,
|
|
3681
|
+
semantic: issues.length ? "failed" : "passed"
|
|
3682
|
+
}
|
|
3683
|
+
};
|
|
3684
|
+
return issues.length ? { ...validation, success: false } : {
|
|
3685
|
+
...validation,
|
|
3686
|
+
success: true,
|
|
3687
|
+
data,
|
|
3688
|
+
currentClaimRefs: result.currentClaimRefs
|
|
3689
|
+
};
|
|
3690
|
+
}
|
|
2044
3691
|
|
|
2045
|
-
export { AssessedClaimSchema, CLAIM_HISTORY_RULESET_VERSION, CONTEXT_CLAIM_HISTORY_RULESET_VERSION, CONTEXT_CLAIM_HISTORY_SCHEMA_ID, ClaimSubjectSchema, ContextAssertionReferenceSchema, ContextAssessedClaimSchema, ContextClaimHistorySchema, ContextClaimSubjectSchema, ContextCollectionFields, ContextEntitySchema, ContextEnvironmentFields, ContextEventFields, ContextHistoricalClaimSchema, ContextMeasurementLinkSchema, ContextObjectFields, ContextPlaceFields, ContextPlatformFields, ContextReferenceSchema, ContextSourceStatementSchema, ContextTargetSchema, ContextTemporalFields, ContextUnassessedClaimSchema, DocumentSnapshotRefSchema, EXPERIMENTAL_CLAIM_HISTORY_SCHEMA_ID, EXPERIMENTAL_OBSERVATION_SCHEMA_ID, EventTimeIntervalSchema, EventTimePointSchema, EventTimeSchema, EventTimeValueSchema, ExperimentalClaimHistorySchema, ExperimentalObservationSchema, FramedPositionSchema, HistoricalClaimSchema, MeasuredQuantitySchema, OBSERVATION_CONTEXT_SCHEMA_ID, ObservationAssertionSchema, ObservationContextSchema, ObservationMeasurementSchema, ObservationMethodSchema, ObservationPositionSchema, ObservationProductSchema, ObservationSnapshotRefSchema, ObservationSourceSchema, ObservationTimeSchema, PRIMITIVES_SCHEMA_ID, PositionSchema, PositionValueSchema, PrimitiveRecordSchema, ProcessingActivitySchema, QuantitativeUncertaintySchema, ReferenceFrameSchema, SourceProvenanceSchema, SourceStatementSchema, UnassessedClaimSchema, ValueAssertionSchema, ValueSelectionSchema, compareUtcInstants, contextAssertions, contextClaimHistoryJsonSchema, experimentalClaimHistoryJsonSchema, experimentalObservationJsonSchema, observationContextJsonSchema, parseContextClaimHistory, parseExperimentalClaimHistory, parseExperimentalObservation, parseObservationContext, parsePrimitiveRecord, primitivesJsonSchema };
|
|
3692
|
+
export { ARCHIVAL_CLAIM_HISTORY_SCHEMA_ID, ARCHIVAL_ENTITIES_SCHEMA_ID, ArchivalAssessedClaimSchema, ArchivalClaimHistorySchema, ArchivalClaimSubjectSchema, ArchivalEntitiesSchema, ArchivalEntityAssertionReferenceSchema, ArchivalEntityReferenceSchema, ArchivalEntitySchema, ArchivalEntityTargetSchema, ArchivalHistoricalClaimSchema, ArchivalSourceStatementSchema, ArchivalUnassessedClaimSchema, ArtifactReviewSchema, ArtifactSnapshotRefSchema, AssessedClaimSchema, CLAIM_HISTORY_RULESET_VERSION, CONTEXT_CLAIM_HISTORY_RULESET_VERSION, CONTEXT_CLAIM_HISTORY_SCHEMA_ID, ClaimSubjectSchema, ContextAssertionReferenceSchema, ContextAssessedClaimSchema, ContextClaimHistorySchema, ContextClaimSubjectSchema, ContextCollectionFields, ContextEntitySchema, ContextEnvironmentFields, ContextEventFields, ContextHistoricalClaimSchema, ContextMeasurementLinkSchema, ContextObjectFields, ContextPlaceFields, ContextPlatformFields, ContextReferenceSchema, ContextSourceStatementSchema, ContextTargetSchema, ContextTemporalFields, ContextUnassessedClaimSchema, DigitalArtifactFields, DigitalCustodyFields, DocumentEventFields, DocumentSnapshotRefSchema, EXPERIMENTAL_CLAIM_HISTORY_SCHEMA_ID, EXPERIMENTAL_OBSERVATION_SCHEMA_ID, EditionCitationSchema, EventTimeIntervalSchema, EventTimePointSchema, EventTimeSchema, EventTimeValueSchema, ExperimentalClaimHistorySchema, ExperimentalObservationSchema, ExternalIdentifierFields, FramedPositionSchema, HistoricalClaimSchema, IdentifierCheckFields, MeasuredQuantitySchema, OBSERVATION_CONTEXT_SCHEMA_ID, ObservationAssertionSchema, ObservationContextSchema, ObservationMeasurementSchema, ObservationMethodSchema, ObservationPositionSchema, ObservationProductSchema, ObservationSnapshotRefSchema, ObservationSourceSchema, ObservationTimeSchema, PRIMITIVES_SCHEMA_ID, PositionSchema, PositionValueSchema, PrimitiveRecordSchema, ProcessingActivitySchema, QuantitativeUncertaintySchema, RESEARCH_CLAIM_HISTORY_RULESET_VERSION, RESEARCH_CLAIM_HISTORY_SCHEMA_ID, RESEARCH_ENTITIES_SCHEMA_ID, ReferenceFrameSchema, ResearchAccountFields, ResearchAssessedClaimSchema, ResearchClaimHistorySchema, ResearchClaimSubjectSchema, ResearchEntitiesSchema, ResearchEntityAssertionReferenceSchema, ResearchEntityReferenceSchema, ResearchEntitySchema, ResearchEntityTargetSchema, ResearchHistoricalClaimSchema, ResearchProcedureFields, ResearchSourceStatementSchema, ResearchUnassessedClaimSchema, ResearchWitnessFields, ResearchWitnessGroupFields, SourceEditionFields, SourceProvenanceSchema, SourceStatementSchema, UnassessedClaimSchema, ValueAssertionSchema, ValueSelectionSchema, WitnessReviewSchema, archivalArtifactReferences, archivalClaimHistoryJsonSchema, archivalEntitiesJsonSchema, compareUtcInstants, contextAssertions, contextClaimHistoryJsonSchema, experimentalClaimHistoryJsonSchema, experimentalObservationJsonSchema, observationContextJsonSchema, parseArchivalClaimHistory, parseArchivalEntities, parseContextClaimHistory, parseExperimentalClaimHistory, parseExperimentalObservation, parseObservationContext, parsePrimitiveRecord, parseResearchClaimHistory, parseResearchEntities, primitivesJsonSchema, researchClaimHistoryJsonSchema, researchEntitiesJsonSchema, researchEntityAssertions, researchEntityContextReferences };
|
|
2046
3693
|
//# sourceMappingURL=index.js.map
|
|
2047
3694
|
//# sourceMappingURL=index.js.map
|