"""Guard the prompt evidence contract; no model or device is invoked here.""" from unittest.mock import AsyncMock, MagicMock, patch import pytest from artemis.agents.checker.checker import ( CheckReport, assemble_checker_prompt_segments, run_final_check, ) from artemis.context import ArtemisContext, ExecutionSetup @pytest.mark.parametrize("entry", ["checkpoint", "final"]) def test_bound_persistent_readout_can_supply_precision_beyond_pixel_ticks(entry): prompt = assemble_checker_prompt_segments(entry, [], probe_tool_registered=False) # Native origin alone is insufficient: the rule must bind the displayed value # to the selected state and require stability plus the visible pointer check. for requirement in ( "标签、节点角色/层级", "目标控件当前所选值或固定指针所选时间", "不能仅因原生节点含有数值或执行者称其可信", "在被验收状态中持续显示", "引用已停稳或暂停的证据", "与同一控件的指针及可辨刻度一致", "结合读数精度与用户容差", "粗刻度无法独立区分秒数,不等于", "无需再由粗像素刻度独立插值证明同一精确值", ): assert requirement in prompt assert "拖动浮层或原生时间读数与固定指针刻度冲突时" not in prompt @pytest.mark.parametrize("entry", ["checkpoint", "final"]) def test_native_origin_does_not_promote_unbound_or_conflicting_evidence(entry): prompt = assemble_checker_prompt_segments(entry, [], probe_tool_registered=False) for excluded_source in ("事件卡片时间", "视频画面水印", "录制起止时间", "拖动/松手浮层"): assert excluded_source in prompt assert "即使它们也能通过原生节点读取,仍须先证明上述绑定与持久性" in prompt assert "可辨精度上存在实际冲突" in prompt assert "读数仍在变化、绑定不明或无法确认已停稳/暂停" in prompt assert "应判证据不足(inconclusive)" in prompt assert "不得挑选有利读数或忽略矛盾" in prompt assert "可信读数明确超出用户容差时仍按通用规则判定" in prompt @pytest.mark.parametrize("entry", ["checkpoint", "final"]) def test_pixel_fallback_remains_quantitative_and_requires_post_action_evidence(entry): prompt = assemble_checker_prompt_segments(entry, [], probe_tool_registered=False) fallback = prompt.split("没有可信持久读数时,", 1)[1] assert "动作后已停稳截图的两处真实数值刻度与固定指针位置作线性插值" in fallback assert "单位换算、估读不确定性及目标误差" in fallback assert "目标误差加估读不确定性不超过用户容差" in fallback assert "刻度非线性或无法确定已停稳,仍应判证据不足" in fallback assert "操作前的校准计划及回执误差不能替代动作后测量" in fallback @pytest.mark.asyncio async def test_final_review_receives_live_readout_and_rules_separately_from_report_claims(): ctx = MagicMock(spec=ArtemisContext) ctx.execution_setup = ExecutionSetup() ctx.data_engine = MagicMock() ctx.data_engine.base_dir = "unused" ctx.data_engine.get_agent_friendly_steps.return_value = [] ctx.data_engine.get_step_number.return_value = 7 elements = "[1] 当前所选值: 23.5; [2] 已暂停" review = AsyncMock(return_value=CheckReport(verdicts=[])) with ( patch("artemis.agents.checker.checker._run_check_loop", new=review), patch( "artemis.agents.checker.checker._capture_final_screen", new=AsyncMock(return_value=("captured-image", elements)), ), ): await run_final_check( ctx, goal="核对当前所选值及容差", plan_text="", ledger=[], check_items=[], execution_report="执行者声称当前所选值为 88", ) review.assert_awaited_once() messages = review.await_args.args[1] assert "无需再由粗像素刻度独立插值证明同一精确值" in messages[0].content assert "不得挑选有利读数或忽略矛盾" in messages[0].content content = messages[1].content text = content[0]["text"] assert "Executor's Final Report (claims to verify, not independent evidence)" in text assert "执行者声称当前所选值为 88" in text assert f"# Final Screen Elements\n{elements}" in text assert any( block.get("type") == "image_url" and block["image_url"]["url"] == "data:image/jpeg;base64,captured-image" for block in content )