- 断点已证 = **时序层**:graph_update 只走实时帧从不重放、flowGraphs 不持久化、补拉通路此前根本不存在(EndRound 注释承诺过「刷新能重放」,代码里从来没有)
铁证:15:16:38 页面重载抹掉 15:09 那轮图,而下一轮心跳要等 ~15:24 ⇒ 打开后台思考页时桶必然为空
- 新增只读端点 GET /api/v1/admin/flow/graph?sessionId=(owner|admin;省略=主会话;graph 与 graph_update 帧的 data 逐字同形)
无图 ⇒ 200 + graph:null(不画假图);图源未接线 ⇒ 500 flow_unavailable(不伪装成「没有图」);不审计、不发布
- loop.FlowPublisher.LastGraph:只读快照(不发布/不改图/不动轮次计数),FlowGraph.Clone 深拷贝避免锁外编码撕裂
- 前端 useFlowSnapshot:只在「本会话还没有图」时补拉(避免每行一个请求);Flow/Agents/Backthink 三处接入 + 来源说明条(明确「最近一轮」与「多轮历史回放尚未实现」)
- 变异实测:LastGraph 恒 nil ⇒ 4 条测试红;白名单去掉 sess-backthink ⇒ 对应测试红(首次变异因未使用 import 编译失败 ⇒ 已作废重做)
- ⚠️ 诚实边界:每会话只回**最近一轮**(多轮回放属 F2);白名单外会话(IM 群聊/陌生人)永远没有图;**后端重启后未跑新轮次前端点如实返 null(图是内存资产,不落库)**—— 主人要的「本地数据」正是 F2 要补的
41 lines
1.6 KiB
Go
41 lines
1.6 KiB
Go
package main
|
||
|
||
import "testing"
|
||
|
||
// 链路图发布会话白名单是**裁决本身**(design/19 §1 裁决 01.4.5 + 裁决 16.4.2),
|
||
// 不是实现细节:它决定哪些会话能产出实时帧,也决定契约 v0.71 的只读快照出口
|
||
// (`GET /api/v1/admin/flow/graph`)能查到什么。删掉任何一档都必须打红。
|
||
//
|
||
// 这条测试同时钉住「隔离的渠道会话绝不进白名单」—— 群聊 / 陌生人会话一旦被
|
||
// 发进管理面板,就是把主人的私聊以外的内容泄漏给面板,属红线。
|
||
func TestFlowSessionAllowed(t *testing.T) {
|
||
t.Parallel()
|
||
allowed := []string{
|
||
"web",
|
||
"web:0f3a0b1c-2d3e-4f50-8a9b-1c2d3e4f5061",
|
||
"web:another-uuid",
|
||
"sess-backthink",
|
||
}
|
||
for _, session := range allowed {
|
||
if !flowSessionAllowed(session) {
|
||
t.Errorf("flowSessionAllowed(%q) = false, want true(该档属 design/19 §1 的发布会话范围)", session)
|
||
}
|
||
}
|
||
|
||
denied := []string{
|
||
"",
|
||
"webbish", // 与 "web" 前缀相似但不是主会话族
|
||
"webx:uuid", // 前缀不匹配必须整档拒绝(HasPrefix 只认 "web:")
|
||
"sess-im-12345", // IM 隔离会话(群聊)
|
||
"sess-stranger-999", // 陌生人私聊
|
||
"sess-memorytidy", // 系统任务会话(design/23,刻意不在白名单)
|
||
"sess-backthink-2", // 后缀不同即不是那个固定会话
|
||
"sess-backthink ", // 尾部空格不得被宽容
|
||
}
|
||
for _, session := range denied {
|
||
if flowSessionAllowed(session) {
|
||
t.Errorf("flowSessionAllowed(%q) = true,但它必须被拒(隔离会话/系统任务/非法 id 一律不进白名单)", session)
|
||
}
|
||
}
|
||
}
|