Commit Graph

859 Commits

Author SHA1 Message Date
ThearchyHelios 1195c4abcb 账本前端补两样:答案身上的召回标记写到问数据的轮次和证据面板「口径」一步;对账结果卡上问「说明有用 / 看不懂」
复核 W4R-03(a×c):召回在原答案的运行上追加 ledger.recalled、ledger.basis_changed,以前只在运行时间线(decode)里,问数据的
轮次底部、证据面板「口径」一步、账本接口都不带——使用者被标了「依据有变」、或者口径升版后「已按 v2 核对,不影响」,重开对话
什么也看不到,得点开「过程」才翻得出来,而使用者进不了记录页。W4-a 已在账本接口加上 recall(ledger/recall.answer_marks:
checked / basis / tasks,几种 state 都带),这边照它画:

- typesLedger:RunLedger.recall(LedgerRecallMarks)
- run/LedgerStep 新增 RecallMarks(问数据的轮次和「口径」一步共用):「已按口径「X」vN 核对,不影响」(同一份口径核对过
  几次只写最近那一版,数变了的不在这里写);依据有变按两类各一行——数据目录(catalog / values / catalog_edit)和表结构
  (structure),认不出的种类只说「答案的依据有变」不猜,项数相加,按新目录新出现错误级问题的写几条;看的人手上有说的是
  这个答案的召回待办时写一句(口径升到 vN、数变了 / 依据有变检查出新问题)并链到 /apps/inbox/{待办}
- LedgerTurn:比对过的卡里写在摘要下面;没比对、不比对的答案(state 不是 matched)只要带着标记就单独写一张小卡
- LedgerPart(证据面板「口径」一步):有标签的写在脚注前;没标签但答案带着标记的,这一步只写标记

复核 W4R-10(多包):细案 02 做法 10 的 E2「说明有用 / 看不懂」以前没有入口。W4-a 已加 POST /ledger/answers/{run}/reconcile/
{对账运行}/feedback({verdict: useful | unclear},记成对账运行上的封存后事件、统计 E2 数它),账本接口的 reconciled 每一项
带 feedback {mine, useful, unclear}。这边:

- api.caliberLedger.feedback;typesLedger 加 LedgerFeedback、LedgerFeedbackOut
- run/LedgerReconcile 的对账卡:做完的卡问一句「「差在哪」的说明有帮助吗?」,两个按钮,看的人选过的按下(aria-pressed),
  写各几个人(服务端给的数),可以改主意;交回来的反馈先画上,账本再取一次让别处挂着的同一张卡跟着变;回收站里只看不反馈;
  没做完的卡不问
- 问数据的轮次里卡片上方没有「差在哪」,卡里补写一份(showWhere),反馈问的就是它;证据面板里它本来就在卡片上方,不重复

检查(写请求都在浏览器层拦下):
- check-evidence 新一段 ledger-recall:比对过的答案「口径」一步写核对过(只写最近那一版)、依据有变(项数相加、新问题条数)、
  链到待办;不比对的答案这一步只写依据有变
- check-chat 新一段 recall:轮次底部同上,另核对做完的对账卡写「差在哪」和反馈人数;不比对的答案单独一张小卡写表结构有变
- check-ledger 对账一段:没做完不问;点「看不懂」请求体只有 verdict、按下、写各几个人;改点「说明有用」再记一条、按下的换过来
- 共用夹具 ledger-fixtures 加 RECALL
把这几个文件的改动拿掉回跑:check-chat recall 6 项、check-evidence ledger-recall 4 项、check-ledger 对账反馈 2 项都不过;
放回去全过
2026-10-16 22:21:11 +08:00
ThearchyHelios f72e1d4a65 问数据、画布右栏、运行详情的步骤时间线:被驳回的那一步画停止的剪影、行尾写「被驳回」,不再是绿色的完成勾
复核 W4R-02:decode 已经给被驳回的那一步(node.failed 带 reason=rejected)记 status=rejected,但三处共用的步骤时间线
run/AssistantStream 不认它:BADGED 里没有 rejected,徽标落到「顶层节点画完成」那一支,被驳回的那一步成了一个绿勾;字色是
正文色,行尾 STOPPED 也没有这一项——副标题写着「张工 驳回:金额不对」,图标却说做完了。细案 5.3 要的是驳回之后各处都写
「被驳回」。

- BADGED 加 rejected:画 lib/status 里 rejected 那一套(停止的剪影、取消的中性色,和画布节点卡同一个)
- 这一行用次一级的中性字色(比取消、跳过的淡色重一档:运行就停在这一步),副标题(谁驳回、备注)用正体、不再是淡色斜体
- 行尾:STOPPED 加 rejected。被驳回的那一步带着耗时(node.failed 的 duration_ms),只写耗时的话停止的方块和取消分不开,
  写成「被驳回 · 900 ms」
- 长列表折起来时照样露出被驳回的那一行(notable 认 rejected):运行停在这一步,不能收进「展开前面的 N 条」
- check-chat「被驳回」一段:run-rej2 的事件换成「查数」那一步 node.failed 带 reason=rejected,点开执行过程核对这一行的
  状态、剪影(stop,不是 square)、图标不是完成的绿色、副标题和行尾写「被驳回」、不写「失败」。改之前这三项不过(徽标是
  done、颜色是 --st-done 的绿),改之后深浅两套都过
2026-10-16 22:18:44 +08:00
ThearchyHelios 9886e10be2 账本接口:刚跑完未封存答 pending 只限结束后两分钟内,早就结束、一直没封存的照实答「运行还没有封存」
复核 W4R-08:上一次给 GET /api/runs/{id}/ledger 加的 pending 分支没有时间界限——成功结束、要比对、没封存的运行一律答
pending。它本来只为 runner 落库 run.finished 之后、封存之前的那一下(页面收到 run.finished 立刻来取)。封存出了错、或者是
封存这件事之前的老数据,会永远答 pending:每个轮次底部、证据面板都转两分钟「正在比对口径」,再取一次还是 pending;改之前
答的 ineligible「运行还没有封存,不比对」才是实情。

- ledger/view.py:新增 SEAL_GRACE_S = 120(和页面等比对结论的上限 store/ledger.ts 的两分钟同一个量级),_just_finished 看
  finished_at 在不在这段时间里(没记结束时间的不算;库里读回 naive 的按 UTC 算,同 db/base.UTCDateTime)。只有刚结束的才答
  pending,其余照旧走 ineligible(NOT_SEALED);_stamp 里的局部 datetime 导入挪到模块顶上
- 测试:刚跑完的那条补上 finished_at=现在;新增一条结束已久、一直没封存的和一条没记结束时间的,都答 ineligible、原因是
  没封存、不排按需比对。去掉 _just_finished 回跑,新测试失败(答的是 pending)
2026-10-16 22:13:52 +08:00
ThearchyHelios 9ad6c035d0 口径账本的界面检查:check-ledger 换成真检查,check-evidence、check-chat 各加一段
都用 page.route 假接口加夹具(scripts/ledger-fixtures.mjs:GET /api/runs/{id}/ledger 的形状照服务端 run_view,挂在可点击
证据的夹具 evidence-doc.json 上,只用通用名),写请求在浏览器层拦下、核对请求体,认不出的写请求一律记账、结束时必须为空。

- check-ledger(骨架里的占位换掉):
  · 比对还没出结果:「口径」一步写「正在比对口径」、按退避再取(间隔越来越长),等满了照实说、给「再取一次」、不再取,
    点了重新取、比对过了就画出来,之后不再取(store/ledger 在开发构建里挂 window.__ledger,把等待起点挪到两分钟前);
  · 三种情况:来自口径(口径、级别、怎么认的、参数、事后补比的说明)、相关口径(差在哪 + 看差多少)、这次的推理;
  · 对账:参数要核对时表单直接打开、照入口字段画 → 发起带核对过的参数(空着的不送)→ 409 带回的选项画「选择要对照的
    指标」、一组没选不发起 → 选好再发起带上对照 → 结果卡片先写进行中、再取一次后画出按你的问法 / 按口径 / 差值 / 相对差,
    点差值打开出处链;
  · 可沉淀的问题:工作流列表弹窗里的抽屉,三类来源分开写、各写怎么认出来的;聚类只按那一轮固化;「不是口径」不写理由
    不能确认、写了才记下并拿掉这一项;固化走 from-run 的 freeze(带同组的别的运行和来源轮次)、打开画布里的草稿;
  · 使用者:不能出具的照服务端的原因写「请搭建者出具」、不摆按钮,对账照样能用,出具过的正式运行不给链接、写由搭建者查看;
  · 设置里口径账本一组:显示生效的值,不合规的值写范围、不让存,保存条写改了哪几项,整组写 ledger。
- check-evidence「口径账本」一节:来自口径的数字挂「受管 / 已发布」小标记(紧跟数字、悬停写口径和认法、读屏念在数字上),
  相关口径、这次的推理、没比对的不挂;面板「口径」一步在出处链最上面,四种标签各写各的,不是数的片段不画;同一次运行只取
  一次账本。别的节的夹具一律答不比对,不落到真服务端。
