feat(ack): add Feishu bug intake

This commit is contained in:
2026-08-01 14:36:21 +08:00
parent 5b0e41199d
commit f8d03fad4d
17 changed files with 2059 additions and 7 deletions
+51
View File
@@ -113,6 +113,15 @@ DELIVERY_STATUSES = {
}
DELIVERY_ARTIFACT_FIELDS = {"id", "type", "reference", "digest"}
DELIVERY_DEPLOYMENT_FIELDS = {"environment", "result", "evidence"}
FEISHU_REQUIRED_FIELDS = {
"title", "actual", "expected", "stepsToReproduce", "acceptance", "priority",
"attachments", "updatedAt",
}
FEISHU_CONFIG_FIELDS = {"provider", "profile", "baseToken", "tableId", "viewId", "fields"}
FEISHU_SOURCE_FIELDS = {"kind", "ref", "recordId", "updatedAt"}
FEISHU_PROFILE_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$")
FEISHU_SOURCE_REF_RE = re.compile(r"^feishu-base:sha256:[0-9a-f]{64}$")
FEISHU_RECORD_ID_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{0,255}$")
DISPATCH_FIELDS = {
"taskId",
"dispatchId",
@@ -654,6 +663,28 @@ def validate_builtin(data: dict) -> list[str]:
{"repoPath", "baseUrl", "devWorktree", "overlayFile", "deliveryFile"},
"project",
)
if "bugIntake" in project:
intake = project["bugIntake"]
if not isinstance(intake, dict):
errors.append("project.bugIntake 必须是对象")
else:
reject_unknown_fields(intake, FEISHU_CONFIG_FIELDS, "project.bugIntake", errors)
if intake.get("provider") != "feishu-base":
errors.append("project.bugIntake.provider 必须是 feishu-base")
profile = intake.get("profile")
if not isinstance(profile, str) or FEISHU_PROFILE_RE.fullmatch(profile) is None:
errors.append("project.bugIntake.profile 非法")
for key in ("baseToken", "tableId", "viewId"):
value = intake.get(key)
if not isinstance(value, str) or not value.strip() or any(char.isspace() for char in value):
errors.append(f"project.bugIntake.{key} 必须是无空白非空字符串")
fields = intake.get("fields")
if not isinstance(fields, dict) or set(fields) != FEISHU_REQUIRED_FIELDS:
errors.append("project.bugIntake.fields 必须且只能映射所需逻辑字段")
elif any(not isinstance(v, str) or not v.strip() or any(c.isspace() for c in v) for v in fields.values()):
errors.append("project.bugIntake.fields 字段值必须是无空白非空字符串")
elif len(set(fields.values())) != len(fields):
errors.append("project.bugIntake.fields 字段值不能重复")
if (
"knowledgeFile" in project
and project.get("knowledgeFile") != "docs/ack/knowledge.yaml"
@@ -710,6 +741,7 @@ def validate_builtin(data: dict) -> list[str]:
return errors
seen_ids: set[str] = set()
seen_source_refs: set[str] = set()
for i, task in enumerate(tasks):
where = f"tasks[{i}]"
if not isinstance(task, dict):
@@ -756,6 +788,25 @@ def validate_builtin(data: dict) -> list[str]:
)
validate_object_fields(task, {"evidence", "verification"}, where)
if "source" in task:
source = task["source"]
# `source` was historically an open extension point. Preserve
# non-Feishu strings/objects and tighten only the namespaced shape.
if isinstance(source, dict) and source.get("kind") == "feishu-base":
reject_unknown_fields(source, FEISHU_SOURCE_FIELDS, f"{where}.source", errors)
ref = source.get("ref")
if not isinstance(ref, str) or FEISHU_SOURCE_REF_RE.fullmatch(ref) is None:
errors.append(f"{where}.source.ref: 必须是不透明 feishu-base SHA-256 引用")
else:
if ref in seen_source_refs:
errors.append(f"{where}.source.ref: 来源引用重复")
seen_source_refs.add(ref)
record_id = source.get("recordId")
if not isinstance(record_id, str) or FEISHU_RECORD_ID_RE.fullmatch(record_id) is None:
errors.append(f"{where}.source.recordId: 必须是合法飞书记录 ID")
if not _nonempty_string(source.get("updatedAt")):
errors.append(f"{where}.source.updatedAt: 必须是非空字符串")
validate_knowledge_fields(task, where, status, errors)
if "dispatch" not in task: