f8e02290f4
0006 定「叫什么、什么形状」:五个接缝的 Protocol 名与签名、公共类型的英文名与 字段清单、类型分到 types / ports / tools 三个模块的判据。 0007 定「同一个签名下什么算对」:三个动作状态的触发条件、动作被拒绝时观察由库 合成而不取执行器那段、解释器不许抛异常、read_log 读不存在的运行返回空日志。 两份拆开是因为后者的权威处按 §0 是 tests/contract/,design doc 只记当初为什么这么定。 这两份改动了 0003 四处,全部在文首登记:记录集合是六种东西不是五类; 参数视图是方法不是字段;预算是四项不是两个计数;ports 装「Protocol 与它们的 入参/返回结构体」那半句写不出来——照它写 types 会反向依赖 ports。 四处全是「把字段类型逐个写出来」这个动作本身逼出来的,纯读文档看不见。 四轮评审:两轮硕士生冷读报了约 45 条,两轮 Codex 对抗审查报了 13 条, 逐条核实后基本全部成立并修完。最后一轮是唯一一次契约测试与文档互相抓到对方的错—— 文档改了方法名测试没跟,测试把 dissect 的动作语言写死成输入会误杀 GovDoc 的实现。 结论回写 architecture.md:第七节补类型归属判据,第八节改 ports 那一行, 第九节补五个 Protocol 的英文名,第十四节把「英文名还没定」那条缺口换成指向; 决策索引加两行。字段表刻意不回写——按 §0 那是代码的权威。 CLAUDE.md 与 README.md 开头的「一次 Agent Session」是术语漂移,改成「一次运行」。 CLAUDE.md §7 加两条工作方式:能压成一段结论的活尽量交给 subagent、 委托出去的活交证据不交判断;以及持续往下做,只在人类门和真判断不了的岔路停。 §8 那句「讲完停下来等回应」与后者打架,收窄到只管说话方式。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
60 lines
3.0 KiB
Python
60 lines
3.0 KiB
Python
"""事件出口的行为契约。
|
|
|
|
两个已知形态差别在可靠性要求上:一个把进度逐步回写业务数据库供前端轮询(要求低延迟、
|
|
可以丢),一个把审计事件送进日志管道(要求不丢、可以慢)。
|
|
|
|
## 写这份文件时撞出来的、`design/0006` 还答不上的问题
|
|
|
|
`Event` 只有一个名字,没有字段,所以这个接缝的契约现在只能验「投递失败不打断循环」这一半,
|
|
验不了「发出去的事件里有什么」。
|
|
"""
|
|
|
|
import pytest
|
|
|
|
pytestmark = pytest.mark.contract
|
|
|
|
|
|
async def test_emit_accepts_an_event(event_sink, records):
|
|
"""能收下一个事件,正常路径不抛异常。"""
|
|
await event_sink.emit(records.event())
|
|
|
|
|
|
def test_a_sink_is_allowed_to_raise_on_delivery_failure():
|
|
"""**这一层不断言「emit 不抛」——一个后端连不上时抛异常的出口是合规实现。**
|
|
|
|
契约写的是「投递失败由**库**捕获、记日志、把失败计数加一,然后继续跑」,所以要断言的
|
|
行为在库那一侧,不在出口这一侧。原来这里写了一条 `await emit(...)` 不抛的断言,那会把
|
|
一个完全合法的审计 sink 判失败——它在日志管道不可用时抛 `ConnectionError`,而库本来就
|
|
该接住。
|
|
|
|
「库接住了失败并继续跑」属于整次运行的行为,落在驱动入口那一层的测试里,不在这个接缝的
|
|
契约里。这条留成一个说明,是为了让下一个想在这儿加断言的人先看到这段。
|
|
"""
|
|
|
|
|
|
@pytest.mark.xfail(reason="事件集还没定,见 docstring", strict=True)
|
|
def test_failure_is_not_re_emitted_through_the_same_sink():
|
|
"""投递失败不再转成一条事件从同一个出口发出去。
|
|
|
|
那会自我喂食:一个持续失败的出口会让失败处理路径变成递归,而递归的表现是进程卡住或
|
|
栈溢出,不是一条错误日志。
|
|
|
|
**这条现在验不了**,因为验它要求能识别「这是一条失败事件」,而 `Event` 还没有字段——
|
|
`design/0006` 里它只有一个名字。方向已经定了(观察走事件流、干预走具名回调),但事件
|
|
集与回调清单要独立成一份 design doc,这条要等到那时候。
|
|
"""
|
|
pytest.fail("Event 还没有字段,识别不了「失败事件」")
|
|
|
|
|
|
@pytest.mark.xfail(reason="事件集还没定,见 docstring", strict=True)
|
|
def test_audit_events_carry_both_raw_and_repaired_model_output():
|
|
"""审计事件要同时带模型原文与修复之后的结果。
|
|
|
|
GovDoc 有一条硬纪律:agent 的原始输出、修复后的输出、恢复来源全程留痕,禁止静默修复。
|
|
它现有的审计出口是一个「发一条带类型和载荷的事件」的接口,迁移之后这条纪律要由事件流
|
|
承载——能不能承载,取决于事件里带不带这两样。
|
|
|
|
这是 `../research-wiki/migrations/govdoc-saas.md` 缺口登记里那一条,同样等事件集定下来。
|
|
"""
|
|
pytest.fail("Event 还没有字段,承载不了审计纪律")
|