Release v1.0.108: precipitate user-facing copy contract
All checks were successful
技能自动化发布 / release (push) Successful in 8s

This commit is contained in:
2026-09-12 14:08:33 +08:00
parent c43c26ad15
commit db2ed089a3
11 changed files with 121 additions and 11 deletions

View File

@@ -522,6 +522,59 @@ class TestActionsManifest(unittest.TestCase):
}
jsonschema.validate(instance=manifest, schema=schema)
def test_action_examples_user_facing_copy_avoids_jargon(self) -> None:
"""示例 Action 的用户可见字符串不得含常见开发黑话(属性名本身除外)。"""
examples_dir = os.path.join(get_skill_root(), "assets", "examples")
# 故意拆开,避免本测试源码触发自身扫描。
forbidden = (
"pickCount",
"itemIds",
"status=",
"import-urls",
"download-enqueue",
"bind.selectionMode",
"selectionMode=",
)
# async/pending 作为整词出现在 description 才禁止;executionProfile 值除外。
word_forbidden = re.compile(r"\b(async|pending|sync)\b", re.I)
for name in os.listdir(examples_dir):
if not name.endswith(".action.example.json"):
continue
path = os.path.join(examples_dir, name)
with open(path, encoding="utf-8") as f:
data = json.load(f)
actions = data["actions"] if isinstance(data, dict) and "actions" in data else [data]
for action in actions:
if not isinstance(action, dict):
continue
texts: list[str] = []
for key in ("description",):
if action.get(key):
texts.append(str(action[key]))
conf = action.get("confirmation") or {}
if isinstance(conf, dict) and conf.get("message"):
texts.append(str(conf["message"]))
props = ((action.get("inputSchema") or {}).get("properties")) or {}
for prop in props.values():
if not isinstance(prop, dict):
continue
for key in ("title", "description"):
if prop.get(key):
texts.append(str(prop[key]))
blob = "\n".join(texts)
for token in forbidden:
self.assertNotIn(
token,
blob,
msg=f"{name} user-facing copy must not contain {token!r}",
)
match = word_forbidden.search(blob)
if match is not None:
self.fail(
f"{name} user-facing copy must not contain {match.group(0)!r}"
)
def test_no_execution_profile_placement_coupling_in_tests(self) -> None:
"""守护:本文件不得引入入口位置与 sync/async 的错误硬耦合断言。"""
path = os.path.join(get_skill_root(), "tests", "test_actions_manifest.py")
@@ -686,6 +739,8 @@ class TestActionsManifest(unittest.TestCase):
"labelColumn",
"selectionMode",
"勾选驱动",
"用户可见文案",
"双受众",
"已选择",
"revealInForm",
"映射只读",

View File

@@ -423,10 +423,16 @@ class TestDocsStandards(unittest.TestCase):
self.assertIn("revealInForm", checklist)
self.assertIn("A4-9", checklist)
self.assertIn("selectionMode", checklist)
self.assertIn("A4-10", checklist)
self.assertIn("用户可见文案", checklist)
runtime = self._read("development/SKILL_ACTION_RUNTIME.md")
self.assertIn("visibleWhen", runtime)
self.assertIn("revealInForm", runtime)
self.assertIn("selectionMode", runtime)
self.assertIn("用户可见文案", runtime)
actions = self._read("references/ACTIONS.md")
self.assertIn("用户可见文案", actions)
self.assertIn("双受众", actions)
def test_guarded_docs_do_not_reference_amazon_implementation(self) -> None:
forbidden = (