feat(ingest): resolve project labels with Jev
CI / quality (push) Canceled after 0s

This commit is contained in:
vasily committed 2026-10-15 10:53:30 +08:00
1 parent 0e1a2ad008
commit 34f4923566
28 files changed
+3932 -81

No files matched your search

+69
View File
@@ -6,7 +6,9 @@ import hashlib
import json
import unicodedata
from collections.abc import Iterable
from copy import deepcopy
from dataclasses import dataclass
from typing import Any
from gewu.domain.contracts import (
AutoAcceptPolicy,
@@ -59,6 +61,73 @@ def proposal_payload_digest(proposal: ProposalRevision) -> str:
return hashlib.sha256(canonical_json(operations)).hexdigest()
def prepare_proposal_digests(draft: dict[str, Any]) -> ProposalRevision:
"""Validate a proposal draft, fill its dependent evidence digests, and pin its payload."""
value = deepcopy(draft)
operations = value.get("operations")
if not isinstance(operations, list) or not operations:
raise ValueError("Proposal must contain a non-empty operations array")
for operation in operations:
if not isinstance(operation, dict):
raise ValueError("Proposal operations must be objects")
node = operation.get("node")
if isinstance(node, dict):
for group in ("claims", "assumptions", "limitations"):
claims = node.get(group, [])
if not isinstance(claims, list):
raise ValueError(f"Node {group} must be an array")
for index, claim in enumerate(claims):
if not isinstance(claim, dict) or not isinstance(claim.get("text"), str):
raise ValueError(f"Node {group}[{index}] must contain claim text")
digest = assertion_digest(claim["text"])
evidence = claim.get("evidence")
if not isinstance(evidence, list) or not evidence:
raise ValueError(f"Node {group}[{index}] must contain evidence")
path = f"{group}[{index}].text"
evidence_digests = []
for item in evidence:
if not isinstance(item, dict):
raise ValueError("Evidence references must be objects")
item["assertion_path"] = path
item["assertion_digest"] = digest
parsed_evidence = EvidenceReference.model_validate(item, strict=True)
item.clear()
item.update(parsed_evidence.model_dump(mode="json"))
evidence_digests.append(evidence_digest(parsed_evidence))
assessment = claim.get("assessment")
if not isinstance(assessment, dict):
raise ValueError(f"Node {group}[{index}] must contain an assessment")
assessment["assertion_digest"] = digest
assessment["evidence_digests"] = evidence_digests
edge = operation.get("edge")
if isinstance(edge, dict):
edge_evidence = edge.get("evidence", [])
if not isinstance(edge_evidence, list):
raise ValueError("Edge evidence must be an array")
for item in edge_evidence:
if not isinstance(item, dict):
raise ValueError("Evidence references must be objects")
item["assertion_path"] = "edge"
item["assertion_digest"] = "0" * 64
parsed_edge = TypedEdge.model_validate(edge, strict=True)
digest = edge_assertion_digest(parsed_edge)
for item in edge_evidence:
item["assertion_digest"] = digest
parsed_evidence = EvidenceReference.model_validate(item, strict=True)
item.clear()
item.update(parsed_evidence.model_dump(mode="json"))
edge["evidence"] = edge_evidence
value["payload_digest"] = "0" * 64
provisional = ProposalRevision.model_validate_json(
json.dumps(value, ensure_ascii=False, allow_nan=False), strict=True
)
return provisional.model_copy(update={"payload_digest": proposal_payload_digest(provisional)})
def edge_assertion_digest(edge: TypedEdge) -> str:
"""Bind edge evidence to the relationship, conditions and rationale."""