File size: 1,629 Bytes
d04144c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
from __future__ import annotations

from dataclasses import dataclass

from .integration import IntegratedRoleResult
from .schemas import SharedRolePacket


@dataclass(frozen=True)
class CompletionCheck:
    passed: bool
    resolution_score: float
    violations: tuple[str, ...]
    missing_evidence_task_ids: tuple[str, ...]


class CompletionGate:
    def evaluate(self, packet: SharedRolePacket, integrated: IntegratedRoleResult, *, external_violations=()) -> CompletionCheck:
        violations = list(external_violations)
        by_task = {item.task_id: item for item in integrated.resolutions}
        missing_evidence: list[str] = []
        resolved = 0
        for task in packet.tasks:
            resolution = by_task.get(task.task_id)
            if resolution is None or not resolution.resolved:
                violations.append("major_need_missing"); continue
            if task.required_facts and not resolution.evidence_ids:
                missing_evidence.append(task.task_id); violations.append("evidence_incomplete"); continue
            if task.actionability_required and not resolution.action_steps:
                violations.append("required_actionability_missing"); continue
            resolved += 1
        if integrated.missing_task_ids: violations.append("major_need_missing")
        if integrated.contradiction_task_ids: violations.append("contradiction")
        total = max(1, len(packet.tasks))
        deduped = tuple(dict.fromkeys(violations))
        return CompletionCheck(not deduped and resolved == len(packet.tasks), resolved / total, deduped, tuple(sorted(set(missing_evidence))))