← Files VeraARCHIVED FILE
modules/deep-research-validator/scripts/review_session.py
36.9 KB · Oct 2, 2026 · 00:29 UTC
from __future__ import annotations
import hashlib
import json
import re
from dataclasses import dataclass
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Sequence
__all__ = [
"ReviewSessionResult",
"RunIntakeResult",
"synchronize_final_artifact_sizes",
"write_review_session_artifacts",
"write_run_intake",
]
SCHEMA_VERSION = "1.0"
PLUGIN_NAME = "deep-research-validator"
WORKFLOW_NAME = "deep-research-validator"
MAX_CLAIM_ITEMS = 750
_REVIEW_COPY: dict[str, dict[str, Any]] = {
"it": {
"product_title": "Verifica della risposta",
"handoff_title": "Passaggio alla revisione",
"run_id": "ID esecuzione",
"review_payload": "Dati della revisione",
"run_intake": "Dati di avvio",
"pending_decisions": "Decisioni da prendere",
"applied_decisions": "Decisioni applicate",
"final_artifacts": "Documenti finali",
"review_in_codex": "Revisione in Codex",
"steps": (
"Verifica il riferimento locale e la sua impronta con `{tool}`.",
"Apri la schermata di revisione con il token restituito usando `{tool}`.",
"Salva le decisioni del revisore con `{tool}`.",
"Applica le decisioni del revisore con `{tool}`.",
),
"handoff_notice": "Il salvataggio e l'applicazione persistenti richiedono la schermata MCP o il server locale. La versione HTML statica permette solo di copiare o scaricare il JSON delle decisioni.",
"columns": (
"Tipo",
"Affermazione o documento",
"Azione proposta",
"Fonte",
"Risultato",
"Stato",
),
"claim": "Affermazione",
"untitled_claim": "Affermazione senza titolo",
"answer_contract_review": "Conformità ai requisiti della risposta",
"coverage_review": "Copertura delle affermazioni selezionate",
"edit_hint": "La modifica registra la correzione del revisore in proposed_fix, nel file claims_review.json, per il claim_index corrispondente. Non rigenera la risposta rivista o corretta.",
"artifacts": {
"answer_contract": "Requisiti della risposta (JSON)",
"claims_review": "Verifica delle affermazioni (JSON)",
"validation_audit": "Controlli sulla verifica (JSON)",
"validated_document": "Risposta verificata (Markdown)",
"validated_document_docx": "Risposta verificata (Word)",
"validation_package": "Registro della verifica (Markdown)",
},
"package_required": [
"# Registro di verifica della risposta",
"## Limiti della verifica",
"## Requisiti della risposta",
"## Verifica dei requisiti della risposta",
"## Copertura della verifica",
"## Inventario del documento",
"## Valutazione delle affermazioni",
],
"dependency_note": "Codex deve eseguire scripts/check_dependencies.py prima degli script del workflow.",
"data_notes": [
"Gli script leggono gli inventari locali del documento e delle fonti e la verifica delle affermazioni.",
"La schermata di revisione mostra una selezione delle evidenze sulle affermazioni e sulle fonti.",
"Non vengono usati per impostazione predefinita connettori esterni, caricamenti, SQL remoto o notebook ospitati.",
],
"caveats": [
"Identità delle fonti, riscontro semantico, ragionamento e limiti del giudizio professionale sono valutazioni del modello. I controlli automatici verificano solo struttura dei record e riscontri meccanici nella specifica copia della fonte citata.",
"I dati nella schermata MCP sono limitati; i file JSON e Markdown conservano tutte le evidenze della verifica.",
"ui_decisions.json resta in attesa finché Codex, la schermata MCP o la revisione alternativa non registrano le decisioni.",
],
"next_actions": [
"Esegui validate_deep_research_review e, quando MCP è disponibile, render_deep_research_review.",
"Prima della consegna rivedi identità delle fonti, riscontro semantico, ragionamento, requisiti, copertura e punti soggetti al giudizio professionale.",
"Correggi claims_review_draft.json o answer_contract.json e rigenera il pacchetto se validation_audit.json segnala errori.",
],
},
"en": {
"product_title": "Answer Validator",
"handoff_title": "Review Handoff",
"run_id": "Run ID",
"review_payload": "Review payload",
"run_intake": "Run intake",
"pending_decisions": "Pending decisions",
"applied_decisions": "Applied decisions",
"final_artifacts": "Final artifacts",
"review_in_codex": "Review In Codex",
"steps": (
"Validate the hash-bound local review reference with `{tool}`.",
"Render the review workbench with the returned token using `{tool}`.",
"Save reviewer actions with `{tool}`.",
"Apply reviewer actions with `{tool}`.",
),
"handoff_notice": (
"Persistent save/apply requires the MCP or local-server review "
"surface. Static HTML fallback can copy or download decision JSON only."
),
"columns": (
"Type",
"Claim or artifact",
"Suggested action",
"Source",
"Output",
"Status",
),
"claim": "Claim",
"untitled_claim": "Untitled claim",
"answer_contract_review": "Answer-contract conformance",
"coverage_review": "Claim-selection coverage",
"edit_hint": (
"Editing this claim writes the reviewer correction to proposed_fix "
"in claims_review.json for the matching claim_index. It does not "
"regenerate the reviewed or corrected answer."
),
"artifacts": {
"answer_contract": "Answer contract JSON",
"claims_review": "Claims review JSON",
"validation_audit": "Validation audit JSON",
"validated_document": "Validated document Markdown",
"validated_document_docx": "Validated document DOCX",
"validation_package": "Validation package Markdown",
},
"package_required": [
"# Answer Validation Record",
"## Assurance Boundary",
"## Answer Contract",
"## Answer-Contract Review",
"## Review Coverage",
"## Document Inventory",
"## Claim Assessments",
],
"dependency_note": "Codex should run scripts/check_dependencies.py before helper scripts.",
"data_notes": [
"Validation package scripts read local document inventory, source inventory, and claim review files.",
"Review payloads expose bounded claim/source evidence for UI review.",
"No external connector, upload path, remote SQL, or hosted notebook execution is used by default.",
],
"caveats": [
"Source identity, semantic support, reasoning, and legal-judgment boundaries are model-authored; fixed checks validate only record structure and mechanical observations in the specifically cited source snapshot.",
"The MCP review payload is bounded; use JSON and Markdown outputs as the complete validation evidence set.",
"ui_decisions.json is pending until Codex, the MCP widget, or fallback review records decisions.",
],
"next_actions": [
"Call validate_deep_research_review, then render_deep_research_review when MCP is available.",
"Review source-identity, semantic-support, reasoning, contract, coverage, and professional-judgment items before delivery.",
"Repair claims_review_draft.json or answer_contract.json and rerun packaging when validation_audit.json fails.",
],
},
"es": {
"product_title": "Validación de respuestas",
"handoff_title": "Entrega para revisión",
"run_id": "ID de ejecución",
"review_payload": "Datos de revisión",
"run_intake": "Datos de ejecución",
"pending_decisions": "Decisiones pendientes",
"applied_decisions": "Decisiones aplicadas",
"final_artifacts": "Artefactos finales",
"review_in_codex": "Revisión en Codex",
"steps": (
"Valide la referencia local vinculada por hash con `{tool}`.",
"Abra el área de revisión con el token devuelto usando `{tool}`.",
"Guarde las acciones del revisor con `{tool}`.",
"Aplique las acciones del revisor con `{tool}`.",
),
"handoff_notice": (
"El guardado y la aplicación persistentes requieren la superficie MCP "
"o el servidor local. El modo HTML estático solo permite copiar o "
"descargar el JSON de decisiones."
),
"columns": (
"Tipo",
"Afirmación o artefacto",
"Acción sugerida",
"Fuente",
"Salida",
"Estado",
),
"claim": "Afirmación",
"untitled_claim": "Afirmación sin título",
"answer_contract_review": "Conformidad con el contrato de respuesta",
"coverage_review": "Cobertura de selección de afirmaciones",
"edit_hint": (
"Al editar esta afirmación, la corrección del revisor se escribe en "
"proposed_fix dentro de claims_review.json para el claim_index correspondiente. "
"Esto no regenera la respuesta revisada o corregida."
),
"artifacts": {
"answer_contract": "JSON del contrato de respuesta",
"claims_review": "JSON de revisión de afirmaciones",
"validation_audit": "JSON de auditoría de validación",
"validated_document": "Documento validado en Markdown",
"validated_document_docx": "Documento validado en DOCX",
"validation_package": "Paquete de validación en Markdown",
},
"package_required": [
"# Registro de validación de la respuesta",
"## Límite de aseguramiento",
"## Contrato de respuesta",
"## Revisión del contrato de respuesta",
"## Cobertura de la revisión",
"## Inventario del documento",
"## Evaluaciones de las afirmaciones",
],
"dependency_note": "Codex debe ejecutar scripts/check_dependencies.py antes de los scripts auxiliares.",
"data_notes": [
"Los scripts del paquete leen los inventarios locales del documento y de las fuentes, además de la revisión de afirmaciones.",
"Los datos de revisión exponen un conjunto acotado de evidencias de afirmaciones y fuentes para la interfaz.",
"De forma predeterminada no se utilizan conectores externos, rutas de carga, SQL remoto ni cuadernos alojados.",
],
"caveats": [
"La identidad de la fuente, el respaldo semántico, el razonamiento y los límites del juicio profesional los redacta el modelo; los controles fijos validan solo la estructura del registro y las observaciones mecánicas en la fuente citada.",
"Los datos de revisión MCP están acotados; utilice las salidas JSON y Markdown como conjunto completo de evidencias de validación.",
"ui_decisions.json permanece pendiente hasta que Codex, el widget MCP o la revisión alternativa registren las decisiones.",
],
"next_actions": [
"Ejecute validate_deep_research_review y, cuando MCP esté disponible, render_deep_research_review.",
"Revise antes de la entrega la identidad de las fuentes, el respaldo semántico, el razonamiento, el contrato, la cobertura y el juicio profesional.",
"Corrija claims_review_draft.json y vuelva a generar el paquete si validation_audit.json falla.",
],
},
}
def _language_code(value: object | None) -> str:
text = str(value or "en").strip().lower().replace("_", "-")
return next(
(code for code in ("it", "es") if text == code or text.startswith(code + "-")),
"en",
)
def _copy(value: object | None) -> dict[str, Any]:
return _REVIEW_COPY[_language_code(value)]
@dataclass(frozen=True)
class RunIntakeResult:
"""Run intake artifact written before validation packaging."""
run_id: str
path: Path
@dataclass(frozen=True)
class ReviewSessionResult:
"""Review-session artifacts for one answer-validation run."""
run_id: str
run_intake_path: Path
review_payload_path: Path
ui_decisions_path: Path
final_artifacts_path: Path
review_item_count: int
def _utc_now() -> str:
return datetime.now(timezone.utc).replace(microsecond=0).isoformat()
def _safe_slug(value: str) -> str:
slug = re.sub(r"[^a-zA-Z0-9_.-]+", "-", value).strip("-._").lower()
return slug or "run"
def _run_id(document_inventory_path: Path) -> str:
timestamp = re.sub(r"[^0-9]", "", _utc_now())
return f"{PLUGIN_NAME}-{_safe_slug(document_inventory_path.stem)}-{timestamp}"
def _write_json(path: Path, payload: dict[str, Any]) -> Path:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(
json.dumps(payload, ensure_ascii=False, indent=2, default=str) + "\n",
encoding="utf-8",
)
return path
def synchronize_final_artifact_sizes(final_artifacts_path: Path) -> None:
"""Refresh declared byte sizes after downstream artifacts reach final form."""
payload = json.loads(final_artifacts_path.read_text(encoding="utf-8"))
outputs = payload.get("outputs")
if not isinstance(outputs, list):
raise ValueError("final_artifacts.json outputs must be a list")
output_dir = final_artifacts_path.parent.resolve()
for output in outputs:
if not isinstance(output, dict) or "size_bytes" not in output:
continue
relative = output.get("path")
if not isinstance(relative, str) or not relative.strip():
raise ValueError("final artifact output path must be a non-empty string")
artifact_path = (output_dir / relative).resolve()
if not artifact_path.is_relative_to(output_dir) or not artifact_path.is_file():
raise ValueError(f"final artifact output is missing: {relative}")
output["size_bytes"] = artifact_path.stat().st_size
_write_json(final_artifacts_path, payload)
def _write_review_handoff_card(
output_dir: Path,
*,
run_id: str,
validate_tool: str,
render_tool: str,
save_tool: str,
apply_tool: str,
language: str,
) -> Path:
copy = _copy(language)
steps = copy["steps"]
path = output_dir / "review_handoff.md"
lines = [
f"# {copy['product_title']} · {copy['handoff_title']}",
"",
f"- {copy['run_id']}: `{run_id}`",
f"- {copy['review_payload']}: `review_payload.json`",
f"- {copy['run_intake']}: `run_intake.json`",
f"- {copy['pending_decisions']}: `ui_decisions.json`",
f"- {copy['applied_decisions']}: `applied_decisions.json`",
f"- {copy['final_artifacts']}: `final_artifacts.json`",
"",
f"## {copy['review_in_codex']}",
f"1. {steps[0].format(tool=validate_tool)}",
f"2. {steps[1].format(tool=render_tool)}",
f"3. {steps[2].format(tool=save_tool)}",
f"4. {steps[3].format(tool=apply_tool)}",
"",
copy["handoff_notice"],
]
if _language_code(language) != "en":
lines.insert(1, "<!-- Review Handoff -->")
path.write_text("\n".join(lines) + "\n", encoding="utf-8")
return path
def _review_handoff_output_record(path: Path, language: str) -> dict[str, Any]:
copy = _copy(language)
required_text = [
"Review Handoff",
"review_payload.json",
"ui_decisions.json",
"applied_decisions.json",
"final_artifacts.json",
]
if _language_code(language) == "es":
required_text[1:1] = [copy["handoff_title"], copy["review_in_codex"]]
return {
"path": path.name,
"kind": "md",
"status": "written",
"required_text": required_text,
"qa_checks": ["nonempty_text", "required_text"],
}
def _local_output_refs(final_artifacts_path: Path) -> list[str]:
refs = [
"run_intake.json",
"review_payload.json",
"ui_decisions.json",
"final_artifacts.json",
]
payload = json.loads(final_artifacts_path.read_text(encoding="utf-8"))
outputs = payload.get("outputs")
if isinstance(outputs, list):
for output in outputs:
if not isinstance(output, dict):
continue
path_value = output.get("path")
if (
isinstance(path_value, str)
and path_value.strip()
and "://" not in path_value
):
refs.append(path_value.strip())
return list(dict.fromkeys(refs))
def _append_execution_trace(
run_intake_path: Path,
final_artifacts_path: Path,
*,
command: Sequence[str],
) -> None:
from vera_assurance.serialization import build_review_execution_step
payload = json.loads(run_intake_path.read_text(encoding="utf-8"))
step = build_review_execution_step(payload, WORKFLOW_NAME, command)
step["outputs"] = _local_output_refs(final_artifacts_path)
payload["execution_trace"] = [step]
_write_json(run_intake_path, payload)
def _as_output_ref(path: str | Path | None, output_dir: Path) -> str | None:
if path is None:
return None
candidate = Path(path)
try:
return candidate.relative_to(output_dir).as_posix()
except ValueError:
return candidate.as_posix()
def _run_path_reference(
path: Path,
client_engagement: dict[str, Any] | None,
) -> str:
"""Return an absolute unmanaged path or a portable managed-run reference."""
if client_engagement is None:
return path.as_posix()
run_root_value = client_engagement.get("run_root")
if not isinstance(run_root_value, str) or not run_root_value.strip():
raise ValueError("Managed Answer Validator context has no run_root.")
run_root = Path(run_root_value).expanduser().resolve(strict=True)
resolved = path.expanduser().resolve(strict=True)
try:
relative = resolved.relative_to(run_root)
except ValueError as exc:
raise ValueError("Answer Validator path is outside the current run.") from exc
if not relative.parts:
raise ValueError("Answer Validator path must identify a run artifact.")
return relative.as_posix()
def _clean_text(value: Any) -> str:
return " ".join(str(value or "").strip().split())
def _base_item(
item_id: str,
item_type: str,
title: str,
*,
allowed_actions: Sequence[str],
recommended_action: str,
source_path: str | None = None,
output_path: str | None = None,
evidence: Sequence[dict[str, Any]] = (),
data: dict[str, Any] | None = None,
) -> dict[str, Any]:
return {
"id": item_id,
"item_type": item_type,
"title": title,
"source_path": source_path,
"output_path": output_path,
"allowed_actions": list(allowed_actions),
"recommended_action": recommended_action,
"evidence": list(evidence),
"data": data or {},
"status": "needs_review",
}
def _review_columns(language: str) -> list[dict[str, str]]:
labels = _copy(language)["columns"]
fields = (
"item_type",
"title",
"recommended_action",
"source_path",
"output_path",
"status",
)
return [
{"field": field, "label": str(label)}
for field, label in zip(fields, labels, strict=True)
]
def _claim_item_type(verdict: str) -> str:
if verdict == "supported":
return "supported_claim"
if verdict == "partially_supported":
return "partially_supported_claim"
if verdict == "not_supported":
return "unsupported_claim"
if verdict == "contradicted":
return "contradicted_claim"
if verdict == "uncertain":
return "uncertain_claim"
return "claim_review"
def _claim_title(claim: dict[str, Any], index: int, language: str) -> str:
copy = _copy(language)
claim_index = claim.get("claim_index") or index
text = _clean_text(claim.get("claim_text"))
if len(text) > 110:
text = text[:107].rstrip() + "..."
return f"{copy['claim']} {claim_index}: {text or copy['untitled_claim']}"
def _claim_items(
claims_review: dict[str, Any], audit: dict[str, Any], language: str
) -> list[dict[str, Any]]:
copy = _copy(language)
claims = claims_review.get("claims", [])
if not isinstance(claims, list):
return []
observations = {
str(entry.get("claim_index")): entry
for entry in audit.get("claim_observations", [])
if isinstance(entry, dict)
}
items: list[dict[str, Any]] = []
for index, claim in enumerate(claims[:MAX_CLAIM_ITEMS], start=1):
if not isinstance(claim, dict):
continue
support = claim.get("support")
support_status = (
_clean_text(support.get("status")) if isinstance(support, dict) else ""
)
claim_data = dict(claim)
claim_index = claim.get("claim_index") or index
if claim.get("claim_index") is not None:
claim_data.update(
{
"target_artifact": "claims_review.json",
"target_records_key": "claims",
"target_id_field": "claim_index",
"target_record_id": str(claim_index),
"target_field": "proposed_fix",
"edit_hint": copy["edit_hint"],
}
)
items.append(
_base_item(
f"claim-{claim_index}",
_claim_item_type(support_status),
_claim_title(claim, index, language),
output_path="claims_review.json",
allowed_actions=(
"accept",
"reject",
"edit",
"mark_unclear",
"request_more_documents",
"skip",
),
recommended_action=_clean_text(claim.get("reviewer_action"))
or "mark_unclear",
evidence=[
{
"kind": "mechanical_source_observations",
"claim_ref": {
"artifact": "claims_review.json",
"records_key": "claims",
"id_field": "claim_index",
"record_id": str(claim_index),
},
"observations": observations.get(str(claim_index), {}),
}
],
data=claim_data,
)
)
return items
def _scope_items(claims_review: dict[str, Any], language: str) -> list[dict[str, Any]]:
"""Expose model-authored contract and coverage decisions for review."""
copy = _copy(language)
items: list[dict[str, Any]] = []
for item_id, item_type, title_key, field in (
(
"answer-contract-review",
"answer_contract_review",
"answer_contract_review",
"contract_review",
),
(
"claim-selection-coverage",
"coverage_review",
"coverage_review",
"coverage_review",
),
):
assessment = claims_review.get(field)
if not isinstance(assessment, dict):
continue
items.append(
_base_item(
item_id,
item_type,
str(copy[title_key]),
output_path="claims_review.json",
allowed_actions=(
"accept",
"reject",
"edit",
"mark_unclear",
"request_more_documents",
"skip",
),
recommended_action=_clean_text(assessment.get("reviewer_action"))
or "mark_unclear",
evidence=[
{
"kind": item_type,
"assessment_ref": {
"artifact": "claims_review.json",
"field": field,
},
}
],
data={field: assessment},
)
)
return items
def _audit_items(audit: dict[str, Any]) -> list[dict[str, Any]]:
failed = audit.get("failed_checks", [])
if not isinstance(failed, list):
return []
return [
_base_item(
f"audit-check-{index}",
"audit_check",
str(check),
output_path="validation_audit.json",
allowed_actions=("accept", "reject", "edit", "mark_unclear", "skip"),
recommended_action="reject",
evidence=[
{
"kind": "validation_audit_check",
"status": "fail",
"check": check,
"invalid_claim_indices": audit.get("invalid_claim_indices"),
"missing_claim_text_indices": audit.get(
"missing_claim_text_indices"
),
"missing_review_indices": audit.get("missing_review_indices"),
}
],
data={"check": check, "audit_ref": "validation_audit.json"},
)
for index, check in enumerate(failed, start=1)
]
def _artifact_items(
paths: dict[str, Path], output_dir: Path, language: str
) -> list[dict[str, Any]]:
labels = _copy(language)["artifacts"]
items: list[dict[str, Any]] = []
for index, (field, title) in enumerate(labels.items(), start=1):
path_value = paths.get(field)
if not path_value:
continue
path_ref = _as_output_ref(path_value, output_dir)
exists = Path(path_value).exists()
items.append(
_base_item(
f"artifact-{index}",
"validation_artifact",
title,
output_path=path_ref,
allowed_actions=("accept", "edit", "mark_unclear", "skip"),
recommended_action="accept" if exists else "mark_unclear",
evidence=[
{
"kind": "artifact_status",
"field": field,
"path": path_ref,
"exists": exists,
}
],
data={"field": field, "path": path_ref, "exists": exists},
)
)
return items
def _output_records(output_dir: Path, language: str) -> list[dict[str, Any]]:
review_files = {
"run_intake.json",
"review_payload.json",
"ui_decisions.json",
"final_artifacts.json",
}
required_text_by_path = {
"validation_package.md": _copy(language)["package_required"]
}
outputs: list[dict[str, Any]] = []
for path in sorted(output_dir.rglob("*")):
if not path.is_file() or path.name in review_files:
continue
relative = path.relative_to(output_dir).as_posix()
output = {
"path": relative,
"size_bytes": path.stat().st_size,
"kind": path.suffix.lower().lstrip(".") or "file",
"status": "written",
}
required_text = required_text_by_path.get(relative)
if required_text:
output["required_text"] = required_text
output["qa_checks"] = ["nonempty_text", "required_text"]
outputs.append(output)
return outputs
def write_run_intake(
output_dir: Path,
*,
document_inventory_path: Path,
source_inventory_path: Path,
claims_review_path: Path,
answer_contract_path: Path,
document_inventory: dict[str, Any],
source_inventory: dict[str, Any],
claims_review: dict[str, Any],
answer_contract: dict[str, Any],
client_engagement: dict[str, Any] | None = None,
client_run_id: str | None = None,
) -> RunIntakeResult:
"""Write run intake before validation package review."""
context_run_id = (
str(client_engagement["run_id"]) if client_engagement is not None else None
)
if client_run_id is not None and context_run_id not in {None, client_run_id}:
raise ValueError("Answer Validator run ID does not match its client context.")
run_id = context_run_id or client_run_id or _run_id(document_inventory_path)
language = _language_code(claims_review.get("language"))
copy = _copy(language)
input_refs = [
_run_path_reference(path, client_engagement)
for path in (
document_inventory_path,
source_inventory_path,
claims_review_path,
answer_contract_path,
)
]
output_ref = _run_path_reference(output_dir, client_engagement)
payload = {
"schema_version": SCHEMA_VERSION,
"plugin": PLUGIN_NAME,
"workflow": WORKFLOW_NAME,
"run_id": run_id,
**(
{"path_reference": "run_root_relative"}
if client_engagement is not None
else {}
),
"created_at": _utc_now(),
"language": language,
"input_paths": input_refs,
"output_dir": output_ref,
"inferred_task": "answer_validation_review_payload",
"assumptions": {
"document_source_name": document_inventory.get("source_name"),
"document_word_count": document_inventory.get("word_count"),
"document_url_count": len(document_inventory.get("urls", []) or []),
"source_count": len(source_inventory.get("sources", []) or []),
"claim_count": len(claims_review.get("claims", []) or []),
"validation_objective": claims_review.get("validation_objective"),
"generation_route": answer_contract.get("generation_route"),
"document_type": answer_contract.get("document_type"),
"validation_profile": answer_contract.get("validation_profile"),
},
"unresolved_questions": [],
"dependency_check": {
"status": "not_run_by_script",
"note": copy["dependency_note"],
},
"data_posture": {
"local_files_read": input_refs,
"external_connectors_used": [],
"upload_paths_used": [],
"remote_sql_execution_used": False,
"hosted_notebook_execution_used": False,
"notes": copy["data_notes"],
},
"status": "ready_for_validation_package",
}
return RunIntakeResult(
run_id=run_id,
path=_write_json(output_dir / "run_intake.json", payload),
)
def write_review_session_artifacts(
output_dir: Path,
*,
run_id: str,
run_intake_path: Path,
document_inventory_path: Path,
source_inventory_path: Path,
claims_review_path: Path,
answer_contract_path: Path,
document_inventory: dict[str, Any],
source_inventory: dict[str, Any],
claims_review: dict[str, Any],
answer_contract: dict[str, Any],
audit: dict[str, Any],
paths: dict[str, Path],
client_engagement: dict[str, Any] | None = None,
) -> ReviewSessionResult:
"""Write review payload, pending decisions, and final artifacts."""
language = _language_code(claims_review.get("language"))
copy = _copy(language)
items: list[dict[str, Any]] = []
items.extend(_audit_items(audit))
items.extend(_scope_items(claims_review, language))
items.extend(_claim_items(claims_review, audit, language))
items.extend(_artifact_items(paths, output_dir, language))
input_refs = [
_run_path_reference(path, client_engagement)
for path in (
document_inventory_path,
source_inventory_path,
claims_review_path,
answer_contract_path,
)
]
review_payload = {
"schema_version": SCHEMA_VERSION,
"plugin": PLUGIN_NAME,
"workflow": WORKFLOW_NAME,
"run_id": run_id,
**(
{"path_reference": "run_root_relative"}
if client_engagement is not None
else {}
),
"created_at": _utc_now(),
"language": language,
"source_paths": [
document_inventory.get("source_name"),
*(document_inventory.get("urls", []) or []),
],
"review_type": "answer_validation_review",
"items": items,
"item_count": len(items),
"columns": _review_columns(language),
"source_artifacts": {
"run_intake": _as_output_ref(run_intake_path, output_dir),
"document_inventory": input_refs[0],
"source_inventory": input_refs[1],
"claims_review_input": input_refs[2],
"answer_contract_input": input_refs[3],
"answer_contract": _as_output_ref(
paths.get("answer_contract"),
output_dir,
),
"claims_review": _as_output_ref(paths.get("claims_review"), output_dir),
"validation_audit": "validation_audit.json",
"validated_document": _as_output_ref(
paths.get("validated_document"), output_dir
),
"validation_package": "validation_package.md",
},
"allowed_actions": [
"accept",
"reject",
"edit",
"mark_unclear",
"request_more_documents",
"skip",
],
"status": "ready_for_review",
"summary": {
"record_integrity_status": audit.get("record_integrity_status"),
"delivery_readiness": audit.get("delivery_readiness"),
"failed_check_count": len(audit.get("failed_checks", []) or []),
"claim_count": audit.get("claim_count", 0),
"support_attention_count": len(
audit.get("support_attention_claim_indices", []) or []
),
"source_count": audit.get("source_count", 0),
"document_url_count": audit.get("document_url_count", 0),
"source_identity_attention_count": len(
audit.get("source_identity_attention_claim_indices", []) or []
),
"reasoning_attention_count": len(
audit.get("reasoning_attention_claim_indices", []) or []
),
"judgment_dependent_count": len(
audit.get("judgment_dependent_claim_indices", []) or []
),
"generation_route": answer_contract.get("generation_route"),
"document_type": answer_contract.get("document_type"),
},
}
review_payload_path = _write_json(
output_dir / "review_payload.json",
review_payload,
)
review_payload_sha256 = hashlib.sha256(review_payload_path.read_bytes()).hexdigest()
ui_decisions_path = _write_json(
output_dir / "ui_decisions.json",
{
"schema_version": SCHEMA_VERSION,
"plugin": PLUGIN_NAME,
"workflow": WORKFLOW_NAME,
"run_id": run_id,
"decided_at": None,
"decision_source": "not_collected",
"review_payload_path": review_payload_path.name,
"review_payload_sha256": review_payload_sha256,
"decisions": [],
"decision_count": 0,
"status": "pending_review",
},
)
review_handoff_path = _write_review_handoff_card(
output_dir,
run_id=run_id,
validate_tool="validate_deep_research_review",
render_tool="render_deep_research_review",
save_tool="save_deep_research_decisions",
apply_tool="apply_deep_research_decisions",
language=language,
)
outputs = _output_records(output_dir, language)
outputs = [
output
for output in outputs
if not (
isinstance(output, dict) and output.get("path") == review_handoff_path.name
)
]
outputs.append(_review_handoff_output_record(review_handoff_path, language))
final_artifacts_path = _write_json(
output_dir / "final_artifacts.json",
{
"schema_version": SCHEMA_VERSION,
"plugin": PLUGIN_NAME,
"workflow": WORKFLOW_NAME,
"run_id": run_id,
"completed_at": _utc_now(),
"review_payload_sha256": review_payload_sha256,
"outputs": outputs,
"caveats": copy["caveats"],
"next_actions": copy["next_actions"],
"status": "written_pending_review",
},
)
_append_execution_trace(
run_intake_path,
final_artifacts_path,
command=[
"python",
"plugins/deep-research-validator/scripts/package_validation.py",
],
)
return ReviewSessionResult(
run_id=run_id,
run_intake_path=run_intake_path,
review_payload_path=review_payload_path,
ui_decisions_path=ui_decisions_path,
final_artifacts_path=final_artifacts_path,
review_item_count=len(items),
)
SHA-256: 01c42382b53ef90b1b0beebfbb0cf24d8e256f6fc61c4620caaf15ecfd2f32df