v5: rebase 融合修复(main 演进 + 附录源同步)

- websearch.py:补回 import socket(栈 46ca59f 的 lint 清理在栈
  上无害,replay 到 main 后 main #274 的超时/DNS 显式报错
  代码引用 socket,缺 import 致 2 项 NameError;re 在 main
  与重排后树中均 0 引用,不补)
- 3 个 websearch fake 补 scope="general" 参数(main 的
  handle_request 现必传 scope,旧 fake 致 TypeError 被吞成
  isError,3 项失败)
- playwright 工具数 14→18(main 新增 4 个,与 main _TOOLS
  逐名一致、无重复)
- playwright 种子测试改按 main 安全升级规则:源脚本 v1→v2
  且 manifest 证明未改动才刷新;未知改动只上报跳过不覆盖
- 附录源同步:01/02/07 三个 roles 源文件与 SKILL.md 附录章
  做同一改写,check_appendix 来源一致性恢复
This commit is contained in:
host committed 2026-11-03 02:49:26 +00:00
1 parent 34a19ab9e2
commit 39ec2b4435
7 files changed
+23 -10

No files matched your search

@@ -12,7 +12,7 @@
## 产出
按 `templates/artifacts/prd-outline.md` 写一份 PRD,至少包含:
按下面大纲写一份 PRD,至少包含:
1. 目标用户和核心场景(不超过 3 个核心能力)。
2. 用户故事,每个故事都有可检验的验收标准(能做什么、做到什么程度算通过)。
@@ -18,7 +18,7 @@
3. 数据模型:核心实体、字段、关系、同步冲突时以哪端为准。
4. Agent 协议:如果产品含 agent 能力,写清任务创建、执行、状态、取消、
超时、失败重试的协议,不要让各客户端自己发明一套。
协议模板见 `templates/artifacts/` 下的 agent 任务契约模板。
(Agent 协议、状态机、错误 taxonomy 的要素见本章第 4、5、6 项。)
5. 任务状态机:至少包含创建、排队、运行中、等审批、已完成、已取消、失败、超时,
每个状态的进入条件、退出条件、可执行操作都要写清。
6. 错误 taxonomy:至少区分模型失败、工具失败、用户取消、超时、权限拒绝、配额超限,
@@ -4,7 +4,7 @@
## 协调员准备
1. 按 `templates/artifacts/acceptance-checklist.md` 生成本项目的验收清单,
1. 按 PRD 验收标准生成本项目的验收清单,
每一项对应 PRD 里的一条验收标准,不额外加、不悄悄删。
2. 为 web 单端准备一份演示产物:怎么启动、测哪个核心流程、
已知问题是什么、哪些平台未编译验证。web 演示可真跑;
+6 -4
View File
@@ -152,7 +152,9 @@ class PlaywrightHandleTest(unittest.TestCase):
resp = playwright_mcp.handle_request(
{"jsonrpc": "2.0", "id": 8, "method": "tools/list"})
names = [t["name"] for t in resp["result"]["tools"]]
self.assertEqual(len(names), 14)
# main 在栈基线之后新增 4 个浏览器工具(18 个,与
# main 的 playwright_mcp._TOOLS 逐名一致,无重复)。
self.assertEqual(len(names), 18)
for want in ("browser_navigate", "browser_snapshot", "browser_click",
"browser_fill", "browser_evaluate", "browser_click_at",
"browser_live_shot", "browser_snapshot_refs",
@@ -607,7 +609,7 @@ class WebSearchMcpTest(unittest.TestCase):
def test_handle_request_dispatch_and_errors(self):
calls = {}
def fake_search(query, limit=5, provider="auto"):
def fake_search(query, limit=5, provider="auto", scope="general"):
calls["search"] = (query, limit, provider)
return {"results": [{"title": "t", "url": "http://x/"}]}
@@ -673,8 +675,8 @@ class WebSearchMcpTest(unittest.TestCase):
def test_main_loop_with_fake_search(self):
old_search = websearch_mcp.search
websearch_mcp.search = lambda query, limit=5, provider="auto": \
{"results": []}
websearch_mcp.search = lambda query, limit=5, provider="auto", \
scope="general": {"results": []}
old_in, old_out = sys.stdin, sys.stdout
sys.stdin = io.StringIO("\nbroken{\n" + json.dumps(
{"jsonrpc": "2.0", "id": 1, "method": "tools/call",
+12 -2
View File
@@ -789,13 +789,23 @@ class PlaywrightEnsureTest(unittest.TestCase):
self.home, script_dir=self.src)
self.assertFalse(out["script_changed"])
self.assertFalse(out["config_changed"])
# 旧脚本被刷新上报。
# 上游改版刷新:源脚本升级(v1→v2)后,manifest 已证明
# 家目录旧脚本未改动,自动刷新并上报。
with open(os.path.join(self.src,
"playwright_mcp.py"), "w") as f:
f.write("# playwright_mcp.py v2\n")
out = pw_mod.ensure_codex_home_playwright(
self.home, script_dir=self.src)
self.assertIn("playwright_mcp.py", out["script_refreshed"])
# 未知改动不刷新:家目录脚本被改成 manifest 与已知
# 已发版哈希都不认识的内容时,只上报跳过、绝不覆盖。
with open(os.path.join(self.home,
"playwright_mcp.py"), "w") as f:
f.write("# stale\n")
out = pw_mod.ensure_codex_home_playwright(
self.home, script_dir=self.src)
self.assertIn("playwright_mcp.py", out["script_refreshed"])
self.assertNotIn("playwright_mcp.py", out["script_refreshed"])
self.assertIn("playwright_mcp.py", out["script_skipped_modified"])
# 自定义 command_args 写进 section(删旧配置重写,
# 否则已有 section 不会被改写)。
os.remove(os.path.join(self.home, "config.toml"))
+1 -1
View File
@@ -68,7 +68,7 @@ class WebSearchMcpTest(unittest.TestCase):
[t["name"] for t in listed["result"]["tools"]])
def test_tools_call_and_errors(self):
fake_search = lambda q, limit=5, provider="auto": { # noqa: E731
fake_search = lambda q, limit=5, provider="auto", scope="general": { # noqa: E731
"results": [{"title": q}]}
# 工具执行一律用注入的 fake,不调真实 search(不联网)。
resp = ws_handle({"jsonrpc": "2.0", "id": 2, "method": "tools/call",
+1
View File
@@ -58,6 +58,7 @@ import html as _html
import ipaddress
import json
import os
import socket
import urllib.error
import urllib.parse
import urllib.request