Cai
7 days ago 0925a419ad0270ba193c558668d318c7237a3d63
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
from __future__ import annotations
 
from dataclasses import dataclass, field
from enum import Enum
from pathlib import Path
from typing import Any, Mapping
import re
 
 
class ContractError(ValueError):
    def __init__(self, code: "ErrorCode", field_name: str | None, detail: str):
        super().__init__(f"{code.value}:{field_name or '-'}:{detail}")
        self.code = code
        self.field_name = field_name
        self.detail = detail
 
 
class ErrorCode(str, Enum):
    NONE = "NONE"
    TASK_SPEC_INVALID = "TASK_SPEC_INVALID"
    CLOCK_INVALID = "CLOCK_INVALID"
    DEADLINE_EXPIRED = "DEADLINE_EXPIRED"
    PROCESS_START_FAILED = "PROCESS_START_FAILED"
    PROCESS_TIMEOUT = "PROCESS_TIMEOUT"
    PROCESS_OUTPUT_LIMIT = "PROCESS_OUTPUT_LIMIT"
    PROCESS_LIVENESS_UNKNOWN = "PROCESS_LIVENESS_UNKNOWN"
    DEVICE_NOT_UNIQUE = "DEVICE_NOT_UNIQUE"
    DEVICE_NOT_ONLINE = "DEVICE_NOT_ONLINE"
    PACKAGE_MISSING = "PACKAGE_MISSING"
    CACHE_UNREADABLE = "CACHE_UNREADABLE"
    UI_ANCHOR_DRIFT = "UI_ANCHOR_DRIFT"
    ACCESS_CONTROL_PRESENT = "ACCESS_CONTROL_PRESENT"
    UI_CURSOR_RESTORE_FAILED = "UI_CURSOR_RESTORE_FAILED"
    DETAIL_RESULT_MISMATCH = "DETAIL_RESULT_MISMATCH"
    CANDIDATE_REJECTED = "CANDIDATE_REJECTED"
    TRIGGER_NOT_OCCURRED = "TRIGGER_NOT_OCCURRED"
    TRIGGER_UNCERTAIN = "TRIGGER_UNCERTAIN"
    CACHE_TIMEOUT = "CACHE_TIMEOUT"
    CACHE_AMBIGUOUS = "CACHE_AMBIGUOUS"
    REMOTE_FILE_CHANGED = "REMOTE_FILE_CHANGED"
    LOCK_TIMEOUT = "LOCK_TIMEOUT"
    QUOTA_LEDGER_INVALID = "QUOTA_LEDGER_INVALID"
    QUOTA_REPLAY_CONFLICT = "QUOTA_REPLAY_CONFLICT"
    QUOTA_EXHAUSTED = "QUOTA_EXHAUSTED"
    QUOTA_DATE_UNCERTAIN = "QUOTA_DATE_UNCERTAIN"
    PULL_FAILED = "PULL_FAILED"
    PDF_MAGIC_INVALID = "PDF_MAGIC_INVALID"
    BYTE_COUNT_MISMATCH = "BYTE_COUNT_MISMATCH"
    HASH_MISMATCH = "HASH_MISMATCH"
    PDF_NOT_OPENABLE = "PDF_NOT_OPENABLE"
    PAGE_COUNT_MISMATCH = "PAGE_COUNT_MISMATCH"
    PDF_ENCRYPTED = "PDF_ENCRYPTED"
    FINAL_PATH_CONFLICT = "FINAL_PATH_CONFLICT"
    PUBLISH_FAILED = "PUBLISH_FAILED"
    RECOVERY_UNKNOWN = "RECOVERY_UNKNOWN"
    MANIFEST_INVALID = "MANIFEST_INVALID"
    PERSIST_LATE = "PERSIST_LATE"
    DUPLICATE_EXISTING_ARTIFACT = "DUPLICATE_EXISTING_ARTIFACT"
    UNEXPECTED_EXCEPTION = "UNEXPECTED_EXCEPTION"
 
 
class TerminalStatus(str, Enum):
    STATE_UNCERTAIN = "STATE_UNCERTAIN"
    BLOCKED_ACCESS_CONTROL = "BLOCKED_ACCESS_CONTROL"
    TIME_BUDGET_STOP = "TIME_BUDGET_STOP"
    PARTIAL_QUOTA_STOP = "PARTIAL_QUOTA_STOP"
    BLOCKED_INPUT = "BLOCKED_INPUT"
    BLOCKED_ENVIRONMENT = "BLOCKED_ENVIRONMENT"
    BLOCKED_AMBIGUOUS_MAPPING = "BLOCKED_AMBIGUOUS_MAPPING"
    VALIDATION_FAILED = "VALIDATION_FAILED"
    INTERNAL_ERROR = "INTERNAL_ERROR"
    PARTIAL_SUCCESS = "PARTIAL_SUCCESS"
    SUCCESS = "SUCCESS"
 
 
