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:
chenmingxuan committed 2026-10-27 01:13:41 +08:00
1 parent fac4ccb760
commit be6e8c2abd
79 files changed
+2263 -277

No files matched your search

+78
View File
@@ -103,6 +103,84 @@ async def test_wrap_update_note_tool_valid_status_change(tmp_path):
assert ctx.planner_task is None
@pytest.mark.asyncio
async def test_short_plan_replacement_cannot_duplicate_untouched_tail(tmp_path):
original = "- [/] Enter text\n- [ ] Clear text\n - verify: Input is empty\n"
path = _setup_plan(tmp_path, original)
ctx = _make_ctx(tmp_path, disable_planner_validation=True)
with patch(
"artemis.graph.graph.invoke_tool_with_injection",
side_effect=_fake_update_invoke(path),
):
result = await wrap_update_note_tool(ctx, _mock_tool("update_note")).ainvoke(
{
"key": "task_plan",
"target": "- [/] Enter text",
"replacement": "- [x] Enter text\n- [/] Clear text",
"tool_call_id": "duplicate-tail",
}
)
assert isinstance(result, ToolMessage)
assert result.status == "error"
assert "Clear text" in result.content
assert "target" in result.content
assert path.read_text(encoding="utf-8") == original
assert ctx.planner_task is None
assert ctx.pending_checkpoints == []
@pytest.mark.asyncio
@pytest.mark.parametrize("keep_existing_duplicate", [False, True])
async def test_duplicate_plan_can_be_repaired_with_a_complete_target(
tmp_path, keep_existing_duplicate
):
original = "- [x] Enter text\n- [/] Clear text\n- [ ] Clear text\n"
path = _setup_plan(tmp_path, original)
ctx = _make_ctx(tmp_path, disable_planner_validation=True)
replacement = "- [x] Enter text\n- [/] Clear text\n"
if keep_existing_duplicate:
replacement += "- [x] Clear text\n"
with patch(
"artemis.graph.graph.invoke_tool_with_injection",
side_effect=_fake_update_invoke(path),
):
result = await wrap_update_note_tool(ctx, _mock_tool("update_note")).ainvoke(
{
"key": "task_plan",
"target": original,
"replacement": replacement,
"tool_call_id": "repair-tail",
}
)
assert result == "Success"
assert path.read_text(encoding="utf-8") == replacement
@pytest.mark.asyncio
@pytest.mark.parametrize("tool_name", ["save_note", "append_note"])
async def test_other_plan_writes_cannot_introduce_duplicate_milestones(tmp_path, tool_name):
original = "- [/] Enter text\n- [ ] Clear text\n"
path = _setup_plan(tmp_path, original)
ctx = _make_ctx(tmp_path, disable_planner_validation=True)
async def fake_invoke(tool, args, tool_call_id, state, record_trace=None):
content = args["content"]
if tool_name == "append_note":
content = path.read_text(encoding="utf-8") + content
path.write_text(content, encoding="utf-8")
return "Success"
content = "- [/] Clear text\n"
if tool_name == "save_note":
content = original + content
with patch("artemis.graph.graph.invoke_tool_with_injection", side_effect=fake_invoke):
result = await wrap_note_tool(ctx, _mock_tool(tool_name)).ainvoke(
{"key": "task_plan", "content": content, "tool_call_id": "duplicate"}
)
assert result.status == "error"
assert path.read_text(encoding="utf-8") == original
@pytest.mark.asyncio
async def test_wrap_update_note_tool_intentional_rename_triggers_validation(tmp_path):
task_plan_path = _setup_plan(tmp_path)