feat(session): 落成事件出口,五个接缝全部有调用点
Event 从零字段变成 kind + run_id + model_binding + step,新增 EventKind(只有一种取值, 但第一天就带 kind,逼每个出口分发)。事件在「一步走完」原子落地之后发,只有这次进程里 真的执行过的步才发;投递失败接住、计数进 RunResult、继续跑,CancelledError 原样穿过。 契约套件那两条 xfail 关掉:一条要断言的是库发了几次、接缝自己看不到;另一条的前提是错的 ——审计纪律由意图日志承担不由事件流承担,改成在 unit 层验日志里原文与改写后的文本各有 位置。_project_observation 那段说「将来靠事件流送出去」的注释一并改对。 283 passed / 15 skipped / 2 xfailed,剩下两条 xfail 是原子写与前缀持久性,没有机器兜底。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -15,7 +15,7 @@ from collections.abc import Mapping
|
||||
|
||||
import pytest
|
||||
|
||||
from polyloop.ports import Action, Event, ToolCall
|
||||
from polyloop.ports import Action, Event, EventKind, ToolCall
|
||||
from polyloop.stores import JsonlRunStore
|
||||
from polyloop.types import (
|
||||
ActionOutcome,
|
||||
@@ -155,8 +155,13 @@ class _Records:
|
||||
tool_call=None if tool_name is None else ToolCall(name=tool_name, arguments={}),
|
||||
)
|
||||
|
||||
def event(self) -> Event:
|
||||
return Event()
|
||||
def event(self, *, run_id: str = "run-1", step_idx: int = 0) -> Event:
|
||||
return Event(
|
||||
kind=EventKind.STEP_FINISHED,
|
||||
run_id=run_id,
|
||||
model_binding={"item": "a"},
|
||||
step=self.step(step_idx=step_idx),
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
两个已知形态差别在可靠性要求上:一个把进度逐步回写业务数据库供前端轮询(要求低延迟、
|
||||
可以丢),一个把审计事件送进日志管道(要求不丢、可以慢)。
|
||||
|
||||
## 写这份文件时撞出来的、`design/0006` 还答不上的问题
|
||||
## 写这份文件时撞出来的问题,`design/0013` 答了
|
||||
|
||||
`Event` 只有一个名字,没有字段,所以这个接缝的契约现在只能验「投递失败不打断循环」这一半,
|
||||
验不了「发出去的事件里有什么」。
|
||||
「发出去的事件里有什么」当时验不了,因为 `Event` 只有一个名字没有字段。现在事件集定下来了,
|
||||
而答案把这份文件里的两条测试都挪走了——它们要断言的行为都在库那一侧,不在出口这一侧,见文末
|
||||
那两条说明。
|
||||
"""
|
||||
|
||||
import pytest
|
||||
@@ -32,28 +33,26 @@ def test_a_sink_is_allowed_to_raise_on_delivery_failure():
|
||||
"""
|
||||
|
||||
|
||||
@pytest.mark.xfail(reason="事件集还没定,见 docstring", strict=True)
|
||||
def test_failure_is_not_re_emitted_through_the_same_sink():
|
||||
"""投递失败不再转成一条事件从同一个出口发出去。
|
||||
"""投递失败不再转成一条事件从同一个出口发出去(`design/0013` 决策七)。
|
||||
|
||||
那会自我喂食:一个持续失败的出口会让失败处理路径变成递归,而递归的表现是进程卡住或
|
||||
栈溢出,不是一条错误日志。
|
||||
|
||||
**这条现在验不了**,因为验它要求能识别「这是一条失败事件」,而 `Event` 还没有字段——
|
||||
`design/0006` 里它只有一个名字。方向已经定了(观察走事件流、干预走具名回调),但事件
|
||||
集与回调清单要独立成一份 design doc,这条要等到那时候。
|
||||
**要断言的是库有没有再发一次,那是整次运行的行为**,所以断言在
|
||||
`tests/unit/test_session.py` 里——那边用一个恒抛异常的出口跑完一次运行,验出口收到的
|
||||
条数恰好等于步数。这个接缝自己看不到「库发了几次」。
|
||||
"""
|
||||
pytest.fail("Event 还没有字段,识别不了「失败事件」")
|
||||
|
||||
|
||||
@pytest.mark.xfail(reason="事件集还没定,见 docstring", strict=True)
|
||||
def test_audit_events_carry_both_raw_and_repaired_model_output():
|
||||
"""审计事件要同时带模型原文与修复之后的结果。
|
||||
def test_audit_events_do_not_carry_the_model_output_the_store_does():
|
||||
"""审计纪律由存储承担,不由事件流承担(`design/0013` 决策二)。
|
||||
|
||||
GovDoc 有一条硬纪律:agent 的原始输出、修复后的输出、恢复来源全程留痕,禁止静默修复。
|
||||
它现有的审计出口是一个「发一条带类型和载荷的事件」的接口,迁移之后这条纪律要由事件流
|
||||
承载——能不能承载,取决于事件里带不带这两样。
|
||||
这条测试原来断言「事件要同时带原文与修复后的文本」,而那个前提是错的——事件流可丢,
|
||||
一件只存在于可丢通道里的事实撑不起「禁止静默修复」。
|
||||
|
||||
这是 `../research-wiki/migrations/govdoc-saas.md` 缺口登记里那一条,同样等事件集定下来。
|
||||
两份文本在意图日志里各有位置:原文在模型调用结果记录的回复里,修复后的那份是步记录的
|
||||
`raw_output`。断言落在 `tests/unit/test_session.py`,因为要跑完一次完整运行再把日志读
|
||||
回来,而这个接缝的契约只看得见一个出口实现。
|
||||
"""
|
||||
pytest.fail("Event 还没有字段,承载不了审计纪律")
|
||||
|
||||
Reference in New Issue
Block a user