TERMINAL_MAPPING: dict[TerminalStatus, tuple[str, int]] = {
    TerminalStatus.STATE_UNCERTAIN: ("STATE_UNCERTAIN", 27),
    TerminalStatus.BLOCKED_ACCESS_CONTROL: ("BLOCKED_ACCESS_CONTROL", 13),
    TerminalStatus.TIME_BUDGET_STOP: ("TIME_BUDGET_STOP", 10),
    TerminalStatus.PARTIAL_QUOTA_STOP: ("PARTIAL_QUOTA_STOP", 11),
    TerminalStatus.BLOCKED_INPUT: ("BLOCKED_INPUT", 12),
    TerminalStatus.BLOCKED_ENVIRONMENT: ("BLOCKED_ENVIRONMENT", 14),
    TerminalStatus.BLOCKED_AMBIGUOUS_MAPPING: ("BLOCKED_AMBIGUOUS_MAPPING", 15),
    TerminalStatus.VALIDATION_FAILED: ("VALIDATION_FAILED", 16),
    TerminalStatus.INTERNAL_ERROR: ("INTERNAL_ERROR", 20),
    TerminalStatus.PARTIAL_SUCCESS: ("ACCEPTED", 2),
    TerminalStatus.SUCCESS: ("ACCEPTED", 0),
}
TERMINAL_PRECEDENCE = tuple(TERMINAL_MAPPING)
 
 
class EvidenceState(str, Enum):
    N = "N"
    V = "V"
    I = "I"
    U = "U"
 
 
class PackageState(str, Enum):
    P00_INIT = "P00_INIT"
    P01_RESERVED = "P01_RESERVED"
    P02_TRIGGER_UNKNOWN = "P02_TRIGGER_UNKNOWN"
    P03_CACHE_MATCHED = "P03_CACHE_MATCHED"
    P04_STAGING_PARTIAL = "P04_STAGING_PARTIAL"
    P05_STAGING_VALID = "P05_STAGING_VALID"
    P06_PUBLISHED = "P06_PUBLISHED"
    P07_MANIFESTED = "P07_MANIFESTED"
    P08_DELIVERY = "P08_DELIVERY"
    P09_TIMING = "P09_TIMING"
    P10_CLOSED = "P10_CLOSED"
 
 
class Lane(str, Enum):
    NEW = "NEW_TRIGGER"
    PURE = "PURE_REUSE"
 
 
class ItemStatus(str, Enum):
    SUCCESS = "SUCCESS"
    DUPLICATE = "DUPLICATE"
    FAILED = "FAILED"
    STOPPED = "STOPPED"
 
 
class QuotaState(str, Enum):
    NONE = "NONE"
    RESERVED = "RESERVED"
    CONFIRMED = "CONFIRMED"
    UNCERTAIN = "UNCERTAIN"
    RELEASED = "RELEASED"
 
 
HASH_RE = re.compile(r"^[0-9a-f]{64}$")
ID_RE = re.compile(r"^[A-Za-z0-9_.:-]{1,200}$")
 
 
TASK_KEYS_V003 = (
    "schema_version", "contract_version", "project_id", "handoff_id", "task_id",
    "source_role_instance_id", "source_thread_id", "target_role_instance_id",
    "target_thread_id", "reply_thread_id", "requester", "review_owner", "mode",
    "query", "quantity", "aliases", "analysts", "institutions", "report_types",
    "date_range", "minimum_pages", "exclude", "source_scope", "destination",
    "priority", "naming_requirement", "output_root", "quota_ledger", "adb_executable",
    "pdfinfo_executable", "package_name", "cache_root", "device_serial",
    "observed_at_utc", "total_budget_ms", "close_reserve_ms",
    "batch_increment_budget_ms", "min_screens", "normal_max_screens",
    "hard_max_screens", "hard_max_candidates", "performance_slot_id",
    "performance_plan_id",
)
 
TASK_KEYS = TASK_KEYS_V003 + ("expected_reports",)
 