- check-chat「口径账本」一节:交付了的轮次底部先写「正在比对口径」,按退避再取到比对出结果后写摘要、比完不再取;按口径正式
  出具、对账按钮照服务端的动作摆,出具的请求只说按哪份口径,出具过的正式运行挂在轮次下面(档位、打开运行);对账请求带
  哪条查询、哪份口径、核对过的参数。
2026-10-16 20:34:21 +08:00
ThearchyHelios b24aabefcf 账本前端:证据「口径」一步、数字上的小标记、问数据轮次摘要与出具 / 对账、可沉淀的问题、设置里的口径账本一组
第 2、3 波只有接口,这一波接上界面(docs/design/next-round/02-口径账本.md 做法 3–6、8 的界面、做法 11;总方案 X3、X10)。
界面层不放逻辑:标签、整题判断、能不能出具、能不能自动、不能的原因、要不要先核对参数都照 GET /api/runs/{id}/ledger 给的画。

- 比对结果不靠实时流(第 2 波遗留):ledger.matched 在封存之后追加、事件总线已经关了,打开着的页面收不到。store/ledger
  按退避轮询(1、2、3、5、8、13 秒,之后每 15 秒),服务端答 pending 时写「正在比对口径」,等满 2 分钟还没结论照实说、
  给「再取一次」;要自动出具的再等出具的那一次出现,出具、对账的运行没结束的等它们结束。只在有组件看着时轮询,同一次
  运行的正文、面板、轮次摘要共用一份
- 证据正文(EvidenceDoc):来自口径的数字后面挂一个小标记,写明受管 / 已发布,悬停写是哪一份口径、怎么认的;相关口径的
  数不加标记
- 证据面板出处链最上面加「口径」一步(run/LedgerStep):来自口径——「X」v3(受管)· 参数 · 等价已证明 / 助手选用 ·
  按口径正式出具;相关口径——差在哪 + 看差多少;这次的推理——没有相关的已发布口径;没比对的写原因;脚注写「封存后比对,
  不在封存范围内」,事后补比的写按哪种口径回看
- 口径对账(run/LedgerReconcile):核对参数(照动作里的入口字段画,推断的参数要先核对,遮罩值不回填)→ 发起(认不出列
  怎么配时照 409 带回的选项选对照的指标)→ 结果卡片:每组按你的问法、按口径、差值(附相对差)各自点得开出处链
  (apps/EvidenceDrawer,同应用页点数)
- 问数据轮次底部(run/LedgerTurn):一行摘要(几个数来自口径「X」vN、几个是这次的推理、几个没有比对)、整题等价时按口径
  正式出具(能自动的写正在自动出具;不能的照写原因,使用者没有应用授权写「请搭建者出具」)、有相关口径时对账;出具过的
  正式运行挂在下面,档位写完整出具 / 降档出具 / 不予出具,搭建者点开去运行;回收站里的对话只看不发起
- 可沉淀的问题(pages/ledger/CertifyQueue):工作流列表弹窗里的抽屉,确定性重复、问题聚类、被反复用的答案分开写;
  「固化成口径草稿」走 /copilot/from-run 的 freeze 并打开画布,聚类没有共同的 SQL、按人挑的那一轮固化;「不是口径」
  要写理由
- 设置(pages/settings/LedgerSettings,照 VerifySettings):自动按口径正式出具、每日上限、比对时限、升版后每次最多重算几组,
  和其余偏好共用保存条、整组写 ledger
- 类型 typesLedger.ts、接口 api/caliberLedger.ts(挂 api.caliberLedger)、文案 lib/termsLedger.ts;types.ts 的证据片段
  答复加 ledger、rendered 两个键
2026-10-16 20:02:08 +08:00
ThearchyHelios a43224fd9a 账本接口补几样界面要画的东西:对账的入口字段、配列选项、对账卡的分组,刚跑完未封存时答 pending
账本前端(第 4 波 W4-c)照「界面层不放逻辑」只照服务端给的画,这几样以前接口不给,界面只能自己猜:

- 「看差多少」那一项(reconcile.actions_of)带上口径的入口字段 fields(名字、必填、有没有缺省值,比对工件里记的那一份):
  核对参数的表单照它画,缺的参数也写得出是哪一项
- 认不出列怎么配(409 ledger_reconcile_needs_pairs)另带 options:这次结果里是数、没遮罩的列,口径卡上的指标(id、名字、
  单位);ReconcileRefused 加 extra,api/ledger 的对账接口把它并进返回体(CodedHTTPException 的 extra,改一行)
- 对过的账(reconciled_view)带 card 和 groups:每一组对照的这次 / 口径 / 差值 / 相对差各是哪个指标、这一组怎么称呼
  (从对账运行的图里读,不按命名规则拼),界面按 /evidence/metrics/{card}/{指标} 取值、三个数各自点得开
- GET /api/runs/{id}/ledger:runner 先发 run.finished、落库,再封存、再排比对,页面收到 run.finished 立刻来取时
  还没封存。成功结束、要比对的运行这时答 pending(不另排按需比对),不再说成「运行还没有封存,不比对」
- 测试:对账的三样(单行、多行),刚跑完未封存答 pending、不排按需比对,失败的照旧 ineligible
2026-10-16 19:46:03 +08:00
ThearchyHelios 4c7c0cf283 驳回节点的画法(前端):被驳回的那一步画成「被驳回」,不画成失败
服务端给被驳回的那一步的 node.failed 带上了 reason=rejected(上一个提交)。界面上读 node.failed 的几处都认它
(05-已知遗留 (a)、总方案 X9:驳回是人的决定,不是故障),没有 reason 的老运行照旧画失败:

- run/decode:问数据过程、记录页、画布右栏的时间线里,这一步状态 rejected(StepStatus 加一种),副标题写谁驳回、
  备注(REJECTED_TEXT.step),原话放展开里;不是 error 级别,不当成出错
- run/trace、run/derive:NodeState 加 rejected;那一段收成 rejected,不记成「失败的节点」,下游是「未到达」不是
  「被阻断」;回放投影同样是 rejected
- 画布节点卡:rejected 一种读数(耗时 + 原话第一句,整句悬停),中性色、停止剪影,不挂失败红框、不抖动;小地图、
  运行胶囊的计数、时间轴泳道认得它;记录页航迹的节点卡写「被驳回:原话」
- check-decode、check-run-states 各加一段:新旧两种形状
2026-10-16 19:42:11 +08:00
ThearchyHelios eb578cad34 驳回节点的画法(后端):被驳回的那一步的 node.failed 带 reason=rejected
第 1 波把人的驳回收成 cancelled + end_reason=rejected,但节点层仍只发一条和故障一模一样的 node.failed:
复核规则把它说成「执行失败」、还当成「重跑一次大概率能好」(重跑只会把同一件事再问一遍人),实例进度里
没开待办的驳回(工具调用审批、代码执行审批)落成「失败」(05-已知遗留 (a)、总方案 X9)。

- engine/compiler:NodeRejected 时照旧发 node.failed(时间线、实例进度按它收尾这一步),数据里多带
  reason=rejected(和 run.cancelled 同一个字段名)、kind、decision、actor、note;真的出错的 node.failed
  一字不变,没有这几个键
- engine/review:认 reason=rejected,记 node_rejected(DEGRADED,不进 RETRYABLE),说「被驳回」不说「执行失败」;
  没有 reason 的老运行照旧 node_failed
- apps/resolve 的实例进度:按 node.failed 上的 reason 画「被驳回」(结论写已驳回 / 超时驳回 / 未确认(被驳回)),
  老运行照旧按待办认
