import type { ReviewStudyAttachment, ReviewStudyBindingEvidence, ReviewStudyFinding } from "./reviewStudyTypes.js"; /** Report pointers preserve native qualifications; descriptive patterns do not acquire causal labels. */ export function reviewStudyFindings(attachments: readonly ReviewStudyAttachment[], bindings: readonly ReviewStudyBindingEvidence[]): readonly ReviewStudyFinding[] { const findings: ReviewStudyFinding[] = []; for (const attachment of attachments) { const add = (path: ReviewStudyFinding["path"], code: string, message: string) => findings.push({ attachment: attachment.id, path, code, message }); add(["assurance"], "STUDY_INTERPRETATION_LIMIT", "Replayed study evidence does not establish source authenticity, independence, causation or review completeness"); for (const binding of bindings.filter((item) => item.attachment === attachment.id && item.status === "declared-related")) add(["request"], "STUDY_SUBJECT_DECLARED_RELATED", `${binding.subject.kind} ${binding.subject.id}: ${binding.qualification}`); if (attachment.kind === "diagnostic-suite") { attachment.result.suites.forEach((suite, i) => suite.checks.forEach((check, j) => { const path = ["suites", i, "checks", j]; if (check.status !== "evaluated" || check.signal === "flagged") add(path, check.status === "not-evaluated" ? "STUDY_CHECK_NOT_EVALUATED" : check.signal === "flagged" ? "STUDY_CHECK_FLAGGED" : "STUDY_CHECK_QUALIFIED", `${suite.id}/${check.check.id}: ${check.status}; ${check.signal}; ${check.reason ?? "inspect native evidence"}`); check.interpretationLimits.forEach((message, k) => add([...path, "interpretationLimits", k], "STUDY_CHECK_INTERPRETATION_LIMIT", message)); })); } else { attachment.result.runs.forEach((run, i) => { if (run.run.status !== "completed") add(["runs", i, "run", "status"], "STUDY_SCENARIO_INCOMPLETE", `Scenario ${i} has ${run.run.status} graph execution`); run.values.forEach((value, j) => { if (value.value === null) add(["runs", i, "values", j], "STUDY_OBSERVATION_UNAVAILABLE", `${value.observation}: ${value.reason}`); value.findings.forEach((finding, k) => add(["runs", i, "values", j, "findings", k], finding.code, finding.message)); }); }); const differences = attachment.kind === "sensitivity" ? [{ path: ["jointDifferences"], entries: attachment.result.jointDifferences }, ...attachment.result.oneAtATime.map((run, i) => ({ path: ["oneAtATime", i, "differences"], entries: run.differences }))] : attachment.result.scenarios.map((scenario, i) => ({ path: ["scenarios", i, "differences"], entries: scenario.differences })); for (const set of differences) set.entries.forEach((difference, i) => { if (difference.difference === null) add([...set.path, i], "STUDY_EFFECT_UNAVAILABLE", `${difference.observation}: ${difference.reason}`); difference.findings.forEach((finding, j) => add([...set.path, i, "findings", j], finding.code, finding.message)); }); if (attachment.kind === "sensitivity") attachment.result.attribution.reconciliation.forEach((row, i) => { if (row.status === "different" || row.status === "unavailable") add(["attribution", "reconciliation", i], "STUDY_ATTRIBUTION_UNRESOLVED", `${row.observation}: ${row.status}`); }); } } return findings; }