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>
This commit is contained in:
96
lib/air_runtime/todo_parser.py
Normal file
96
lib/air_runtime/todo_parser.py
Normal file
@@ -0,0 +1,96 @@
|
||||
"""
|
||||
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
|
||||
Reference in New Issue
Block a user