EXPECTED_REPORT_KEYS = (
    "report_identity", "title", "institution", "report_date", "page_count",
)
 
 
@dataclass(frozen=True)
class TaskSpec:
    raw: Mapping[str, Any]
 
    @classmethod
    def from_mapping(cls, value: Mapping[str, Any], *, exact: bool = True) -> "TaskSpec":
        keys = tuple(value.keys())
        schema = value.get("schema_version")
        expected_keys = TASK_KEYS if schema == "HIBOR_FAST_TASK_SPEC_V004" else TASK_KEYS_V003
        if schema not in {"HIBOR_FAST_TASK_SPEC_V003", "HIBOR_FAST_TASK_SPEC_V004"}:
            raise ContractError(ErrorCode.TASK_SPEC_INVALID, "schema_version", "V003 or V004 required")
        if exact and keys != expected_keys:
            missing = [k for k in expected_keys if k not in value]
            extra = [k for k in value if k not in expected_keys]
            raise ContractError(ErrorCode.TASK_SPEC_INVALID, "keys", f"order/missing/extra:{missing}:{extra}")
        required = {"project_id", "task_id", "handoff_id", "mode", "query", "quantity",
                    "output_root", "quota_ledger", "adb_executable", "pdfinfo_executable",
                    "package_name", "cache_root", "total_budget_ms", "close_reserve_ms"}
        if missing := required.difference(value):
            raise ContractError(ErrorCode.TASK_SPEC_INVALID, "keys", f"missing:{sorted(missing)}")
        if value["project_id"] != "project-info":
            raise ContractError(ErrorCode.TASK_SPEC_INVALID, "project_id", "must be project-info")
        if value["package_name"] != "cn.com.hibor":
            raise ContractError(ErrorCode.TASK_SPEC_INVALID, "package_name", "must be cn.com.hibor")
        if value["cache_root"] != "/sdcard/Android/data/cn.com.hibor/files/myfile/":
            raise ContractError(ErrorCode.TASK_SPEC_INVALID, "cache_root", "unexpected cache")
        if value["mode"] not in {"dry-run", "discover", "collect-one", "batch", "resume-postprocess"}:
            raise ContractError(ErrorCode.TASK_SPEC_INVALID, "mode", "unsupported")
        quantity = value["quantity"]
        if type(quantity) is not int or quantity < 1 or quantity > 10:
            raise ContractError(ErrorCode.TASK_SPEC_INVALID, "quantity", "must be 1..10")
        total = value["total_budget_ms"]
        expected = 600_000 if quantity == 1 else 600_000 + 240_000 * (quantity - 1)
        if type(total) is not int or total != expected:
            raise ContractError(ErrorCode.TASK_SPEC_INVALID, "total_budget_ms", f"expected {expected}")
        if value["close_reserve_ms"] < 15_000 or value["close_reserve_ms"] >= total:
            raise ContractError(ErrorCode.TASK_SPEC_INVALID, "close_reserve_ms", "outside safe range")
        if type(value.get("batch_increment_budget_ms")) is not int or value["batch_increment_budget_ms"] != 240_000:
            raise ContractError(ErrorCode.TASK_SPEC_INVALID, "batch_increment_budget_ms", "must be 240000")
        if not isinstance(value.get("observed_at_utc"), str) or not value["observed_at_utc"].endswith("Z"):
            raise ContractError(ErrorCode.TASK_SPEC_INVALID, "observed_at_utc", "UTC Z timestamp required")
        if not isinstance(value.get("source_scope"), Mapping):
            raise ContractError(ErrorCode.TASK_SPEC_INVALID, "source_scope", "mapping required")
        for path_key in ("output_root", "quota_ledger", "adb_executable", "pdfinfo_executable"):
            if not isinstance(value[path_key], str) or not value[path_key]:
                raise ContractError(ErrorCode.TASK_SPEC_INVALID, path_key, "non-empty string required")
        expected_reports = value.get("expected_reports", [])
        if not isinstance(expected_reports, list):
            raise ContractError(ErrorCode.TASK_SPEC_INVALID, "expected_reports", "array required")
        identities: set[str] = set()
        for index, report in enumerate(expected_reports):
            if not isinstance(report, Mapping) or tuple(report.keys()) != EXPECTED_REPORT_KEYS:
                raise ContractError(ErrorCode.TASK_SPEC_INVALID,
                                    f"expected_reports[{index}]", "exact ordered report keys required")
            identity = report["report_identity"]
            if not isinstance(identity, str) or not HASH_RE.fullmatch(identity) or identity in identities:
                raise ContractError(ErrorCode.TASK_SPEC_INVALID,
                                    f"expected_reports[{index}].report_identity", "unique SHA-256 required")
            identities.add(identity)
            for field_name in ("title", "institution", "report_date"):
                if not isinstance(report[field_name], str) or not report[field_name].strip():
                    raise ContractError(ErrorCode.TASK_SPEC_INVALID,
                                        f"expected_reports[{index}].{field_name}", "non-empty string required")
            if type(report["page_count"]) is not int or report["page_count"] < 1:
                raise ContractError(ErrorCode.TASK_SPEC_INVALID,
                                    f"expected_reports[{index}].page_count", "positive INT required")
        if value["mode"] in {"collect-one", "batch"}:
            if schema != "HIBOR_FAST_TASK_SPEC_V004" or len(expected_reports) != quantity:
                raise ContractError(ErrorCode.TASK_SPEC_INVALID, "expected_reports",
                                    "V004 collection requires one exact report per item")
        elif expected_reports:
            raise ContractError(ErrorCode.TASK_SPEC_INVALID, "expected_reports",
                                "only collect-one/batch may bind reports")
        return cls(dict(value))
 
    def __getattr__(self, name: str) -> Any:
        try:
            return self.raw[name]
        except KeyError as exc:
            raise AttributeError(name) from exc
 
    @property
    def output_path(self) -> Path:
        return Path(self.raw["output_root"])
 
    @property
    def quota_path(self) -> Path:
        return Path(self.raw["quota_ledger"])
 
 
