fix: P0/P1/P2 全量修复 — 取消安全 + 异常传播 + 事务回滚 + DSL 语义分析 #18

Merged
shaotao merged 5 commits from fix/dsl-semantic-hotfix into main 2026-06-12 19:49:38 +08:00
Owner

修复一览

P0(元评审共识,3/3)

# 问题 方案
1 并发任务异常静默失败 _task_errors 写入 context,主循环 finally 中检查并 raise
2 compound_stack 取消安全 list → 不可变 tuple,每次修改创建新值
3 broadcast_event 非原子 for 串行 → asyncio.gather 并行 + return_exceptions=True

P1(本迭代,3/3)

# 问题 方案
1 热重载丢失状态 CancelledError 时保存 compound_stack_event_buffer 到 context
2 并行分支浅拷贝陷阱 dict(ctx)deepcopy(ctx)
3 无守卫循环死锁 DSL 验证器 _check_unguarded_loops()

P2(2/3)

# 问题 方案
1 事务回滚 新增 TransactionContext(context + file),15 个测试
2 DSL 语义分析 可达性 BFS + 入度检查,6 个测试
3 TaskGroup 跳过(Python 3.14 限制,while True 协程在 TG 退出时不会自动 cancel)

测试

127 passed, 0 failed, 0 skipped

## 修复一览 ### P0(元评审共识,3/3) | # | 问题 | 方案 | |---|------|------| | 1 | 并发任务异常静默失败 | `_task_errors` 写入 context,主循环 finally 中检查并 raise | | 2 | compound_stack 取消安全 | `list` → 不可变 `tuple`,每次修改创建新值 | | 3 | broadcast_event 非原子 | `for` 串行 → `asyncio.gather` 并行 + `return_exceptions=True` | ### P1(本迭代,3/3) | # | 问题 | 方案 | |---|------|------| | 1 | 热重载丢失状态 | `CancelledError` 时保存 `compound_stack` 和 `_event_buffer` 到 context | | 2 | 并行分支浅拷贝陷阱 | `dict(ctx)` → `deepcopy(ctx)` | | 3 | 无守卫循环死锁 | DSL 验证器 `_check_unguarded_loops()` | ### P2(2/3) | # | 问题 | 方案 | |---|------|------| | 1 | 事务回滚 | 新增 `TransactionContext`(context + file),15 个测试 | | 2 | DSL 语义分析 | 可达性 BFS + 入度检查,6 个测试 | | 3 | TaskGroup | 跳过(Python 3.14 限制,`while True` 协程在 TG 退出时不会自动 cancel) | ### 测试 127 passed, 0 failed, 0 skipped
1. _periodic_runner 异常传播:异常不再被静默吞掉,而是
   写入 context._task_errors,在主循环 finally 块中检查并
   raise RuntimeError,确保后台任务失效不会无声无息。

2. broadcast_event 原子性:从串行 for 循环改为
   asyncio.gather 并行投递,任一 Agent 投递失败不影响其他
   Agent,失败的记录日志而不是中断整个广播。

Closes #P0 (2/3) — compound_stack 取消安全待后续修复
compound_stack 从 list[str] 改为 tuple[str, ...]:
每次修改都创建新 tuple 赋值回原变量,避免跨 await 点
被取消时栈状态不一致。

波及函数:
- _resolve_state_def: 返回 (state_def, new_stack) 元组
- _resolve_escape: 返回 (state, new_stack) 元组
- _update_compound_stack: 返回 new_stack 而非原地修改
- agent_coro 主循环中所有调用点同步更新

Closes #P0 (3/3) — 三轮元评审全部 P0 问题已修复
1. Pipeline 并行分支浅拷贝陷阱(bixiweave/pipeline.py)
   dict(ctx) → deepcopy(ctx),避免嵌套 dict/list 在分支间共享修改。

2. DSL 验证器增加无守卫循环检测(bixiweave/dsl.py)
   检测状态存在指向自身的 transition 且无 guard/on_enter/timeout
   出口的死循环风险。

3. 热重载时恢复 compound_stack 和 _event_buffer(bixiweave/compiler.py)
   - CancelledError 时保存 compound_stack 和 _event_buffer 到 context
   - 状态机正常退出时也保存
   - reload() 通过 preserve_context 自动恢复这些状态
新增 bixiweave/transaction.py:
- TransactionContext 类追踪 context 和文件系统变更
- set() / update() 追踪 context key 的新增和修改
- write_file() 追踪文件写入(支持 dict/str/bytes)
- commit() 确认变更生效,rollback() 恢复变更前状态
- 上下文管理器支持:with 块无异常自动 commit,异常自动 rollback
- 嵌套事务支持
- 跳过不可序列化的运行时对象(llm/mcp/_system)

Agent context 中注入 transaction 工厂:
context["transaction"] = lambda: TransactionContext(self.context)

15 个测试覆盖全部功能。
1. 可达性分析(_analyze_reachability)
   - BFS 从 initial 出发遍历所有 transition
   - 正确处理跳出符号(#)和 compound 子状态
   - compound 状态的所有子状态自动标记为可达
   - 报告不可达的顶层或嵌套子状态

2. 入度检查(_check_in_degree)
   - 统计所有 transition 指向每个状态的次数
   - 入度为 0 且不是 initial 的顶层状态报告为孤立
   - compound 子状态豁免(通过父状态隐含可达)

6 个新测试覆盖全部场景。
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
bixiu/bixiweave!18
No description provided.