feat: release 2.2.0 with PC-only tests and execution reliability fixes
Unify task scheduling and queue state, fix Android input and observation recovery, preserve screenshot paths, and match final review criteria accurately. Add collapsible copyable results and publish the complete 2.1.0 upgrade notes. Validation: 3189 backend tests, 326 frontend tests, 24 client tests, production build and quality gates.
This commit is contained in:
1 parent
fac4ccb760
commit
be6e8c2abd
79 files changed
+2263
-277
No files matched your search
@@ -84,7 +84,12 @@ class CheckVerdict(BaseModel):
|
||||
"""One check item's verdict. The verdict value is never altered by the
|
||||
release decision (fail-open affects release only)."""
|
||||
|
||||
item_text: str = Field(description="The check line's text, quoted verbatim.")
|
||||
item_text: str = Field(
|
||||
description=(
|
||||
"The criterion body, quoted verbatim; exclude the displayed list marker "
|
||||
"and [verify]/[assert] (on_complete)/(at_end) metadata prefix."
|
||||
)
|
||||
)
|
||||
kind: Literal["verify", "assert"]
|
||||
status: Literal["passed", "failed", "inconclusive"]
|
||||
evidence: str = Field(
|
||||
@@ -470,6 +475,24 @@ def _format_check_items(check_items: list) -> str:
|
||||
# --- Shared tool loop ----------------------------------------------------------------
|
||||
|
||||
|
||||
def _canonicalize_verdict_items(verdicts: list[CheckVerdict], check_items) -> list[CheckVerdict]:
|
||||
"""Recognize only exact labels rendered for this review's declared checks."""
|
||||
expected = {(ci.kind, ci.text) for ci in check_items}
|
||||
labels = {}
|
||||
for ci in check_items:
|
||||
if getattr(ci, "when", None) is None:
|
||||
continue
|
||||
label = _format_check_items([ci])
|
||||
labels[(ci.kind, label)] = ci.text
|
||||
labels[(ci.kind, label.removeprefix("- "))] = ci.text
|
||||
return [
|
||||
v.model_copy(update={"item_text": labels[(v.kind, v.item_text)]})
|
||||
if (v.kind, v.item_text) not in expected and (v.kind, v.item_text) in labels
|
||||
else v
|
||||
for v in verdicts
|
||||
]
|
||||
|
||||
|
||||
def _completed_text_report(response, check_items) -> CheckReport | None:
|
||||
"""Reuse a complete Checker verdict, never a draft, tool receipt or partial stream."""
|
||||
if not isinstance(response, AIMessage) or response.tool_calls or response.invalid_tool_calls:
|
||||
@@ -495,6 +518,9 @@ def _completed_text_report(response, check_items) -> CheckReport | None:
|
||||
# and every declared check so an incomplete answer still gets formatted.
|
||||
if report.test_result is None or not report.test_result.reason.strip():
|
||||
return None
|
||||
report = report.model_copy(
|
||||
update={"verdicts": _canonicalize_verdict_items(report.verdicts, check_items)}
|
||||
)
|
||||
expected = {(ci.kind, ci.text) for ci in check_items}
|
||||
covered = {(v.kind, v.item_text) for v in report.verdicts}
|
||||
if not expected <= covered:
|
||||
@@ -532,7 +558,7 @@ def _normalize_report(report: CheckReport, check_items: list) -> CheckReport:
|
||||
"""Node-side hygiene: vague failed verdicts downgrade to inconclusive; every
|
||||
expected item gets a verdict (missing ones are inconclusive)."""
|
||||
verdicts: list[CheckVerdict] = []
|
||||
for v in report.verdicts:
|
||||
for v in _canonicalize_verdict_items(report.verdicts, check_items):
|
||||
if v.status == "failed" and not v.evidence.strip():
|
||||
verdicts.append(
|
||||
CheckVerdict(
|
||||
|
||||
Reference in new issue
Block a user