Files
AirPlan-V2/lib/air_runtime/todo_parser.py
AirLongDian e73a4da354 feat: P1-19 P1-20 实现 - 边界测试强制 + 高风险审计 + UI Skill 路由
P1-19.1: Arc 边界测试强制
- TaskNode 新增 test_required 字段
- _inject_boundary_tests() 为每个模块注入接口测试和单元测试任务
- Done When 验证必须包含"测试通过"

P1-19.2: AirRvr 高风险审计
- 新增 HighRiskAudit, HighRiskFinding 数据类
- ReviewReport 新增 highRiskAudit 字段,含 lifecycle/nullPointer/danglingPointer/exceptionSafety/concurrency + overallRisk + deliveryVerdict
- 序列化/反序列化支持

P1-19.3: block-release 集成
- dispatch_worker_group() 派发前扫描最新审查报告
- deliveryVerdict=block-release 时阻止所有后续派发
- 记录 eng.blocked 事件

P1-20: frontend-design Skill 集成
- is_ui_task() UI 任务检测
- ensure_frontend_design_skill() 自动安装 Skill
- route_ui_task() UI 任务路由决策
- enter_worker() 集成 UI 检测,skill 不可用时阻止执行
- commands/do.md 更新 UI 处理说明
- SKILL.md 新增 INV-12/INV-13/INV-14

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 17:06:48 +08:00

96 lines
3.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
TODO 解析器 — V2 修复 P1-8列索引从表头推导不再硬编码 cells[1]/cells[6]/cells[7]。
"""
from __future__ import annotations
import re
from dataclasses import dataclass
from pathlib import Path
@dataclass
class TodoTask:
task_id: str
task: str
files_dirs: str = ""
status: str = "TODO"
done_when: str = ""
validations: str = ""
adr: str = ""
def parse_tasks(todo_path: Path) -> list[TodoTask]:
"""从 todo.md 解析任务列表,动态检测列索引。"""
if not todo_path.exists():
return []
content = todo_path.read_text(encoding="utf-8")
lines = [l.strip() for l in content.splitlines() if l.strip()]
# 找到 Markdown 表格头
header_idx = -1
for i, line in enumerate(lines):
if line.startswith("|") and "Task" in line and "Status" in line:
header_idx = i
break
if header_idx < 0:
return []
# 解析列名
header_line = lines[header_idx]
header_cols = [c.strip() for c in header_line.split("|") if c.strip()]
# 建立列名 → 索引映射
col_map = {}
for idx, col_name in enumerate(header_cols):
col_name_lower = col_name.lower()
if "task" in col_name_lower:
col_map["task"] = idx
elif "status" in col_name_lower:
col_map["status"] = idx
elif "files" in col_name_lower or "dir" in col_name_lower:
col_map["files_dirs"] = idx
elif "done" in col_name_lower or "when" in col_name_lower:
col_map["done_when"] = idx
elif "valid" in col_name_lower:
col_map["validations"] = idx
elif "adr" in col_name_lower:
col_map["adr"] = idx
# 跳过表头和分隔符
tasks = []
for line in lines[header_idx + 2:]:
if not line.startswith("|"):
continue
cells = [c.strip() for c in line.split("|") if len(c.strip()) > 0]
if not cells:
continue
task_cell = cells[col_map.get("task", 0)] if col_map.get("task", 0) < len(cells) else ""
# 提取 [T-xxx] 格式的任务 ID
tid_match = re.match(r"\[([A-Za-z0-9_\-\.]+)\]", task_cell)
task_id = tid_match.group(1) if tid_match else task_cell
task = task_cell
status = cells[col_map.get("status", 1)] if col_map.get("status", 1) < len(cells) else "TODO"
files_dirs = cells[col_map.get("files_dirs", 2)] if col_map.get("files_dirs", 2) < len(cells) else ""
done_when = cells[col_map.get("done_when", 3)] if col_map.get("done_when", 3) < len(cells) else ""
validations = cells[col_map.get("validations", 4)] if col_map.get("validations", 4) < len(cells) else ""
adr = cells[col_map.get("adr", 5)] if col_map.get("adr", 5) < len(cells) else ""
# 清理标记
task_id = re.sub(r"^\[|\]$", "", task_id).strip()
if task_id and task_id != "---":
tasks.append(TodoTask(
task_id=task_id,
task=task,
files_dirs=files_dirs,
status=status.upper() if status else "TODO",
done_when=done_when,
validations=validations,
adr=adr,
))
return tasks