fix(qg): read result: alongside verdict:/status: in tests gate
All checks were successful
CI / test (push) Successful in 12s
CI / test (pull_request) Successful in 11s

_parse_tests_verdict now accepts three equal-rank machine-readable
frontmatter fields in 13-test-report.md — result: (canonical tester
output), verdict: and status: (legacy/enduro-trails). Any one non-empty
field suffices; a negative token in any field stays authoritative.

Fixes the producer/consumer contract mismatch where the tester emits
`result: PASS` (per .openclaw/agents/tester.md) but the gate only read
verdict:/status:, causing a testing->development rollback loop until
MAX_DEVELOPER_RETRIES (observed on ORCH-17). Token sets frozen and gate
signature/QG_CHECKS unchanged for full backward compatibility.

Refs: ORCH-047
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-05 21:03:32 +00:00
parent 75fb4069a4
commit 51a76e8169
3 changed files with 88 additions and 15 deletions

View File

@@ -322,6 +322,64 @@ class TestCheckTestsPassed:
assert passed is False
assert "not found" in reason.lower()
# --- ORCH-047: `result:` is read as an equal-rank machine field ---
def test_result_pass_passes(self, setup_work_item_dir):
# TC-01 / AC-01: canonical tester field `result: PASS` (no verdict/status).
self._write(
setup_work_item_dir,
"---\ntype: test-report\nresult: PASS\n---\n\n# Test Report\n",
)
passed, reason = check_tests_passed("enduro-trails", "ET-001")
assert passed is True
assert "PASS" in reason
def test_result_fail_fails(self, setup_work_item_dir):
# TC-02 / AC-02: `result: FAIL` (no verdict/status) -> rollback, reason has FAIL.
self._write(setup_work_item_dir, "---\nresult: FAIL\n---\n\nbody\n")
passed, reason = check_tests_passed("enduro-trails", "ET-001")
assert passed is False
assert "FAIL" in reason
def test_result_pass_but_verdict_blocked_fails(self, setup_work_item_dir):
# TC-03 / AC-03: negative in another field is authoritative over result: PASS.
self._write(
setup_work_item_dir,
"---\nresult: PASS\nverdict: BLOCKED\n---\n\n23 passed\n",
)
passed, reason = check_tests_passed("enduro-trails", "ET-001")
assert passed is False
assert "BLOCKED" in reason
def test_result_pass_but_status_failed_fails(self, setup_work_item_dir):
# TC-04 / AC-03: status: failed authoritative over result: PASS.
self._write(
setup_work_item_dir,
"---\nresult: PASS\nstatus: failed\n---\n\nbody\n",
)
passed, reason = check_tests_passed("enduro-trails", "ET-001")
assert passed is False
assert "FAILED" in reason
def test_result_ready_to_deploy_passes(self, setup_work_item_dir):
# TC-05 / AC-04: positive token without the word PASS, in result field.
self._write(
setup_work_item_dir,
"---\nresult: ready-to-deploy\n---\n\nbody\n",
)
passed, reason = check_tests_passed("enduro-trails", "ET-001")
assert passed is True
def test_no_machine_field_reason_mentions_result(self, setup_work_item_dir):
# AC-06: none of result/verdict/status -> fail; reason now lists result too.
self._write(
setup_work_item_dir,
"---\ntype: test-report\nversion: 1\n---\n\nResult: PASS\n",
)
passed, reason = check_tests_passed("enduro-trails", "ET-001")
assert passed is False
assert "result" in reason.lower()
class TestCheckDeployStatus:
"""BUG 8: deploy -> done must be gated on the deployer's machine-readable