- 测试:六种关卡的 node.failed 都带上这几个键;普通失败没有;复核与实例进度的新旧两种形状(去掉修复回跑会失败)
2026-10-16 19:30:47 +08:00
ThearchyHelios 45fab21072 第 4 波契约骨架:频率选择抽成公共组件 components/FreqPicker.tsx(频率与时区的纯函数搬到 lib/freq.ts,pages/schedules/model.ts 原样再导出),定时维护和定时任务共用;前端类型加 ScheduleTargetCreate(建评测题集、数据源维护这两种非工作流目标),api.schedules.create 收它;设置 ledger 一组加 recall_limit(口径升版后每次最多重算几组,缺省 50,0 表示不自动重算);test_notify_events 的 PUSHED_THIS_ROUND 拆成一项一行、给 source.changed、ledger.recall 各留一个位置;check-all 预登记 catalog-changes、evals、ledger 三项检查(先放占位) 2026-10-16 19:09:47 +08:00
ThearchyHelios dd052f6d21 合入下一轮第 3 波(docs/design/next-round):表结构变化与取值观察(结构版本与前后差异、影响面、正式运行的 schema.drift 提醒、剖析引擎的四类取值观察与同口径比较、撤销「已列全」、变化记录与处理、数据源定时维护);口径账本闭环(caliber_from.nodes、问数据的 use_caliber 选用已发布口径、按口径正式出具(自动只给问数据的答案)与口径对账、认证队列与固化成口径草稿、发布后补比);评测执行(后台评测与无头作答、评分与重评、统计、对比与 A/B、预算与止损、清理、定时评测与通知);触达与分享(邮件渠道、每天的待办汇总、访问密钥到期提醒、分享链接与 /s 页、「最近一次封存」按来源白名单认)。验收:后端全量 14210 通过;tsc、文案检查;本波相关的 15 项浏览器检查 4431 项全过;四种库的方言容器测试全过;泄露扫描干净 2026-10-16 19:00:49 +08:00
ThearchyHelios 337513b83d check-publish:「版本列表标出已退役的版本」等列表取回再数(并行时打开那一刻列表还是空的,偶发不过) 2026-10-16 18:59:50 +08:00
ThearchyHelios 728d0c5d2a README:第 3 波的升级说明(结构变化与取值观察、定时维护、按口径自动出具、最近一次封存的来源白名单、使用者的原始运行记录、邮件与待办汇总、密钥到期提醒);应用一节加「分享链接」(开关、两种模式、/s#key、到期与撤销、嵌入、留痕) 2026-10-16 18:51:02 +08:00
ThearchyHelios eb6e3c7ab7 合入 W3-d 邮件、待办汇总、分享链接:邮件渠道(SMTP 可选、口令加密、退信不追踪);每天的待办汇总与访问密钥到期提醒;分享链接(只存哈希、必设到期、只显示定时出数的视图、/s 页不进外壳、下载报告);「最近一次封存」按来源白名单认;使用者拿不到邮件渠道的收件地址;同站门户嵌整站的配置说明 2026-10-16 18:48:07 +08:00
ThearchyHelios 1c8ca4c3d7 合入 W3-c 评测执行:后台评测与无头作答、六种期望五种结论的评分、重评、结论裁判记评测账、统计(Wilson 区间、按角色用量)、对比与 A/B(McNemar)、目录贡献预设、预算与止损、清理;定时任务接题集目标,定时评测有退步 / 没有评完推全局渠道;定时页照实显示非工作流目标 2026-10-16 18:46:44 +08:00
ThearchyHelios defdb28e3f 合入 W3-b 口径账本闭环:caliber_from.nodes 改名映射;问数据的助手 use_caliber 选用已发布口径(服务端确定性内联实例化);按口径正式出具(自动只给问数据的答案)与口径对账;认证队列、固化成口径草稿、发布后补比;账本统计补上对账与认证转化;使用者的原始运行记录只给问数据的运行 2026-10-16 18:45:45 +08:00
ThearchyHelios 9da3e518d7 合入 W3-a 表结构变化与取值观察(next-round 03 B1–B4):结构前后差异、影响面、结构版本与 schema.drift;剖析引擎的四类取值观察、比较器与撤销「已列全」;关联覆盖率下降;变化记录的接口与处理;数据源定时维护;容器集成测试扩到对象索引和取值观察;观察写库出错各占保存点、不拖垮剖析;离线核对页认 schema.drift 2026-10-16 18:45:45 +08:00
ThearchyHelios 695f63f7ba W3-a 可验证报告离线核对页认 schema.drift:事件清单写「表结构变化」
复核 L4:schema.drift 写在正式运行的封存范围里(和 catalog.drift 一样在发起时追加),可验证报告离线核对页的
EVENT_LABEL 里却没有它,事件清单直接显示原文「schema.drift」。照总方案 X10 的叫法(结构变化,和「数据目录变化」
分开)补上「表结构变化」。

复核顺带提的 ledger.matched 没加:它追加在封存之后、不在封存范围内,导出只带 seq ≤ manifest_seq 的事件
(bundle/build.py),离线核对页上不会出现它。

测试:test_schema_changes 加一条静态检查,读 verifier.js 的 EVENT_LABEL,catalog.drift、schema.drift 都有中文说法。
2026-10-16 18:37:51 +08:00
ThearchyHelios c08b2d0d52 W3-a 取值观察写库出错不再拖垮剖析:每条观察、每条覆盖率下降各占一个保存点
复核 L1:catalog_profile_observe.save 和 record_coverage_drops 与这张表的剖析结果、写进目录的结论共用一个事务,
只靠 try/except 记日志。flush 一出错(序列化不了的值、约束冲突)SQLAlchemy 就把整个事务回滚了,异常被吞掉,
外层 catalog_profile._record 提交时抛 PendingRollbackError,整个剖析任务记成「剖析任务出错」,剖析写的目录跟着
丢掉,和模块说明「辅助步骤失败不挡剖析」不符。复现:给一条观察带上序列化不了的值跑 maintenance,任务 failed。

改法:
- save:先把剖析这张表自己的写入 flush 下去(那里出错是剖析的事,照常往外抛);读目录一个保存点;每条观察
  (落库、修剪、比较、撤销已列全、记变化)挪进 _save_one,各占一个保存点。一条出错只回滚它自己,连同它撤销的
  已列全一起回滚,不会只撤了已列全却没有那条变化;别的观察和剖析结果照常提交。
- record_coverage_drops:每条覆盖率下降一个保存点。sc.record 原本只给插入包了保存点,更新已有那一条的改动要到
  外层提交时才 flush,出错照样拖垮整个事务。
- _revoke_complete:从库里重读目录(populate_existing)再写,去掉同一事务里撞了重试的循环。catalog.write_entry
  乐观锁对不上时回滚的是整个会话、保存点挡不住;会话里握着保存点回滚前的目录对象时(只 refresh 过的干净对象,
  保存点回滚不让它过期),拿它的版本去写正好撞上这一条。重读之后别人提交不了新版本:这张表的结果在同一事务里
  已经写过,事务拿着 SQLite 的写锁。

测试(test_catalog_observe 新增四条,去掉修复都会失败):观察 flush 报错时剖析结果、目录照常提交、别的观察照常
落库;前一条观察记变化出错时它的撤销一起回滚、后一条照常撤销;覆盖率下降更新出错只回滚它自己;会话握着旧目录
对象时撤销按库里的版本写。
2026-10-16 18:37:51 +08:00
ThearchyHelios 38d9082994 使用者的原始运行记录只给问数据的运行,对账运行经证据接口读(复核 M3)
复核发现:使用者(开登录后的 user 角色)发起的口径对账运行带 MASK_RESULTS、记在本人名下,run_events_readable 只看这两条,
于是他能经 /api/runs/{id}、/events、/graph 读到对账运行的原始图和事件,里面原样是口径取数节点的 SQL 模板。这一波正是以
「口径的 SQL 可能写着遮罩值」为由不让使用者在问数据里看口径、use_caliber,这条读法绕开了证据面板按遮罩换 SQL 的那一层。

做法:api/deps.run_events_readable 给 user 角色再加一条 origin_kind == "chat"——原始记录、事件、运行时的图、实时流只给他
自己的、查询结果在工具边界遮过的问数据运行(原来的说明里就写着「实际上满足的就是它自己的问数据运行」,这次照实收紧)。
对账运行他仍然能经账本和证据接口读(can_read_run 不变,那里展示的 SQL 按遮罩换过),对账的发起也不变。改了
api/deps.py 的这一个函数(没分给哪个包)和 ledger/reconcile 的模块说明。

测试:开账号密码登录,使用者对自己的问数据答案经接口发起对账,对账运行的 /api/runs/{id}、/events、/graph 都回 403,证据
接口 200;他自己的问数据运行这三样照旧 200。去掉这一条回跑,新用例失败;test_auth_access、test_personal_apps 照旧过。
2026-10-16 18:25:43 +08:00
ThearchyHelios 37d0f0abf1 自动按口径出具只给问数据的答案(复核 M2)
复核发现:自动出具对所有参与比对的探索来源都生效(问数据、画布试运行、蓝图试运行、个人应用刷新),而拍板 L1 和细案
做法 5 说的是「问数据的答案」。画布上反复试跑一份和受管口径等价的草图,每次都是新的来源运行,去重按来源运行算,于是每
试一次就自动发起一次正式运行(含报告撰写的模型费用),直到每日上限;个人应用刷新、蓝图试运行同样。

做法:plan_issue 算 auto_reason 时,设置开着之后先看来源,不是 chat 的写 AUTO_NOT_CHAT(「这次是画布试运行,不是问数据
的答案:自动出具只给问数据的答案,这里只给一键出具」),只给一键,一键照常发起。条件链改成逐条 if/elif,好读。

测试:equivalent_run 缺省改成问数据(origin_kind=chat),原来的自动出具用例照旧过;新加「画布试运行不自动、写明原因、
一键照常」。去掉来源这一条回跑,新用例失败。
2026-10-16 18:25:33 +08:00
ThearchyHelios 72a45f3ef3 check-capabilities 跟上全局渠道推送的定时评测两个事件(复核 M1)
W3-c 打开 eval.regressed、eval.failed 的全局渠道订阅时没改这项检查的期望,分支上「全局渠道:推给全局渠道的五项」和「登记了
还没推送的不列」两项就不过(复现:check-capabilities 63 通过、2 不过)。

- 全局渠道的期望改成 [...every, 'review.done', 'eval.regressed', 'eval.failed'],标题照 W3-d 的写法叫「推给全局渠道的几项」,
  合并时两边的期望并在一起即可;个人渠道、应用渠道的期望不变(这两个事件只推全局渠道)。
- 「登记了还没推送的不列」里去掉评测的两项;另加一项:全局渠道列出的叫法照实带「定时」(定时评测有退步、定时评测没有评完)。

check-capabilities 66 项、check-schedules 355 项全过。
2026-10-16 18:23:03 +08:00
ThearchyHelios 9e33c0f801 评测的两个通知事件照实叫「定时评测有退步」「定时评测没有评完」(复核 L3)
eval.regressed、eval.failed 只在定时发起的评测收尾时推(runner._conclude 只认 trigger == "schedule"),手动发起的那次人自己
在看、不推,这和细案 01 做法 15 把这两个事件放在定时一节里一致。可订阅时的叫法却是「评测有退步」「评测失败」,没写「定时」:
有人给全局渠道勾上「评测失败」、再手动发起一次评测并止损停下,渠道里收不到任何消息,界面上也没处说为什么。

- notify/service.EVENTS 和前端 NOTIFY_TEXT.eventLabel 同一句改成「定时评测有退步」「定时评测没有评完」(test_notify_events
  逐字对照两边)。eval.failed 不只是失败:到了上限、止损停下(partial)同样推,所以叫「没有评完」,和消息标题本来的说法一致。
- notify_eval 的消息标题也带上「定时」:定时评测「题集名」有退步 / 没有评完。
- 新测试:两个事件的叫法带「定时」;手动发起的评测止损停下,订阅了这两个事件的全局渠道收不到任何消息(去掉 trigger 的判断
  这一条就失败)。
- 模块说明、接口说明里引用旧叫法的几处一并改。
2026-10-16 18:23:03 +08:00
ThearchyHelios e5003a1d46 W3-d 复核 L2:使用者列通知渠道时拿不到全局、应用邮件渠道的收件地址
复核发现:GET /api/notify/channels 对使用者照样列全局、应用渠道(应用的通知里要挑渠道,只用名字和 id),而邮件渠道的
mail_to 给的是全部收件地址原文;channel_out 的说明却写着「搭建者以上才看得到」。Webhook 渠道只给主机名,邮件渠道多露了
一层:任何使用者都能拿到内部的收件名单。

做法:channel_out 加 recipients 参数,list_channels 按 _can_edit 的同一条规矩判(关登录时谁都能改,开了登录要搭建者
以上):改得了的照原样给 mail_to(改地址要整份重填),改不了的只给条数 mail_count。个人渠道照旧写发到自己的账号邮箱。
建、改渠道的返回本来就只给能改的人,照旧。说明同步改;前端 typesApp 的 NotifyChannelRow 加 mail_count(设置页的
通知只给使用者列个人渠道,界面上用不上,不加入口)。

测试(tests/test_notify_mail.py):开登录,使用者列渠道时全局、应用的邮件渠道照样在、没有 mail_to、只有条数,自己的个人
渠道照常;搭建者看得到地址。去掉修复回跑,这一项失败。
2026-10-16 18:20:52 +08:00
ThearchyHelios 36c8f581cc W3-d 复核 H1:「最近一次封存」按来源白名单认,问数据按口径出具的正式运行不再顶替看板的数
复核发现:问数据的答案按已治理口径正式出具的运行(origin_kind=ledger_issue,W3-b 加的自动出具缺省开)参数是从某人的
提问里绑出来的,而 apps/resolve.sealed_runs 只排除了补算(app_backfill)。于是 from=formal 的 latest_sealed 视图、
趋势、分享链接(latest,以及建 fixed 分享时取的那次)、视图数据接口、补算判断「新版本算过没有」都会换成那组参数的数,
而且没有任何提示。

做法:sealed_runs 的来源从黑名单改成白名单 SEALED_ORIGINS(定时、手动正式运行、应用入口和按需视图、入站钩子、直接调
接口,外加没有来源列之前的老运行);不算的列在 NOT_SEALED_ORIGINS,逐条写明原因。上面这几处都经 sealed_runs,改一处都
跟上。按白名单认是为了以后再加来源时默认不算、要算得先登记,不会再悄悄漏进看板。

测试(tests/test_shares.py):
- 先跑一次正常的正式运行,再跑一次更新的 ledger_issue 正式运行:应用页、趋势、视图数据接口、latest 和 fixed 两种分享
  都还是正常那次,分享的下载报告也不认出具的那次。去掉修复回跑,这一项失败;
- ORIGIN_KINDS 每种来源都要归进 SEALED_ORIGINS 或 NOT_SEALED_ORIGINS 之一,以后新加的来源没归类先失败。
2026-10-16 18:20:52 +08:00
ThearchyHelios 7e8d3a9c49 口径比对的 drain(测试用)只等还没做完的任务,做完还留在登记里的顺手去掉
为什么:并行跑全量测试时偶发过 drain 原地空转(faulthandler 抓到主线程一直在 drain 里,每轮 gather 都立刻返回),四次全量里挂了两次。这一波起整题等价的探索运行比完会接着自动出具,后台任务的进出更密。改成只 gather 没做完的、把做完的从登记里去掉之后,连跑两次全量都没再出现。drain 只给测试用,服务里不调它;没能在单进程里复现出根因,照实记进遗留。
2026-10-16 17:46:20 +08:00
ThearchyHelios b1c585daf4 W3-a 结构变化的影响面:一批(一次刷新)只读一次目录
后台探查每批读完、刷新对象索引发现真删除的表时,各张表的影响面以前每张都重读一遍整个源的目录(structure_impact 不给
notes_by_table 时自己读)。一批几十张表、几千张表的库删了一批表时,这一读就重复几十次。改成一批(一次刷新)读一次、
各表共用;已发布模板的图本来就按任务缓存。行为不变,test_schema_changes、test_introspect_jobs、test_source_changes 照过。
2026-10-16 17:29:00 +08:00
ThearchyHelios 8181efe836 W3-a 前端跟上:运行里的结构变化提醒、剖析清单里取值观察的说法
后端这一波新写的 schema.drift 事件、剖析任务里的取值观察检查,前端不跟上就是半截的:

- run/decode.ts 认 schema.drift:时间线里一行警告(运行未被拦截),展开逐表写发布时和现在的结构版本、表删了写「表已删除」,
  下面列出还没处理的结构变化;schemaDrift / schemaDriftLine 解析,和 catalog.drift 分开记、不混读版本号。
- 运行横幅(pages/runs/Banners.tsx):和 catalog.drift 共用同一块横幅(data-run-banner 不变)。只有结构变了时标题按结构说;
  目录、结构都变了时结构另起一组带小标题;每张表给到数据目录的入口。RunDetailView 取最后一条 schema.drift 一并传进去。
- 剖析清单:取值观察的四类检查(行数、码值观察、最新业务日期、按日行数)在检查项、发过的查询、跳过的项里有中文叫法;
  逐表进度多一类「取值观察 N/M」(后端 checks_by_kind 加 observe 一组)。剖析的范围、任务的触发(定时维护建的)进类型。
- 文案进 lib/terms.ts(SCHEMA_DRIFT_TEXT:叫「结构变化」,和已有的「目录有变化」分开,X10)。
- 检查脚本:check-decode 加 schema.drift 一节(标题、警告级、逐表版本、表已删除、另有几条、形状不对不出行),check-runs
  加一节(只有结构变化、目录和结构都变了两种情形下的同一块横幅、入口链接、时间线一行、运行照常完成)。

验证:tsc、check-copy;decode 680、runs 327、catalog-profile 191、stream 461、publish 285 项全过。
2026-10-16 17:28:07 +08:00
ThearchyHelios ed72ee68d6 W3-a 关联覆盖率下降、清理目录跨表、扩容器集成测试到对象索引和取值观察
- 剖析核对关联关系时,覆盖率从够有确证的线(95%)掉到线以下的记一条 coverage_drop(取值变化,warning):剖析本身照旧
  按规则降回推断(人工确认的只补上新的覆盖率),这里只把「覆盖率下降」留进变化记录。人点的剖析也记(变化记录全站一份)。
  关系的发现里多带 previous_coverage。
- 处理动作「清理目录」逐表清:这张表上说到被删的列的项,加上别的表指向它的关联关系(表删了时尤其如此);记这张表的
  目录版本,别的表也动了的另记 catalog_versions。
- 剖析任务的逐表进度多一类 observe(取值观察的四类合成一组),前端「取值观察 N/M」用它。
- 定时维护补一条测试:AGENTLAB_SCHEDULER 关着时调度循环不起,到点的维护不触发、不建任务。
- scripts/dialect-it.sh / dialect_it.py 扩到第 2 波的对象索引字典查询(走字典查询不退回 Inspector、类型、表注释、行数
  估算、Oracle 小写名的 quote)和这一波的取值观察(经剖析引擎只观察:行数、小表码值整表、大表 PG / Oracle 块采样、MySQL
  前 N 行、最新业务日期排除无效日期,max_date_sql 直接跑)。夹具的订单表加了表注释。本机跑了一次四种库的容器
  (postgres:16、mysql:8.4、mariadb:11、gvenzl/oracle-free:23-slim):121 项全过、0 项不通过,跑完容器已删、镜像留着。

验证:后端全量 14046 通过(11 跳过、8 预期失败)。
2026-10-16 17:28:07 +08:00
ThearchyHelios e65ac3e619 助手的节点手册:子工作流补上 workflow_version(口径账本做法 4 ⑤)
为什么:手册里子工作流只写了 {workflow_id, input},模型不知道能固定版本。补上写法,并说明问数据里不要拿子工作流去拼已发布的口径——子工作流里的数报告引用不到,组合口径走 use_caliber 的内联实例化。
2026-10-16 17:18:37 +08:00
ThearchyHelios ced7234180 按口径出具也按 SQL 里参数的位置校验绑定值;去掉 certify 没用到的导入
为什么:出具用的参数来自比对时的绑定,运行时要渲染进口径的 SQL 模板。写在引号里的值带着引号、裸写的值不是数,渲染出来就不是那一版口径的 SQL 了。组合时校验助手给的参数用的是同一个判断(ledger/compose.slot_contexts / value_problem),这里共用;那张图里别的节点引用运行时变量的不归出具管,跳过不报。宁可少出具,照实说为什么不代填。
2026-10-16 17:17:48 +08:00
ThearchyHelios fd1ec6f75d 测试:开登录时自动按口径出具按提问人的权限判
为什么:自动出具在后台跑,没有请求里的身份,要按这次运行的发起人取(ledger/issue._starter)。补一条用例:使用者没有应用授权时不自动出具,应用固定了这一版、授给他使用之后才出具,正式运行记在他名下、origin 标自动。
2026-10-16 17:15:50 +08:00
ThearchyHelios 40daf37107 评测发起后立刻交给后台再读回;补「复核要重来」的测试
为什么:发起评测时提交之后、交给后台之前还 await 了几次读回,这中间别的请求读到「排队中、却没在这个进程里跑」会把它
当成服务重启留下的、标中断。改成提交之后立刻交给后台,再读回。

- test_eval_runner:复核给了 retry_instruction 时带着这次运行的图、照那句话重来一次,第二次复核(attempt=1)不再要重来,
  两次作答运行都记在这一题名下
2026-10-16 17:15:17 +08:00
ThearchyHelios 5e9413e080 账本:按来源运行找出具过的正式运行、对过的账改在 SQL 里按 origin.source_run 筛
为什么:GET 账本每次都要列这两样,原来把全部 ledger_issue / reconcile 运行读出来在 Python 里筛,出具多了就是整表扫。改成 origin_kind(有索引)加 json_extract(origin, "$.source_run"),写法同 auth/keys 认幂等键。
2026-10-16 17:11:55 +08:00
ThearchyHelios fc9b31ecfe W3-d 收尾:关登录时不发汇总和到期提醒、分享页不下发入口表单、邮件标题去换行;测试互不干扰
- notify/digest:关登录时「派给我的」认不出是谁、访问密钥也不生效,待办汇总和密钥到期提醒一概不发,也不认领当天
- resolve_page 的分享旁路不下发入口的表单(分享链接里不能发起)
- 邮件标题里的换行合成空格:应用名、节点名是人起的,邮件头里有换行就是头注入
- 邮件、汇总的测试前后各清一遍渠道、投递记录和 notify 一组,对外访问地址的缓存跟着清;汇总测试只删自己建的待办。
  全量并行跑时不再被同一进程里别的测试留下的行带偏
2026-10-16 17:11:54 +08:00
ThearchyHelios a0cdb16617 重评时把评分规则版本记进指纹;去掉几处没用的导入
为什么:按新规则重评之后,和别的评测比时「评分规则」那一项要照实标出变了;总表上另记重评的时间。
2026-10-16 17:11:09 +08:00
ThearchyHelios 88e219eabc 评测总表照实列出没开评的题、按评完的题估每题花费;对比测试单独成文件,补超时止损的测试
为什么:总表里「没开评、停下、出错」的题以前只体现在计数里,准确率旁边看不出来;下一次开跑前的估算按每题平均花费算,
把没开评的题也摊进去会估低。

- stats.summarize 加 not_run;per_case_cost_usd 只按评完了的题平均
- test_eval_compare.py:逐题转移与归因、指纹差异、A/B 配对表、选基准(从 test_eval_stats 拆出来,对上细案的测试清单)
- test_eval_runner:最近 10 题里 7 题超时就止损
2026-10-16 17:08:29 +08:00
ThearchyHelios 538799e70d 沉淀:认证队列、固化成口径草稿、发布后补比;账本统计补上对账与认证队列的转化(口径账本做法 8、10,拍板 L9)
为什么:同一类问题被反复问、Agent 反复写出同一个形状的 SQL,就该沉淀成一份口径;以前 from-run 保留 Agent,沉淀出的模板没有固定 SQL,以后永远比对不上。第 2 波的统计在 E2 反馈、E3 认证队列上照实写了「目前没有数据」,这一波补上能补的。

做了什么(新增 ledger/certify.py):
- GET /api/ledger/certify-queue(搭建者):确定性重复(问数据运行里被报告引用、没对上口径的查询,按数据源 + normalize_sql 文本分组,≥3 次运行且来自 ≥2 段对话;跨运行变化的字面量是参数候选,展示的形状不带取值)、问题聚类(app/chat/questions 全站唯一的那份)、被反复用的答案(导出 ≥2 次用第 2 波的 report.exported 审计、做成过应用看蓝图来源、在画布里打开或提取过看工作流 origin)。每项给代表问题、轮次、相关口径、是否已固化。
- POST /api/ledger/certify-queue/dismiss:「不是口径」写审计 ledger.certify.dismiss,之后不再进队。
- 固化成口径草稿(from-run 加 freeze、peers 后转调 certify.freeze):报告引用的查询变成固定 SQL 的取数节点,跨轮变化的字面量换成 '{{ input.pN }}'(缺省值取代表运行的),单行结果的数值列成口径卡指标,报告核对规则按受管要求写、契约 report_from + metrics_from + required(多行加 cells);过 validate_graph 和受管门禁。新工作流 origin.sources 记来源问题。
- 发布之后补比:api/workflows.commit_publish 提交后调 certify.after_publish,来源运行按新口径补比一次(新的 trigger=certified,界面写「按之后发布的口径回看」,时间线和账本接口同一句)。
- 统计:E2 加对过账的答案数、参数经人核对的、指标由人选的次数(反馈照实写第 4 波);E3 认证队列的转化(固化、发布、不是口径)。

测试:tests/test_ledger_certify.py(参数推断与模板、三种信号进队、门槛、不是口径、固化草稿过受管门禁、发布后来源运行补比成等价已证明并出队、再问标等价已证明、统计)。
2026-10-16 17:06:41 +08:00
ThearchyHelios df6c3ce10d 定时页照实显示非工作流目标;评测停止时几臂一起收尾;工作流题的测试
为什么:这一波后端开始接受评测题集、数据源维护这两种定时目标(总方案 X4),定时页要能把它们照实列出来、点进去
不报错;界面上不能有点了没用的入口。

- 定时页列表:非工作流目标写种类(评测题集 / 数据源维护)和目标名,不标运行类别,「编辑」写成「查看」
- 详情:非工作流目标的设置只读地列出名称、目标、频率、时区、补发宽限,不出工作流那张表单(选工作流、输入模板、
  Webhook 对它们不适用);页头的启用、立即运行、删除和触发历史照常
- 立即运行发起的是一次评测时说「已发起一次评测」(以前会落到「未能发起运行」);触发历史写评测的编号
- types.ts:Schedule 加 target_kind / target_id / target_label / target_name / target_exists,触发记录加 eval_run_id;
  文案进 SCHEDULE_TEXT
- check-schedules 加一段「目标不是工作流的定时任务」
- runner:还没准备好就被停下、出错时,按 pair_id 找全几臂一起收尾
- 测试:工作流题按钉住版本跑探索运行(不发正式运行、不建图);钉住基线的接口
- 后端几句给人看的话按文案规范改(不用「跑」「钉」)
2026-10-16 17:04:05 +08:00
ThearchyHelios 1fe3d85773 W3-d 检查脚本:check-share 新建,check-capabilities 加邮件
- check-share.mjs:/s 分享页不进外壳(整页只请求分享页一个接口)、key 只在请求头里、视图照画而流程组件是说明、印章常显、
  页脚、点数字说明出处在报告里、下载报告带请求头、撤销 / 到期 / 链接不完整写明、没允许嵌入的在 iframe 里不显示;应用编辑里
  开关没开时不给新建、建分享链接只显示一次、列表与撤销。接口全部伪造,按路径开头拦(前端模块地址里也有「/api/」)
- check-capabilities:「邮件服务」一节(管理员改、搭建者只看、口令只说设没设、有改动时不能发测试邮件、测试邮件照服务端
  的话写「已交给邮件服务器」)、待办汇总的发送时间只交这一项、邮件服务没启用时不列邮件格式、个人邮件渠道写「发到账号邮箱」
  且建的时候不带地址、投递记录写「已交给邮件服务器」;可订阅事件的期望跟着第 3 波打开的待办汇总、密钥快到期更新
- check-all 登记 share
2026-10-16 17:00:49 +08:00
ThearchyHelios 906ec072ce 按口径正式出具与口径对账(口径账本做法 5、6,拍板 L1、L5)
为什么:问数据的答案整题等价于已发布口径时,要能按那一版发起一次完整走过门禁的正式运行(路径一、二a);只是相关、不等价时,要能看「差多少」(路径二b)。两条路都不能让客户端冒充来源、自己传参数,也不能在 Python 里算差值。

正式出具(新增 ledger/issue.py):
- POST /api/ledger/answers/{run_id}/issue {workflow_id, version}:参数只从比对工件的绑定取,补入口缺省值再过 apps/forms 校验;经 launch_run 发起正式运行,来源 ledger_issue,origin 记 source_run、conversation_id、turn_id、binding_hash、口径和认法、是否自动;按 expect_hash 钉 graph_hash。
- 不能出具照实写原因:整题不等价、点名别的口径、版本退役或没发布、图里有人工节点或写入(apps/backfill._side_effect 抽成共用的 graph_side_effect)、缺入口字段、超过每日上限、权限(L5:使用者只能按他有权使用的应用固定着的版本出具,复用应用授权,origin 记 granted_by_app;别的写「请搭建者出具」)。
- 自动出具(L1):比对完(只在运行一封存就比的那一次)整题独立等价、版本受管、设置开着、当天没超上限才自动;助手选用的只给一键。不自动的原因写进 actions 的 auto_reason。
- 同一运行、同一口径、同一组参数只发起一次(input_key 去重),进程内按(运行、工作流、版本)串行。
- 设置加 ledger 一组:自动出具开关、每日上限、比对时限(ledger/match 读它)。

口径对账(新增 ledger/reconcile.py,derive 扩 reconcile_graph / ours_block / assemble 的出口契约):
- POST /api/ledger/answers/{run_id}/reconcile:按那一版确定性派生只读图(这次的取数 ours_q1 → 口径的取数 → 口径卡 → 对账卡,出口契约 metrics_from,不加报告、不调模型),来源 reconcile 的探索运行;对账卡算这次、口径、差值、相对差。
- 参数是启发得来的、不全、含遮罩值时先让人核对(needs_check,没核对的 409);指标式不是取数一格、差异有兜底时要人选对照哪个指标,选择记进 origin。
- 多行结果:被引用的行按维度列经合并查询 LEFT JOIN 对齐、按引用先后排,最多 10 行;维度是遮罩列、合并键不唯一照实拒绝。使用者发起带 MASK_RESULTS / READ_ONLY,搭建者也带 READ_ONLY。
- GET /api/runs/{id}/ledger 的 actions 补上出具、对账两类,issued、reconciled 列出发起过的运行。

测试:tests/test_ledger_issue.py(自动出具、只给一键的几种、拒绝原因、使用者的应用授权、并发只发一次、设置)、tests/test_ledger_reconcile.py(核对参数、派生图确定与契约、差值和相对差、出处链、要人选列、多行对齐、遮罩维度和重复键拒绝、使用者只读遮罩);第 2 波 test_ledger_api 的 actions 断言按这一波改。
2026-10-16 16:55:06 +08:00
ThearchyHelios e29adf80d0 评测执行的测试:执行、评分、统计与对比、清理、定时与通知
为什么:照细案 01 的测试清单把这一波的行为钉住,顺带修了跑测试时撞出来的两处。

- test_eval_runner:后台排队、并发不超过 max_parallel 也不超过全局并发的四分之一、同一题集同时只能一次、停止后只重做
  没做完的(评完的不动)、重启标中断不续跑、单次金额上限触顶不再开新题、评测每日账单独记(不占裁判、核查的额度)、
  连续 3 题助手调用失败止损、每题超时取消正在跑的运行、停在审批上的作答取消掉(评测不代批)、接口的估算与进度、设置里
  eval 一组和 /settings/eval/spend
- test_eval_score:五种结论、写出精度和百分数、维度配对与弱匹配、多行 / 名单 / 没有数据 / 反问、路径和期望先于数字、
  dual 两版、允许的相对误差和双盲复算是同一个函数;读封存的答案、逐题 E1 用账本的比对函数;重评只读封存、不调模型
- test_eval_stats:Wilson、精确 McNemar、P50/P90 和手算对照;总表的各项;转移与四种归因;指纹差异;配对表;选基准
- test_eval_cleanup:保留规则;forget 只删一条引用都不剩的文件;清理后总表还在、结果和底下的运行删掉、共用的工件留着
- test_eval_schedule:定时接口收题集(拒工作流、输入模板、Webhook)、到点建一次评测并写 eval_run_id、重叠跳过、错过的按
  宽限补;有退步推「评测有退步」、没评完推「评测失败」,消息里没有期望值和答案里的数
- test_eval_e2e 补:开评拍目录快照、记表结构前后;打开裁判时结论裁判走按需裁判那条路、花费记在评测账上,文字要点单列
- 修:期望反问的题在建图、运行失败(含超时)时把原因写全;改非工作流目标的定时任务时提交后不再读过期对象
2026-10-16 16:49:47 +08:00
ThearchyHelios f5c94cf12d W3-a 变化记录的接口与处理、定时维护(next-round 03 B3、B4)
变化没有统一的记录:进不了审阅,发不了通知,口径账本的召回也没有可以订阅的接口;目录只能手动探查、手动剖析。现在:

- api/source_changes.py(骨架已在 datasources 通配之前登记,搭建者可用,访问密钥不放):GET /api/datasources/{id}/changes
  (缺省开着的和知道了的,按「使用次数权重 × 严重程度」排,带各级别计数)、GET /changes/{change_id}、POST
  /changes/{change_id}/resolve。处理动作落到目录已有的写入接口:清理目录里说到被删的列的项(整份写、乐观锁)、加入码值
  (apply_patch 配 _merge_codes,含义待填写,依据写变化编号)、重新起草这张表(模型用不了时只按表结构起草)、重新剖析、
  知道了、误报(必须写理由);每个动作写 resolution,动了目录的记目录版本,建了任务的记任务编号。
- data/source_changes.py 给召回:list_changes(按 seq 游标拉)、affecting(按源和表反查)、resolve、triggers_recall(只认
  结构类、处理时改了目录的、触及有效记录条件的新码值)。
- 通知 source.changed 的发送函数 notify/service.notify_source_changed:只推还要人看的「要处理」级变化,按源汇总,消息里
  只有源名、各级别条数和链接(D9);探查、剖析任务结束时有新的要处理变化就调。这一行的订阅范围第 4 波有了界面才打开,
  现在推不出去(照实)。
- 定时维护(data/source_maintenance.py,X4:就是一条 target_kind=source_maintenance 的定时,不另建表):fire 按顺序建
  探查任务 → 剖析任务(范围 needed / observe / maintenance 按选的步骤),任务上记 trigger=schedule、fire_at、schedule_id;
  剖析等探查进了同源队才建,靠 SourceLanes 串行;剖析没开、数据源停用 / 不存在 / 是上传表格时照实记没执行,同源还有
  探查或剖析在做时记跳过;触发记录的 inputs 写明建了哪几个任务、哪几步没执行。check_target 核对目标。维护步骤存在
  options.catalog_maintenance,经 GET / PUT /api/datasources/{id}/maintenance 改,数据源表单不显示它(api/datasources.py
  的 _MANAGED_OPTIONS 加了这一个键)。

测试:test_source_changes(列表与计数、误报要理由、处理过的 409、清理目录与加入码值记目录版本、剖析没开照实 422、
去重、游标拉取与反查、召回触发、通知只有源名条数链接)、test_source_maintenance(按顺序串行、trigger 与 fire_at、
剖析没开记未执行、停用 / 不存在 / 忙、调度循环只补最近一次且积压只记一条、步骤接口与核对目标)。
2026-10-16 16:48:40 +08:00
ThearchyHelios f887cdc30a W3-d 邮件、待办汇总、分享链接的界面
- 设置 → 通知:渠道多一种格式「邮件」,邮件服务没启用时不列;个人邮件渠道不填地址,写「发到账号邮箱 …」(账号没有邮箱照实写
  发不出去),全局、应用的填收件地址;投递记录里邮件成功写「已交给邮件服务器」、悬停说明退信不追踪;新增「邮件服务」一节
  (管理员改、搭建者只看:口令留空不改、可清除,发测试邮件要先保存)和「每天的待办汇总」的发送时间
- 设置 → 偏好设置的「应用」一节:「允许建分享链接」开关(缺省关,写明关掉时已有的一并打不开)
- 应用编辑 → 岗位与授权:分享链接的管理(选页面、始终最新 / 固定这一期、有效期 1–90 天、允许嵌入;链接只显示这一次;
  列出状态、到期、打开次数,撤销先确认);开关关着时不给新建,已有的照列能撤销;个人应用写明不能分享
- /s 分享页:不进外壳(App 在 /s 上整个跳过认人、取目录、心跳、读主题,请求层在 /s 上也不送去登录页),key 从 # 后面读、
  放进请求头;同一个 ViewRenderer 渲染、操作全禁用;印章常显(「数据的来历」逐个列视图的印章);点数字说明出处在下载的
  报告里;页脚「只读 · 由 X 于 … 分享 · … 到期」;撤销、到期、关了开关、链接不完整按服务端的话写明;没允许嵌入的分享在别的
  页面的 iframe 里不显示内容、给「在新窗口打开」
- ViewRenderer 加 onEvidence:点数字交给调用方(分享页),不开在线证据面板
- check-copy 加「快照链接」一条(总方案 X10:写「分享链接」),带自检
2026-10-16 16:44:06 +08:00
ThearchyHelios c95e5b4e84 W3-a 取值观察:剖析引擎的四类观察检查、比较器、撤销「已列全」(next-round 03 B2)
码值出现新取值、比例剧变、业务日期断档、某天没数据,以前都看不到:确认过的码值列不再取分布,大表只看物理顺序最老的
前 10 万行,「已列全」过期以后 SQL 检查还照它提示。现在:

- 剖析引擎加四类观察检查(data/catalog_profile_observe.py):rows(行数,用剖析开头那条估行数的结果)、codes_observe
  (所有码值列,包括人工确认过、标了已列全的)、freshness(已确认或已验证的业务日期的最新日期)、daily_volume(最近 35
  天按天计数)。计入每张表的预算、止损和留痕,每条观察记口径 basis 和它出自哪几条查询;遮罩列不进任何观察查询。
- 大表口径照目录-D7:有确认的业务日期且该列打头有索引看 35 天窗口;PG / Oracle 没有就块采样(比例按表的大小取一档,
  不跨档比);MySQL / SQLite 没有就按存储顺序的前 N 行,照实写口径。最新业务日期、按日行数在大表上只在有索引时才查。
  SqlDialect 加 max_date_sql(有效区间里的最大日期),方言金样跟着加了两条。
- data/value_drift.py 纯函数比较器:新码值(已列全的任何新值、没列全的要占 0.5% 以上且有历史)、PSI 剧变(≥0.25
  warning、0.1–0.25 info,两次都要 1,000 行)、断档(超过 max(2×中位间隔, 3 天))、缺天(以前的窗口没有过)、量跌
  (不到前 28 天中位数的 30%)、行数跌 30% / 涨 3 倍(估算值要连续两次)。只和同口径的历史比。
- 观察记进 table_observations(每张表每种观察留最近 26 条),比出的取值变化进 source_changes,和这张表的剖析结果、目录在
  同一个事务里。机器唯一写进目录的事:数据证明码值表不再列全时撤销「已列全」(目录版本 +1,署名「取值观察」,值、含义、
  状态都不动),SQL 检查随之不再按旧码值表提示 unknown_code。
- 剖析任务的范围加 observe(观察到期的有目录的表,只观察)和 maintenance(定时维护:需要剖析的 + 观察到期的);人点的
  「需要剖析 / 全部 / 指定的表」照旧不观察。只观察的那一项不算「上次剖析」(不顶掉表清单的上次剖析和给助手的剖析事实)。
  任务的设置快照里记 trigger / fire_at / schedule_id(剖析任务表没有这几列)。
- 观察结论(最新业务日期、更新的行数)经 last_facts → schema_view.profile_lines 和剖析事实同一节进 look、db_schema、
  /schema、表助手(X7),遮罩在渲染那一刻再核。

测试:test_value_drift(阈值两侧、口径不同不比)、test_catalog_observe(四类观察与口径、撤销已列全与 SQL 检查、去重、
遮罩不进查询、大表窗口与前 N 行、PG / Oracle 采样 SQL、观察不算上次剖析、进表详情)。
2026-10-16 16:40:41 +08:00
ThearchyHelios 2371e75f17 评测执行的主干:后台评测、无头作答、评分、统计、对比与 A/B、清理(下一轮第 3 波 W3-c 第一段)
为什么:第 2 波只有题集和期望答案的来源,评测本身跑不起来。这一段把细案 01 做法 9–12、16、17 的后端接上,
量的是用户在问数据页拿到的同一轮,而不是另抄一份编排。

- eval/answer.py:无头作答,串起 answer_ops → chat/turn 的决定 → launch_run(origin=eval,带目录视图、只读、遮罩)
  → 等终态(停在审批的取消、记「评测不代批」;驳回记路径「被驳回」)→ review_of_run → 按同一规则重来一次;
  工作流题按钉住版本的图跑探索运行(不发正式运行,免得被 pick_base 和应用视图取到)。用量按角色记
- eval/runner.py:用 core/job_runtime,一次评测一个任务,题 × 重复 × 臂一项;A/B 几臂共用期望、同一题同时开跑;
  开评拍目录快照、记上传表格版本和表结构哈希的前后;并发不超过 max_parallel 和全局并发的四分之一;每题时限;
  评测单独一本账(开跑前估算、每题预留后找补、触顶不再开新题);止损;能停、只重试没做完的、重启标中断不续跑
- eval/score.py:六种期望、五种结论(量纲不同单列)、维度配对与弱匹配、路径不对直接不通过、口径 dual 两版都报;
  逐题 E1 用口径账本的 compare_queries(总方案 X2,不另写 signals);SCORER_VERSION 可重评(eval/rescore.py,只读封存)
- eval/judging.py:结论裁判走按需裁判同一条路(evidence._judge_now),文字要点由裁判判、单列不计准确率;花费经
  judge.charged_to 记在评测那本账上(engine/judge.py 只加了一个上下文变量和取账本的一处)
- eval/stats.py、eval/compare.py:准确率与可追溯准确率 + Wilson 区间、各项分布、P50/P90、按角色成本、引用率、逐题 E1;
  选基准、逐题转移与归因、指纹差异、A/B 配对表与精确 McNemar;「目录贡献」预设对照(当前 vs 关,可加只用已核实)
- eval/cleanup.py + core/artifact_store.forget:保留最近 N 次 + 钉住的基线 + A/B 另几臂,删结果、明细工件、底下的运行和
  引用行,工件文件一条引用都不剩才删;总表永久保留
- engine/tolerance.py:双盲复算的 _agree / _delta_pct 原样搬来共用(dual_check 改 import,行为不变)
- 设置加 eval 一组(builder 读、admin 写)和 GET /api/settings/eval/spend;题集预算在全站缺省上覆盖,每日上限只能往下收
- api/evals.py:发起(单臂 / A/B / 目录贡献预设)、估算、进度、逐题、停止、重试、重评、钉基线、和上一次比、配对表、清理
- main.py 的 lifespan:影子起草和评测的启动整理、关停(评测先于运行停,停时取消自己发起的运行)
- 测试:test_eval_e2e(dual_check_shop + 剧本模型:按支付日 330 通过且 SQL 等价,按下单日 310 不通过、差在 time;
  目录对照 1:0;和上一次比找到退步、归因做法变);通知事件、定时接口形状的老测试按新事件和新字段更新
2026-10-16 16:38:09 +08:00
ThearchyHelios 4f4ad48ad7 组合式问数:问数据的助手可以 use_caliber 选用已发布口径,服务端确定性内联实例化(口径账本做法 4,拍板 L3)
为什么:问数据以前看不到口径,也没有组合机制;子图做骨架的路走不通(子图证据不交回父图、使用者用不了子工作流)。改成助手只选口径、填参数,取数节点由服务端按固定版本照抄,证据照常封存、比对照常复证。

做了什么:
- 问数据的查看对象加 caliber(look/calibers 的 compose 模式):另给每张卡的入口字段(类型、必填、可选值、格式、缺省值)和「能不能组合」(ledger/derive.load_pinned 同一个认法),能组合的写出 use_caliber 的写法;仍不给 SQL。只给搭建者及以上:使用者的图里看得到拷进来的取数 SQL。
- 新增 ledger/compose.py:use_caliber {id, workflow_id, version, card, metrics, inputs} 经 derive.caliber_block(扩了 card_id、inputs 两项)实例化——取数节点照抄、id 加前缀、模板按参数渲染成字面 SQL,口径卡 id 就是操作 id、caliber_from 带改名映射。参数按入口字段过 apps/forms 校验,写进 SQL 引号里的不能带引号、裸写的只能是数;只能选这一轮看到的那一版、能组合的卡、卡上的指标;不合规拒收。
- GraphApplier 落地 use_caliber:节点、连线记进 seen(兜底重放照样在),入口连到第一个取数节点;转给界面的只有那一条操作带 result;拒收的原因经自查交回模型(GraphChecker.issues)。
- 提示:协议多一段 USE_CALIBER_PROMPT;LOOK_WHEN 里问数据那一条按细案改写(只在能选用口径时换)。运行时写 SQL 的 Agent 不看口径(L2)。
- 比对器认出助手选用的查询(从封存的图里读 caliber_from.nodes):证明了的标 composed「助手选用」,没能证明的照常是相关口径并说明;标签 how=composed,指标标签记取自哪几条查询;比对算法版本升到 2。
- 前端:问数据过程里 use_caliber 照实显示一行(选用了哪一版、生成几个节点;拒收写原因),节点数算进「新增几个步骤」;文案进 LEDGER_TEXT.use;check-decode 加一节。

测试:tests/test_ledger_compose.py(实例化确定性、各种拒收、查看文字不带 SQL、提示切换、落地与接线、拒收交回模型、剧本模型走一遍生成再真跑出 composed 标签、使用者不给口径)。
2026-10-16 16:35:28 +08:00
ThearchyHelios f759c5c82d W3-d 分享链接(后端):只读、不用登录、必设到期、只显示定时出数的视图
为什么做(04 细案 C2,集成-I6):会话 cookie 是 SameSite=Lax,跨站的 iframe 里带不上,想把看板放进别的门户只能每人开账号且
必须同站;分享链接是跨站嵌入唯一走得通的办法。它把数据交出了授权范围,所以每一步往窄处取:
- view_shares(db/models_apps.ViewShare):只存 key 的 sha256 和开头 6 位;latest 跟着最近一次封存走,fixed 存建分享那一刻
  每个视图的运行 id、按建分享时的应用版本解析;到期必填;打开只计数
- POST / GET / DELETE /api/apps/{id}/shares:要应用的 manage,建要管理员开了 apps.share_links(缺省关);个人应用、没发布、
  归档的不能分享;这一页没有最近封存的视图不收;到期最长 90 天;key 只在建的时候交回一次(/s#<key>);建、撤销写审计
- GET /api/shares/page、/api/shares/report.html 是公开路由(中间件把 /api/shares/ 当公开前缀),只认请求头
  X-AgentLab-Share,地址里带的 key 不认;撤销、到期回 410 写明;开关关掉时已有的一并 403;应用归档、页面没了 410
- apps/resolve.resolve_page 加 share 旁路:只解析 latest_sealed 视图(fixed 用存下的运行,趋势也停在那一期),按需、实例、
  探索视图写明原因、从不发起运行(auto 的也不);流程组件和报告换成写明原因的说明块;页面缓存键另加分享的标识;out 另给
  各视图取到的运行和可下载的报告
- 下载报告只认这条分享此刻解析出来的运行,内部就是 export_report_html(同样遮过、照常记导出审计,导出人记成不冒充任何人的
  分享身份)
- 分享接口都不放给访问密钥(不在 KEY_ROUTES,门禁 403)

安全头:/s 用 AGENTLAB_EMBED_ORIGINS 和 no-referrer(第 1 波 web_security 预留的规则,这次在真实页面托管下测)。C3 同站门户
嵌整站只做配置和文档:写进 web_security 的模块说明和 deploy/agentlab.env 的「嵌入」一节,「分享链接还没有上线」那句改掉。
2026-10-16 16:30:42 +08:00
ThearchyHelios 8d59ca1581 W3-a 表结构变化:前后差异、影响面、结构版本与 schema.drift(next-round 03 B1)
以前重新探查是整份替换表结构、不比前后:删了一列,那一列上人工确认过的目录项、写死这一列的已发布模板都收不到提醒,
正式运行要到查询那一步才报错。现在:

- data/schema_diff.py:纯函数,同一张表前后两次的结构比出变化(加列、删列、类型、可空、注释、主键与唯一约束、外键);
  疑似改名(类型相同、夹在同样两列之间、只有一对)只说「可能」;读不到唯一约束、外键的一边不比;升级后第一次读到
  (结构版本 0)只建基线,不把通用写法换成方言完整写法说成类型变了。
- 后台探查顺带记变化(data/introspect_jobs.py):刷新对象索引记新出现的表、真删除的表(第一次建索引只建基线);开始
  执行时记离开范围的有目录的表、范围规则和上一次不同;逐批读完拿上一次的结构比,读不到结构的记 table_reflect_failed。
  离开范围、读不到结构不算删除。记变化出错只记日志,不挡探查。
- 变化记录 source_changes(db/models_sources.py 的 SourceChange,格式 agentlab-change/1,seq 自增不复用、按指纹去重),
  记录与读取在 data/source_changes.py;取值观察的表 TableObservation 同文件建好。
- 影响面(data/catalog_impact.structure_impact):目录里说到被删、被改的列的项(列级项、业务主键、业务日期、有效记录条件
  按 parse_valid_filter 取列、两端的关联关系)带状态,人工确认过的升为要处理;已发布模板写死的 SQL 换上新结构再跑
  sql_unknown_columns,说得出「这条 SQL 现在会报列不存在」。sql_unknown_columns 从 api/copilot.py 挪到
  data/sqlcheck_columns.py 共用,copilot 那边只改 import。
- 发布时记 WorkflowVersion.schema_versions(只记直接引用、结构版本大于 0 的表);正式运行发起时比对,变了写 schema.drift
  (core/events.py 登记,只提醒、不拦);版本详情给出 schema_versions、schema_changes;发布前检查把这一版直接引用的表上
  还没处理的结构变化列成警告(schema.change_open),不拦发布。

测试:test_schema_diff(纯函数)、test_schema_changes(SQLite 夹具 ALTER 前后、影响面、去重、真删除与离开范围、
schema.drift、发布前检查、升级后基线)。
2026-10-16 16:26:11 +08:00
ThearchyHelios 86d2840e41 W3-d 邮件渠道、待办汇总、访问密钥到期提醒(后端)
邮件(04 细案 B2,集成-I4 / I5,改写应用平台 Q6):
- notify/mail.py:标准库 smtplib 放进 asyncio.to_thread,单次超时 10 秒;4xx 和连不上按 1/2/4 秒重试,5xx 不重试;
  报错只写主机名和服务器回话,口令一律抹掉;纯文本 + 转义过的 HTML,带 List-Unsubscribe 和 Auto-Submitted;测试可换假的
  SMTP 工厂。模块说明写清改写 Q6 的理由:SMTP 是可选配置、退信不追踪照实说「已交给邮件服务器」
- 通知渠道多一种格式「邮件」(core.CHANNEL_FORMATS;定时任务的 Webhook 只会发 HTTP,FORMATS 不加,免得定时任务能选
  邮件):个人邮件渠道不收地址,只发到名录里的账号邮箱,没有邮箱记一条失败投递并写明去「设置 → 身份」填;全局、应用的填
  固定地址,最多 20 个;邮件服务没启用时不收新的邮件渠道;渠道列表另给 mail(启没启用、本人的账号邮箱),投递记录带格式
- 设置的 mail 一组(只有管理员能改):口令用 core/crypto 加密,读的时候只说配没配;启用时要配全;发件人名称、主机不许
  换行;「发测试邮件」只发到管理员自己的账号邮箱,关登录、没有邮箱时 422 说清怎么办
- mail.default_for_assignees(缺省关):开了之后第一次派到待办的人自动有一个个人邮件渠道(订阅待办打开、动作结果未知、
  审核有结果),本人删了不再建回来;关登录时不建

待办汇总与密钥到期(04 细案 B3):
- notify/digest.py 接进调度循环的步骤列表:每天到 notify.digest_at(缺省 09:00,服务端时区)给订阅了 task.digest 的
  个人渠道发汇总(条数、最早时限、最多 5 条节点名、去待办箱的链接,没有待办不发);按日期在 settings 表里插一行认领,
  重启不重发;认领行和自动建邮件渠道的名单是通知自己记的账,设置接口不列不收
- key.expiring:到期前 7 天、1 天各推一次,个人密钥推本人的个人渠道、服务账号的推全局渠道,推之前记审计、按审计去重;
  轮换下来的旧密钥、撤销了的、建的时候就在这一档里的不推
- 两个事件推送实现了,才在 notify/service.EVENTS 打开它们的订阅范围

设置的 notify 一组加 digest_at;只给一部分时补上存着的另几项;环境变量锁住对外访问地址时只核对那一项,汇总时间照常能改。
apps 一组加 share_links(分享链接的开关,缺省关,下一个提交用)。测试:假 SMTP 工厂下的收件人、失败投递、重试、口令、
设置与权限、自动建渠道;汇总按时、只发一次、重启不重发;密钥两档、谁收、不该推的几种。
2026-10-16 16:21:41 +08:00
ThearchyHelios 1f8250699c 口径卡来源支持节点改名映射 caliber_from.nodes(口径账本做法 4 ③)
为什么:组合式问数、对账、召回要把一份口径的取数节点拷进另一张图,同一张图里放两份口径时节点 id 会撞,拷过来的取数节点得改名(c1_fetch)。以前 caliber_from 只认同名的取数节点,改了名口径卡就取不到输入。

做了什么:
- metrics._pinned_caliber 认 caliber_from.nodes {原 id: 新 id}:按语法树把式子里 nodes.<原 id>(含 cell(nodes.X, …) 的第一个参数、nodes['X'])的根换成新 id 再算,字符串字面量里的字不动;指标集的 source 照记映射。映射形状不对节点失败并说清哪一处。
- 画布校验 _caliber_from_issues:映射的形状和运行时同一个判断(rename_problem),改名后的节点要在图里、在口径卡上游。
- 升版检查照常挡、照常记事件,事件带上 caliber_nodes;证据的 _caliber_source / _caliber_upgrade 照带映射。
- 运行对比按映射把式子换回那一版里的写法再比:同一份口径换了节点名不算口径变了。推演按卡上记的改名后的式子原样重算。
- 没写 nodes 的老图一字不差:式子不经 unparse、来源只有三项、升版事件没有新键(单独测)。

测试:tests/test_caliber_from_nodes.py(改写规则、运行时取值、证据链、推演、校验、升版检查、对比、老图不变)。
2026-10-16 16:20:25 +08:00