@dataclass(frozen=True)
class PackageEvidence:
    lane: Lane
    state: PackageState
    staging: EvidenceState
    final: EvidenceState
    quota_terminal: EvidenceState
    artifact_terminal: EvidenceState
    manifest: EvidenceState
    delivery: EvidenceState
    timing: EvidenceState
    terminal: EvidenceState
 
    def any_unknown(self) -> bool:
        return EvidenceState.U in (
            self.staging, self.final, self.quota_terminal, self.artifact_terminal,
            self.manifest, self.delivery, self.timing, self.terminal,
        )
 
 
@dataclass(frozen=True)
class TerminalReceipt:
    target_path: Path
    terminal_path: Path | None
    persist_attempted: bool
    persisted: bool | None
    presence: EvidenceState
    operation_completed: bool | None
    liveness_unknown: bool
    stdout_emitted: bool
    bytes: int | None
    sha256: str | None
    error_code: ErrorCode | None
    source_state: PackageState
 
    def validate(self) -> None:
        if self.presence is EvidenceState.V:
            valid = (self.persist_attempted and self.persisted is True and
                     self.operation_completed is True and not self.liveness_unknown and
                     self.terminal_path == self.target_path and self.bytes is not None and
                     self.bytes >= 2 and self.sha256 is not None and HASH_RE.fullmatch(self.sha256) and
                     self.error_code is None)
        elif self.presence is EvidenceState.U:
            pair = ((self.operation_completed is True and not self.liveness_unknown and
                     self.error_code is ErrorCode.RECOVERY_UNKNOWN) or
                    (self.operation_completed is None and self.liveness_unknown and
                     self.error_code is ErrorCode.PROCESS_LIVENESS_UNKNOWN))
            valid = (self.persist_attempted and self.persisted is None and pair and
                     self.terminal_path is None and self.bytes is None and self.sha256 is None)
        elif self.presence is EvidenceState.I:
            known_pair = (self.bytes is None and self.sha256 is None) or (
                self.bytes is not None and self.sha256 is not None and HASH_RE.fullmatch(self.sha256))
            valid = (self.persist_attempted and self.persisted is False and
                     self.operation_completed is True and not self.liveness_unknown and
                     self.terminal_path is None and self.error_code is ErrorCode.PERSIST_LATE and known_pair)
        else:
            if self.persist_attempted:
                valid = (self.persisted is False and self.operation_completed is True and
                         not self.liveness_unknown and self.terminal_path is None and
                         self.bytes is None and self.sha256 is None and
                         self.error_code is ErrorCode.PERSIST_LATE)
            else:
                valid = (self.persisted is False and self.operation_completed is False and
                         not self.liveness_unknown and self.terminal_path is None and
                         self.bytes is None and self.sha256 is None)
        if not valid:
            raise ContractError(ErrorCode.TASK_SPEC_INVALID, "TerminalReceipt", "cross-field mismatch")
 
 
@dataclass(frozen=True)
class Candidate:
    candidate_id: str
    title: str
    institution: str | None
    report_date: str | None
    analysts: tuple[str, ...] = field(default_factory=tuple)
    page_count: int | None = None
    score: int = 0
    bounds: tuple[int, int, int, int] | None = None
 
    def __post_init__(self) -> None:
        if not ID_RE.fullmatch(self.candidate_id):
            raise ContractError(ErrorCode.TASK_SPEC_INVALID, "candidate_id", "invalid")