Commit Graph

409 Commits

Author SHA1 Message Date
ThearchyHelios 89a4bc8cfc 合入期 5 验收修复 M1:核查 agent 正文里的依据标记不再断句——只装着依据的括号(含【】[]〔〕[])连同里面的停顿标点一起去掉,去掉之后按真正挨上的两个字补空格,两个数不再并成一个;核查 agent 的提示词改成依据写在句末;收尾超时照实记 timeout_s;没给理由时照实说没有给出理由 2026-10-12 13:12:20 +08:00
ThearchyHelios b6a33b4ee2 合入期 5 前置修复 K3b:工具边界遮罩收口——分析出意外、(表达式).* 展开、把 SQL 或表名写成字符串参数的函数、记着别的会话 SQL 的系统视图与 SHOW / EXPLAIN、当一张表用的 VALUES 读到遮罩列时整份遮;按值遮只换文字格、数值证据原样并记下换过的格子;分档标签一档一行时照常显示;交给核查 agent 的证据摘录先遮,反方理由加最后一道关;长 CTE 链的分析有缓存与步数上限 2026-10-12 13:12:10 +08:00
ThearchyHelios 60be16a0bb M1 复核修正(第 3 轮):依据两边什么都没有时也判粘连,两个数不再并成一个;只装着依据的括号认【】[]〔〕[];右边紧贴着下文的半角逗号后面补空格
渲染(evidence._tidy):
- 上一轮只沿着去掉的字往两边找粘连:依据前后没有空白、括号、标点时没有切口,「华东 [[v:Q1.r0.销售额]][[see:Q1]]
  [[v:Q1.r0.订单数]] 笔」渲染、组装都是「4,919.5212 笔」,「[[v:…]][[see:Q1]]2 月」是「4,919.52 月」,「**华东 [[v:…]]**
  [[see:Q1]]2 月」组装出「华东 4,919.52 月」,verify_doc 都不报。现在每个依据(连着写的一串、只装着依据的括号都算)
  两边都判(_glue 看所有不显示的字,不只看切口),会读成一串的补一个空格:这一片有去掉的字的照旧换掉头一个,没有的
  插在头一个依据前面(粗体收尾的星号外面)
- 只装着依据的括号认【】、[](全角半角混用也算)、〔〕、[],和()一样连括号去掉,外面悬着的停顿标点照原规则收拾;
  依据在这些括号开头、末尾紧挨着分隔标点的,同()一样收拾。核查 agent 拿到的查询结果以「【证据 Qn】」开头,模型照着
  写成「【[[see:Q1]]】」,以前渲染成「【】」
- 右边是写作者紧贴着下文写的半角逗号的(「1,200([[see:Q1]]),900」去掉括号读成「1,200,900」),空格补在逗号后面
  (同「212,[[see:Q1]],5」→「212, 5」)。上一轮把这一类当成可以不管的「),5」写法;和基线 58a6759 逐份比,结构化组合里
  有 2481 份是上一轮去掉括号之后才并成一个数的(「增长 12([[see:Q1]]),212 笔」→「增长 12,212 笔」),现在都隔开了
- 找挨上的字时也越过删除线的两个波浪线:前端照样画出删去的数,挨着后面的数读
- 流式渲染把这几种括号、半角方括号、波浪线也攒到后面的字来了再放;半角「]」攒着时,标记收尾的「]]」和标记里末尾的
  空白不算,标记整个放出去,从它后面攒起

不含依据的文档渲染结果、内容哈希逐字不变:随机 1.6 万份不含依据的文档和基线逐份比过,BEFORE 两份哈希照旧。

测试:依据两边什么都没有(数、标记、粗体、斜体、删除线、连着写的一串)、几种括号(只装着依据、全角半角混用、套着、
和别的括号连着写、开头末尾带标点、不含依据的不动)、半角逗号的用例;结构化组合加上两种括号,中间只有依据的也算;流式
渲染、随机语料加上几种括号和波浪线;审批质询、核查 agent、反方审稿、双盲复算各补一个端到端用例。15 处改动逐一去掉实现,
相关测试都变红(第 2 轮的 7 处也重跑过,照样变红)。另在测试之外:复核用的抽查脚本 6000 份 × 6 个种子只剩写作者自己
写成「212,,2」的一份;结构化组合 29.4 万份、随机含依据的 1.6 万份和基线比没有新并成的数,流式逐字、随机分块都等于整篇
渲染。
2026-10-12 12:24:14 +08:00
ThearchyHelios 45987748ef K3b 复核修正(第 3 轮):VALUES 当一张表用又读到遮罩列的整份遮,列名按方言认;sys 库读 performance_schema 的 ps_* 函数进认不全的名单
- VALUES(复核 major,基线就有):_function_columns 按 Postgres、SQLite 的习惯把 VALUES 的列记成 column1…,MySQL 8.0
  实际叫 column_0、column_1…(真库实测),派生表、CTE、LATERAL 里的 VALUES ROW 取了号码,外层按名字取、照原名带出时
  对不上,号码原样交给模型、落库;MariaDB 拿第一行的值当列名,同样对不上。现在 VALUES 当一张表用(FROM、JOIN、
  LATERAL 里的,WITH 里的一段,UNION 的一支,整条就是它,_values_table)又读到遮罩列(含派生出来的名字)的,不论哪个
  库、列怎么起名,整份遮。套了括号的 VALUES(整条、UNION 的一支)解析器原样挂在树上、不在任何 SELECT 的 FROM 里,以前
  哪个库都没认(Postgres 也漏),一并收口。IN / ANY / EXISTS、标量子查询里的 VALUES 不算:值经外层的表达式带出,或者
  只用来筛行
- 列名按方言认(_values_names,_function_columns 用同一套):Postgres、SQLite 叫 column1…,MySQL 叫 column_0…(一行
  ROW(…) 摊开数列),写了列名表的按列名表(Postgres 的列名表只改前几列),MariaDB、Oracle 认不准记 None。整份遮时只收
  值取自遮罩列的那几列的值(_values_taken 按位置):以前 VALUES 按表值函数认,出的每一列(连同 key、value、别名)都算,
  同一行里没碰遮罩列的区域收成遮罩值,在同一次核查里处处被换掉
- 行为变化:Postgres、SQLite 的 LATERAL VALUES 读到遮罩列,以前只遮那几列,现在整份遮(多遮,同一份里的编号也遮);
  原来「VALUES 里直接写遮罩列只遮那一列」的用例改成整份遮
- sys 库(复核 minor):INDIRECT_NAMES 的 sys 规则补上按 sys.ps_ 开头认的函数(ps_thread_trx_info 交回线程的事务里执行
  过的语句原文,ps_thread_stack 同类;只交回编号、开关的 ps_thread_id、ps_is_* 一并算,多遮;不写 sys. 的要默认库就是
  sys 才执行得了,不认,同 sys 的视图)。工具边界和可验证报告共用这一份名单,报告不再判它没碰遮罩列、照常附带快照
- 质询模块说明的已知局限同步

补测试:VALUES 当一张表用 × MySQL(派生表、LATERAL、CTE、外层改名、JOIN LATERAL、LATERAL 子查询、标量子查询、派生
出来的名字、UNION 第一支、整条套括号、派生表里 UNION 的一支)、MariaDB 两种、Postgres 三种、SQLite 一种;三种没读到
遮罩列的对照;四种写法的收值(号码收、区域不收)和 (表达式).* 之后不按位置收;_function_columns 四种列名、两种认不准;
核查 agent 真实查询工具上的端到端(复核点名的三条 MySQL 写法);sys.ps_* 六种写法、三种不该误伤的对照、可验证报告
两种、端到端一条。逐项去掉实现(整份遮、MySQL 的起名、按位置收值、当一张表用的位置、整条就是 VALUES、VALUES 不按表值
函数认、摊开 ROW、列名表、_function_columns 的起名、(表达式).* 之后停、MariaDB / Oracle 认不准、sys.ps_),对应的测试
都变红。另在 MySQL 8.0.46、Postgres 18 临时实例上经核查 agent 的真实查询工具复跑 28 条(MySQL 的 VALUES 14 条、对照
4 条、sys 函数 4 条,Postgres 的 VALUES 6 条):交给模型的、落库的、通用工件接口给的都没有号码;2b5989e 上其中 16 条漏
号码(sys.ps_thread_stack 在只读事务里执行不了,前后一样)。
2026-10-12 12:18:34 +08:00
ThearchyHelios ce36aabe92 合入期 5 验收修复 M3:可验证报告核对正文显示的文字与引用的值一致——文字取值逐字比对,数字按服务端的显示规则(千分位、小数位、百分数、万 / 亿换算、单位)核对,认不出的格式判无法核对而不是通过;紧挨数字的不可见字符、缩进挤出屏幕的列表一并判不通过 2026-10-12 11:42:06 +08:00
ThearchyHelios 9344cb9232 M3 复核修正(第 2 轮):紧挨着核对过的数字的字按码点分类收紧,列表缩进有上限、表格只排有格的列
复核发现「紧挨着」只认一部分字:隔着 DEL、U+FFF0 这类显示时不占位置的字补一位数,或者前面加一个 U+2010 这类和负号
长得一样的横线,读的人看到的数已经变了,页面仍「全部通过」;伪造的列表缩进写成十万级,核对通过的数字被挤出屏幕仍判
通过。

- 紧挨着的字按码点分几类(kindOf):数字;负号(横线类标点全算、波浪形的除外,减号类符号,Unicode 易混字符表里映射到
  「-」「ー」的字,在 Chrome 常用字体里逐个画出来是一道横线的字);分隔(半角逗号、句点和易混字符表里长得一样的窄标点,
  全角的不算);不占位置的格式字符、默认不显示的字、变体选择符照旧跳过;控制字符(制表、换行等空白除外)、未分配和私用
  区的字、孤立的代理项、组合附加符号、可选连字符、发丝空格、对象替换符和在 Chrome 里量出来几乎不占宽度的几个字算「说不
  准」:隔着它们照样往外找数字、负号,找不到的也判无法核对、原因另写。只跳过不标记的话,叠在空格上的组合横线、Chrome
  画成短横的 C1 控制字符照样能在数字前面显示出一个「负号」
- 同一句里有从右往左书写的文字(希伯来文、阿拉伯文这类)和方向控制字符一样判无法核对:Chrome 里实测,一个希伯来字母
  就能把逻辑上在数字后面的「5」显示到数字紧前面(读作 574,613.65元),按字的先后看不出来
- 代理对只在真成对时拼成一个字:不成对的前半不再把后面的「1」吞进去;分在两个文字片段里的一对页面上照样显示成数学字母
  区的数字,也拼起来看
- 列表缩进最多显示 8 级,负数按 0 级:十万级的缩进把数字挤到 140 万像素以外,负的连滚动都滚不到;表格只排有格的列:列号
  写成十万时不再建几十万个空格、把那一格挤到右边,写成 2^32 时整段核对也不再出错。核对和显示共用 listItems / tableCells
- 真数据(两份数据目录里 39 份报告文档、1591 处数字和取值、45 个列表、70 张表格)里新旧规则都一处没有触发
- 测试:test_bundle_shown.py 补 DEL、U+FFF0、U+2010、U+2013 等紧挨着的写法,说不准的字、从右往左的文字、缩进和列号的
  用例,逐项去掉实现都会变红;check-bundle 在经营月报上补四种紧挨着的伪造(从右往左那条先量出「5」确实显示在数字紧前
  面),缩进写成十万级和负数(宽屏、窄屏)、表格列号写成十万的伪造
2026-10-12 10:52:09 +08:00
ThearchyHelios 2b5989ef00 K3b 复核修正(第 2 轮):VALUES、行构造器里的 (表达式).* 和交回复合类型的表值函数整份遮;记着别的会话 SQL 的视图、不只读表结构的 SHOW 和 EXPLAIN 整份遮
- VALUES 里的 (表达式).*(基线就有):Postgres 在 VALUES 里同样把复合类型、记录展开成好几列,LATERAL VALUES 按表值
  函数认时一个元组项只算一列,column2 起的号码原样交给模型、落库。现在 (表达式).* 不只看选出项:树上任何位置的(VALUES、
  行构造器、函数参数里的),以及 VALUES 里的 s.*、(s.*),读到遮罩列就整份遮。(表达式).* 的值只按那个表达式认,不再把 *
  当成展开 FROM 的来源(没设遮罩的复合类型 (p.address).* 以前因此被整列遮)
- 同一类、复核没点到的:Postgres 里 FROM 中交回复合类型的表值函数。unnest 的数组元素是复合类型时展开成各个字段
  (unnest(ARRAY[p.card]) 出 addr、tel,写了列名表也只改前几列的名字),不认得的函数只写了列名、没写类型的列名表
  (jsonb_populate_record(NULL::contact, …) AS r(a) 后面的字段照原名出),读到遮罩列的整份遮(_spreads_record);拆普通
  数组的 unnest 也一样(元素类型 SQL 里看不出,多遮)。写了带类型列定义的照写的认
- Oracle 的 JSON 点号、对象类型的属性(t.profile.tel,解析器读成库、表、列):前面几段也算列,以前只认 tel,号码原样交出
- 记着别的会话 SQL 原文、被锁那一行的值的来源补进 INDIRECT_NAMES(工具边界和可验证报告共用一份):MySQL 的 INNODB_TRX、
  OPTIMIZER_TRACE,MariaDB 的 INNODB_LOCKS、QUERY_CACHE_INFO,Oracle 的 V$ / GV$ / X$ 一律算、AWR 历史、SQL 调优集和
  调优顾问、固定的执行计划和 SQL Profile、闪回事务,DBMS_XPLAN、DBMS_SQLTUNE 这类出报告的包
- 不是查询的语句(_reads_beyond):只读表结构的 SHOW(表、列、索引、库、表状态、建库语句)、DESCRIBE / EXPLAIN 一张表
  照常,别的 SHOW(ENGINE INNODB STATUS、BINLOG / RELAYLOG EVENTS、REPLICA STATUS、WARNINGS、PROFILES、CREATE TABLE /
  VIEW、TRIGGERS……)、EXPLAIN 一条语句(MySQL 把只有一行的表的值代进计划里的条件,8.0 实测)不逐个认,一律整份遮。
  SHOW CREATE TABLE 不算只读表结构:对视图交回的是视图定义(WHERE 里写死的号码,8.0 实测),以前因为写着 TABLE x 本来
  就整份遮,照旧;SHOW TABLE STATUS 以前同样因此整份遮,现在照常
- 质询模块说明的已知局限同步;补一条:视图、函数、约束定义里写死的字面量(information_schema.views、pg_views、
  pg_get_viewdef()……)不认,照常交给 agent

补测试:VALUES × LATERAL / JOIN LATERAL / CTE / FROM / 匿名 ROW / 整行 / 表和子查询的 *,函数参数里的 (表达式).*,
没碰遮罩列的对照;交回复合类型的表值函数九种写法和三种照旧按名字认的对照;Oracle 点号四种和两种对照;十九种视图和包、
十三种 SHOW、九种 EXPLAIN、十二种只读表结构的对照;核查 agent 真实查询工具上的端到端(Postgres 的 VALUES 展开、MySQL
的 INNODB_TRX 和 SHOW ENGINE INNODB STATUS)。逐项去掉实现((表达式).* 的取值、树上的 Dot、VALUES 里的 *、新名字、
_reads_beyond、只读表结构的豁免、SHOW CREATE TABLE、unnest、只写列名的列名表、三段名),对应的测试都变红。另在
Postgres 18、MySQL 8.0 临时实例上经核查 agent 的真实查询工具复跑复核的复现和上面这些写法(44 条),交给模型的、落库的、
通用工件接口给的都没有号码;169c500 上其中 23 条漏号码。
2026-10-12 10:50:40 +08:00
ThearchyHelios 3d85a43301 M1 复核修正(第 2 轮):去掉依据之后按真正挨上的两个字判粘连——两个只装着依据的括号连着写、数字包着粗体时,两边的数不再并成一个;依据标记的「]」不当数字;一片里只补一个空格
渲染(evidence._tidy):
- 上一轮判粘连只看原文里紧挨着括号的两个字(往后只越过 [[see:]])。两个只装着依据的括号连着写(「[[v:Q1.r0.销售额]]
  ([[see:Q1]])([[see:m:gmv]])2 月」)、数字包着粗体(「**[[v:…]]**([[see:Q1]])2 月」,组装文档时星号又被去掉)
  时,去掉之后真正挨上的是两边的数,读者看到「4,919.52 月」「212212 笔」,compose_doc、verify_doc 都不报。现在先把依据
  标记和只装着依据的括号(套着的、几个连着写的都算)认成渲染出来什么都没有的一段(_voids),连着写的几个并成一组,外面的
  停顿标点、空白照原规矩收拾;所有切口去掉之后,再往两边越过去掉的字、依据、粗体斜体的星号和下划线,找真正挨上的两个看得
  见的字,会读成一串的补回一个空格(_glue)
- 依据标记的「]」越过去、不当数字:「华南居首。[[see:Q1]]([[see:Q2]])2 月」不再多出空格,「[[see:Q1]]([[see:Q1]])
  2 月」不再以空格开头
- 同一片里最多补一个空格:「212,[[see:Q1]],[[see:m:gmv]],5」→「212, 5」(上一轮是两个空格);挨上的是写作者自己留着的
  空白的,不补
- 半角标点后面补空格并进同一条规则:只在下文是数字、字母或渲染出字的标记时补(「a,[[see:Q1]],b」→「a, b」),下文是中文
  的照写作者原样
- 流式渲染把末尾的星号、下划线也攒到后面的字来了再放,和整篇渲染判得一样
- 粗体把括号包在里面时(「**[[v:…]]([[see:Q1]])**2 月」)空格落在收尾的星号前面:前端行内 Markdown 照样认这对星号,
  组装出的文档去掉了包着标记的星号,读者看到的一样;挪到星号外面得先知道这对星号从哪开始,流式渲染的一块里不一定有。
  测试里用严格的 xfail 写明这条已知限制

测试:两个括号连着写(全角半角混用、套着的)、粗体在括号前、后、里面、下划线和单个星号、依据的「]」、一片只补一个的用例;
结构化组合(数、依据、只装依据的括号、空白、星号、标点)逐份查写作者隔开了的两边没有挨上、没多出连着的两个空格,逐字和
每 4 个字分块渲染都等于整篇渲染;审批质询、核查 agent、反方审稿、双盲复算各补一个端到端用例。11 处改动逐一去掉实现,
相关测试都变红。另在测试之外抽查:结构化组合 15.9 万份和基线 58a6759 逐份比数字串,只剩复核说可以不管的「),5」这类半角
标点紧贴数字的写法;随机 8000 份不含依据的文档,内容哈希、渲染结果和基线逐份一致;随机 1.6 万份含依据的文档流式和整篇
一致。
2026-10-12 10:35:13 +08:00
ThearchyHelios 6d4f452f36 M4 复核修正:规范化后不同的行带上所在的子句,ON 挂在 JOIN 那一行的末尾
复核发现上一版把 ON 单独排成一行后,「规范化后不同的行」丢了关联条件的上下文:只差一个关联条件时那一栏只剩
「AND refunds.state = 'done'」,有两个 LEFT JOIN 时看不出是哪个 JOIN 的,也看不出是关联条件还是筛选条件;两边 JOIN
的表不同时,两边都有的那一行「ON」被 difflib 当成相同的行吃掉,表名下面直接跟条件;关联子查询换成两张表时也一样。
这几种都只给兜底说明,审核人全靠这一栏逐行对照。

- sql_semantics:ON 挂在 JOIN 那一行的末尾(表名、子查询的右括号后面),条件各占一行缩在下面,仍比 WHERE 的条件
  深一级(ON 里和 WHERE 里的同一个条件不当成同一行)。text_diff 里不同的行前面带上它的各级上级行(排在前面、缩进比
  它浅的最近一行,一层层数到顶格),以「  」开头、两边相同,只为标出不同的行在哪个 WHERE、哪个 JOIN 的 ON、哪层
  子查询里;截到 40 行时末尾落单的上级行不留。只动排版:拿测试里的 SQL 语料(990 对,4 种方言、有无表结构、两个
  方向)逐对比过,equivalent、items、fallback、level 和改之前完全一致,有没有不同的行也一致,不再有单独一行的 ON。
- SqlDiff 的约定多一条:「  」开头的是上下文行,只跟着不同的行出现。test_sql_semantics 的 check_shape 原来只认
  「- 」「+ 」,改成也认「  」、并要求最后一行是不同的行(374 个用例只因这一行断言变红,判定和说明都没变)。
- 审批卡(DualCheckApproval):两栏各自只放用得着的上级行(这一边不同的行往上数,缩进更浅的最近一行,一层层往上),
  只有另一边用得着的不放;上级行不高亮、用淡一级的字色(同原文栏里没标的行),标题后面说明「淡色的行两边相同,标出
  所在的子句」,悬停也是这句;这一边没有不同的行时照旧说没有,老工件没有上级行,照旧。lineDiff 注释里「条件并成
  一行」的旧说法改正。
- 测试:test_sql_text_diff 里 ON 那条的期望改成带 JOIN 的上级行,补两个 LEFT JOIN 只差一个关联条件(两个方向)、
  两边 JOIN 的表不同、关联子查询换成两张表、同一个条件在 ON 和 WHERE 里、顶格的行不带上下文、40 行截断几条;去掉
  上级行、去掉「ON 挂在行末」、去掉截断后的清理,各自让对应的用例变红。
- 夹具 dual_check.json 按 test_dual_check_fixture 的办法重新生成(只变 text_diff),另加 join_condition 变体(只差
  一个关联条件、只给兜底);check-verify 查两栏的上级行(WHERE)列在不同的行前面、不高亮、标题有说明,join_condition
  下原流程一栏是带 ON 的 JOIN 加缩进的条件、盲算一栏不画只有原流程用得着的上级行,深浅色对比度、390px 不溢出。
2026-10-07 01:34:59 +08:00
ThearchyHelios ab2173c665 M3 复核修正(第 1 轮):单位带尾随空白的真报告不再被判不通过;运行输入也核对显示文字;出处面板的长整数一位不差;核对通过的字要原样显示在正文里
- 显示文字比对两边都只去首尾空白:口径卡单位写成「元 」(单位输入框不去空白、指标节点照存)时,服务端照原样接在数
  后面,正文是「74,613.65元 」。以前只去正文一边,原样导出的真报告被判不通过,出处面板里两行字看起来一模一样,读者
  会以为文件被改过
- 运行输入([[i:…]])不随文件附带,照旧无法核对;正文显示的字拿引用的值按服务端的写法(不分组、不接单位、可带换算)
  重新渲染比对,只改显示文字(统计周期、地区这类)的判不通过
- 出处面板:DECIMAL 落成数字文本的格子,「快照里的值」照快照原文写;快照那一行的数照 JSON 原文写。超过 2^53 的订单号
  不再在「通过」的条目里和一位不差的「引用的值」并排、自相矛盾
- 正文的排法核对和显示共用一套函数(unitPlan / flowsOf / checkLayout),读的人看到的数要就是核对过的那个:伪造的链接
  把数字塞进不显示的链接地址时,整句按未排版的原文显示;正文里根本不显示的(分隔线里、表格行列之外的格)服务端导出时
  不会出现,判不通过;紧挨着补数字、负号(隔着零宽字符、显示时藏起来的排版标记也算)或同一句里有方向控制字符的判无法
  核对,两个数之间的区间连字符照常通过。正文按片段本身找核对结果:文字片段冒用数字的 id 借不到「通过」(以前会显示成
  绿色下划线的可点数字)。真数据 71 份报告文档、3266 处已解析的数字和取值里,新规则一处都没有触发
- 「无法核对显示文字」的几条原因改成读者看得懂、知道怎么办的说法,不再提 BigInt、正则、ASCII
- check-bundle:真导出上断言没有「无法核对显示文字」(显示规则漏了真实写法时这里先报);订单号连同快照那一行一位不差;
  新样本页 formats-unit(单位带尾随空白,原样导出全部通过);经营月报上链接藏数、补一位数、整段改成分隔线、文字片段冒用
  数字的 id 四种伪造各自断言
2026-10-07 01:34:32 +08:00
ThearchyHelios c97390511e M1 复核修正(第 1 轮):去掉只装着依据的括号不再把两边的数字粘成另一个数;括号去掉之后外面照一个依据再收拾;换行不算句末;核查 agent 的理由按渲染后的正文判有没有给;反方审稿收尾超时按时长上限说
渲染(evidence._see_cuts):
- 括号是两边唯一的分隔、去掉之后数字字母会粘成一串的(「[[v:Q1.r0.销售额]]([[see:Q1]])2 月」渲染成「4,919.52 月」,片段
  本身没错、月份不算裸数字,哪项核对都查不出来),括号换成一个空格;半角逗号夹着依据、去掉后一个之后同理(「212,5」→
  「212, 5」)。切口可以带替换串,连成一片的按并集换
- 只装着依据的括号去掉之后,整对括号当一个依据看外面:括号前悬着的停顿标点(「华南居首,([[see:Q1]])。」)、到句末只隔着
  的空白、夹在两个分隔标点中间的后一个,和不带括号的写法收拾得一样
- 依据后面到句末标点只隔着空白的,空白一起去掉(「华南居首 [[see:Q1]] 。」不再剩「华南居首 。」)
- 换行不再算句末:列表项末尾的「;」、软换行处连着两个分句的「,」是写作者自己的,依据挂在它后面正合规矩,照旧留着(上一轮
  把它们删了)
- 没有依据的文档渲染、内容哈希不变(随机两万份和基线逐份比过);流式分块输出仍等于整篇渲染
- 依据写在「依据」「见」「取自」「来源」这类引出语后面时引出语悬空,渲染时去不掉(引出语在依据前面,流式渲染得攒住每个可能
  是引出语的字):测试里改成严格的 xfail 写明已知限制,不再把验收里的缺陷钉成预期;核查 agent 遇到这种写法记一条日志

核查 agent(verify_agent):
- 有没有给理由按渲染出来的正文判:只写了依据([[see:Q1]]、「([[see:Q1]])」)显示出来是空的,占位词后面挂着依据的显示出来
  只剩占位词,都算没给。以前按原文判,标记里的字也算,理由文档照样交回:审批卡画出一片空白,判「被推翻」的因为引用到了 Q1
  被当成有出处、出具降档;封存之后的质询回答只剩系统说明却记成回答成功
- 「无」「暂无」「none」只在理由是另一个结果的说明时(双盲复算、反方审稿)算没给;结构里只有理由一项(审批质询)时理由就是
  回答,「无。」是一个回答,不再报「模型没有给出回答内容」让提问人重问、再花一次钱
- 收尾超时的原话收成常量 SETTLE_TIMEOUT,调用方据此换成自己的说法

反方审稿:交结论那次超过时长上限的句子,理由写「反查未完成:已达上限(每句时长上限 N 秒)」(收紧过的说运行时长上限将至),
和时间线上的 challenge_limit 一致,不再把核查 agent 的内部原话摆上审批卡。

测试:补节点级用例钉住收尾超时之后两个节点对外的说法(challenge_limit 的 limit、claims_skipped、cause,双盲复算的无法复算
原因和 dual_check_limit,收紧过的两种);理由只有依据、占位词挂依据、「无。」作为回答的各层用例。每处改动都做过去掉实现、
测试变红的验证。
2026-10-07 01:19:26 +08:00
ThearchyHelios 169c500526 K3b 复核修正(第 1 轮):数值格按位置认、(表达式).* 带外括号也整份遮、同名 CTE 按作用域认、认不全的写法名单合一、整份遮不再把每一格收成遮罩值、证据摘录分段遮、反方最后一道关只看正文
- 同名列(回退):column_types 按列名记,同名的几列只留得下后一列的类型,文字的备注被当成数值列,号码不再按值换、
  原样交给模型并落库。现在只在列名唯一时按列类型认数值列(_typed_numbers),同名的按格子本身认:整数、小数才算数,
  写成字符串的数按文字照换、记进 masked_cells;列名唯一、列类型是 number 的 NUMERIC / DECIMAL 字符串照原样
- (表达式).* 外面套了括号、起了别名(((p.card).*)、(p.card).* AS x,Postgres 实测照样展开):以前只遮第一列。选出项
  先去掉外括号、别名再认(_unwrapped),(s).*、(s.*) AS x 照旧当 s.*
- WITH 里和数据源里的表同名的一段(WITH sales AS (SELECT * FROM sales WHERE …)):不带 RECURSIVE 的一段看不见它自己和
  排在后面的段(_scoped_cte),同名的按数据源里的表认;以前认成它自己,* 展开什么都不带出,整行引用的号码原样交出
- 认不全的写法:和可验证报告合成一份名单(masking 的 OPAQUE_SQL、INDIRECT_FUNCTIONS、INDIRECT_NAMES,bundle 引用后另加
  系统 schema),补上 MySQL 会执行的注释、U&"…"、pg_stat_get_activity、逻辑解码、MySQL 的 column_statistics /
  general_log / PROCESSLIST / performance_schema、MariaDB 的 column_stats、Oracle 的直方图 / *_tab_columns / V$SQL 一族;
  去掉注释再认一遍(函数名和括号之间夹注释);去掉 DuckDB、SQL Server 的函数(query 误伤同名 CTE、字符串);没有 * 的
  一支选出项和结果列数对不上的、Postgres 解析不了的整份遮
- 整份遮只收确定取自遮罩列的那几列的值(_derived_masks 的 collect,报告快照收值同一规则):以前每一格都收,区域名、
  列名在同一次核查里处处被换掉,封存的 SQL 也被改写。快照另记 masked_whole,格子引用照实说写法认不全
- 证据摘录分段遮(inquiry.ExcerptMask,judge.prepare 的 mask 取代 kinds):SQL 照封存 SQL 的办法换,范围阈值、LIMIT 照
  原样,和遮罩列做等值、IN、LIKE 比较的字面量不论收没收到都换;格子逐格遮,数值格照原样;质询把被问的那句(连同表格)
  当作已经摆在眼前,摘录、查询工具、回答里都不换
- 反方审稿:理由文档的目录列名照快照换(同质询的 _renamed_entry),agent 自己 SQL 里照原样留着的阈值、行数在正文里
  不换(_shown),最后一道关只看正文和原文,不再把 agent 起的列名、阈值当成露了遮罩值整句改判

补测试:同名列一文一数、DECIMAL 字符串(含端到端引用取值),外括号 × 复合类型 / 记录函数 / 匿名 ROW × 最外层 /
UNION 前后支 / 和 * 写在一起,同名 CTE 四种写法和 RECURSIVE 对照,十七种认不全的写法和三种不该误伤的写法,整份遮后的
收值与封存 SQL,Postgres 解析不了的整行引用,摘录的阈值、数值格、IN / NOT IN 字面量、被问那句(反方、质询各端到端),
反方别名里的阈值(经受住、被推翻各一条)、找某个值的别名、正文里的阈值。逐项去掉实现,对应的测试都变红。
改了文件所有权以外的 bundle/build.py:_OPAQUE_SQL、_INDIRECT_READS 改为引用 masking 的同一份名单(另加系统 schema,
覆盖面不小于原来)。
2026-10-07 01:09:36 +08:00
ThearchyHelios 2bb14758bd M3 补测:没有 BigInt 的浏览器上数字的显示文字判无法核对,文字取值和其余核对项照常
较早的浏览器没有 BigInt(或不认 \p{…})时,显示规则的渲染走不了:只有数字的显示文字判「无法核对显示文字」并写明
原因,整段核对脚本不能因此出错。node 里载入时把 BigInt 遮掉验证这一点;跳过原因的措辞照文案规范改。
2026-10-07 00:21:24 +08:00
ThearchyHelios 6ff9d7155b M3 可验证报告核对正文显示的字:按服务端的显示规则把引用的值重新渲染、逐字比对
验收发现:页面只核「引用的值 = 口径卡的值或快照的格子」,不核正文显示的字。只改显示文字(74,613.65元 改成
84,613.65元、华南改成华东)、引用值不动、重算全部哈希的伪造页照样「全部通过」,读者看到的数和核过的数不是同一个。

- verifier.js 移植 engine/evidence.py 的 render_number / render_cell:千分位、不分组、小数位(含负数位)、比率显示成
  百分数、万 / 亿换算、pct、int、.N、单位后缀、负数、0,文字压成一行(按 Python 的空白),竖线换成 ¦,ISO 日期时间按
  文档记下的单元格显示规则版本。数照 Python 的 Decimal 算:系数用 BigInt,ROUND_HALF_UP,浮点取最短写法,整数取 JSON
  原文(新写的 numberTokens 读出每个数的原文,超过 2^53 的订单号一位不差)
- ⑤ 在引用的值对上之后再核显示文字(只去掉首尾空白):对不上判不通过,原因点名「正文显示「X」,引用的值是「Y」」,
  数字另写按显示规则应显示的字;渲染不出来的(不认识的换算、显示规则版本、对象和列表、非 ASCII 数字、缺 BigInt 的
  浏览器)判「无法核对显示文字」,不判通过。出处面板改成列出按显示规则应为的字,一致时注明,页脚说明同步
- 在 node 里载入时(没有 document)交出核对用的纯函数,tests/test_bundle_shown.py 拿服务端的渲染逐条对照约八万个
  组合,并在 node 里整份核对:经营月报样本每种格式原样通过,只改显示文字的伪造逐个变红并点名
- 样本页加经营月报(formats);check-bundle 在真导出上只改一个数字(有文字取值时也改一个)的显示文字,在经营月报上
  改文字取值、数字、换算成万的口径卡引用,各自变红并点名,原样导出照旧全绿
2026-10-07 00:17:22 +08:00
ThearchyHelios 5b18eab310 M4:双盲复算「规范化后不同的行」每个条件一行,两边相同的条件不再被标成差异
验收时审批卡把两边完全相同的日期区间标成了差异:_text_diff 逐行比两边 sqlglot pretty 的输出,pretty 按宽度决定
换不换行,两个合取的 WHERE 排成一行、三个的拆成三行,同一个条件在两边落在不同的行上。原文 SQL 都是单行时原文栏
一行都不标,这一栏是唯一逐行指出差在哪的地方,审核人会以为统计期也不一样。

现在逐行比之前两边按同一套排版写(_diff_lines / _line_generator,按方言给 sqlglot 的生成器加一处改动):WHERE、
HAVING、ON 的合取式每个条件一行,各层子查询里的也一样;条件本身是 OR 连起来的一律加括号(单独一个和与别的并列时
写法一样);ON 照 WHERE 的样子单独一行、条件缩进在下面(ON 里和 WHERE 里的同一个条件不当成同一行)。比的时候
不看条件前面的 AND(一边多出来的条件排在最前面时,另一边同一个条件只差在有没有 AND),列出来的仍是各自原样的行。
只动排版:判不判等价、差异说明不变(拿测试里的 SQL 对照语料按三种方言、有无表结构逐对比过,items、equivalent、
fallback、level 全部一致),sql_semantics、sql_equivalence 原有测试全过。

前端夹具 dual_check.json 里的 text_diff 是服务端的真实输出,按 test_dual_check_fixture 的办法重新生成(只变了
text_diff);check-verify 的对照行一节另查「只差结束日期时开始日期不标」「多出的两个条件各占一行」。
2026-10-07 00:12:31 +08:00
ThearchyHelios 261636d3b7 依据括号渲染测试的说明照收紧后的规则写:几个依据之间的分隔标点只在括号里、句末前一并去掉,两边都是字时照旧留着 2026-10-07 00:10:28 +08:00
ThearchyHelios 7de4aa1550 收尾超时一律记时长上限:循环里先查满了次数、再在交结果时超时的,limit 也是 timeout_s
上一个提交在模型自己停了查询时补上了 timeout_s,但循环里先撞上别的上限(查询次数、轮数)、交结果那次又超时的,仍记
先撞上的那个:质询会显示「未能回答(已达查询次数上限):回答超过时长上限…」,自相矛盾;双盲复算会说盲算查满了次数、
反方审稿说「每句最多查询 N 次」——查满了照样能交结果,没交成是因为超时。现在交结果那次超时一律记 timeout_s,和报错
原因一致(运行时长上限将至、时长被收紧过的,双盲复算、反方审稿照旧按 timeout_s 说成运行时长上限将至)。金额被拒的
收尾不变,仍报循环里先撞上的那个(test_verify_truncation 钉着它,原因见那里)。
2026-10-07 00:09:46 +08:00
ThearchyHelios 735dc32052 核查 agent 的依据照写作者的规矩挂在句末;收尾超时照实记时长上限;占位词不当理由
- 交结果的提示(VERIFY_FINAL_PROMPT,双盲复算、反方审稿、审批质询共用)以前写「提到某次查询时写 [[see:Q1]]」,模型就
  把它写进括号、句中;反方审稿的系统提示自己也把它写在括号里(「([[v:Qn.rX.列名]] 或 [[see:Qn]])」)。改成和写作者
  一致:依据挂在句末(「……结果一致。[[see:Q3]]」),说明它渲染后不显示,不要放进括号、句中或「依据」「见」「取自」
  后面。已经写进括号的由上一个提交的渲染收拾干净
- 模型自己停了查询、收尾那次调用超过时长上限时,limit 以前是 None:质询的报错写着「回答超过时长上限(180 秒)」,
  answer.limit 却是 null,界面少了「已达时长上限」;双盲复算、反方审稿把它当成别的原因,审批卡上照出内部原话。
  现在记 timeout_s;循环里先撞上的上限照旧优先(同金额被拒的收尾),结构化输出、退回普通调用两条路都一样
- 交回的理由是空的、只有标点,或者只是一个占位词(验收里盲算交了字面的「placeholder」,审批卡照着显示):当没给
  理由,不组装理由文档。反方审稿的记录里理由为空,审批卡照旧写「反方没有给出理由」;质询照「模型没有给出回答内容」
  报错;双盲复算的对照工件里说明为空

测试:交给模型的提示(含三处调用方的系统提示、任务)里不再示范括号里的依据;写进括号的依据在理由文档里渲染干净、
仍挂在句子上;审批质询的回答、反方审稿的理由、双盲复算的说明端到端都没有空括号;收尾超时 limit 为 timeout_s(核查
agent 和质询接口、事件里都是);占位词的理由三处都不当理由。
2026-10-07 00:05:17 +08:00
ThearchyHelios d0e9c6d1e7 依据标记写进括号、夹在标点中间时渲染不再断句:_tidy 把依据周围留下的括号、标点一起收拾掉
真实模型验收里,核查 agent 把 [[see:Qn]] 写进括号、句中(「结果一致([[see:Q3]],原报告 SQL 未按 status 过滤)」
「合计 [[v:Q1.r1.金额合计]]([[see:Q1]])」),see 渲染成空,正文留下「(,原报告…)」「()」,审批质询的回答、反方审稿和
双盲复算的理由都这样,正式运行里随封存永久留着。_tidy 以前只去掉 see 前面的空白。

- 括号(全角、半角,一对里混用也算)里只有依据的,连括号一起去掉,括号前的空白照依据前的空白处理
- 依据在括号开头、后面跟着「,」「、」「;」「:」的,标点一起去掉;在括号末尾、前面紧挨着标点的同理
- 后面就到句末(句末标点、换行、全文结尾)、前面或几个依据之间有停顿标点的,去掉这些停顿标点(冒号不动:它引出的
  可能是下一行的表格);依据夹在两个分隔标点中间的,去掉后一个;依据后面、句末前面的标点是写作者自己的,不动
- 两边都是字时,依据之间的分隔标点可能就是分句的标点,按它断开、照旧处理
- 依据标记本身留在原文里:句子照旧挂着那条依据(unit.see、cites 不变),只是周围的字不显示
- 流式渲染把末尾的括号、分隔标点也攒到后面的字来了再放(_TRAILING_SEE),分块输出拼起来仍等于整篇渲染

不含依据的括号、没有依据的文档一个字不动:两份文档的内容哈希和改动之前(58a6759)一致,对照基线随机渲染 6 万段,
改了的都是依据挨着括号、分隔标点的,不含依据的 2 万段逐字相同。封存过的文档复核不重新收拾原文,照旧通过。
2026-10-07 00:04:41 +08:00
ThearchyHelios 76cb4d2845 M4:db_schema 认整个写成一个字符串的 JSON 数组的表名
验收时盲算、反方审稿的模型把 table 写成 '["orders", "stores"]'(一个字符串),工具只按逗号拆,回「中没有 ["orders"」
「中没有 "stores"]」,两张表明明都在,模型重试几轮才改成不传 table。表名参数的解析抽成 _schema_tables:元素都是
字符串的 JSON 数组(整个作为一个字符串传进来,或者数组里的一项)按数组认,其余照旧按逗号拆;不是 JSON 的
([orders]、单引号)、元素不是字符串的照旧。只动 db_schema 这一段,查询工具不动。
2026-10-07 00:02:15 +08:00
ThearchyHelios df6b0dd3ae M4:触发历史的翻页游标写成 Z 结尾,照原样拼进查询串也能翻页
next_before 原来是「2026-10-06T14:37:03.376320+00:00」,照接口说明「原样传回」拼进地址时「+」被查询串解成空格,
回 422「翻页位置需要是接口返回的 next_before」。界面用 URLSearchParams 编码不受影响,照说明写脚本、做集成的会撞上。
现在游标是 UTC 的 Z 结尾、精确到微秒(和运行列表的游标一个写法,放进地址不用编码);解析时 Z、+00:00、被解成空格
的「 00:00」(老的游标照原样拼进地址时就是这样)都认,不是时间的照旧回 422。check-schedules 的假接口照服务端用记录
时间作游标,另加一节:老服务端给 +00:00 的游标时,页面编码成 %2B 传回、第二页接得上、不重复。
2026-10-07 00:02:15 +08:00
ThearchyHelios e410d2a6a7 M4:单个工作流接口的 run_count 和列表接口按同一个函数算
验收时 GET /api/workflows/{id} 的 run_count 恒为 0,同一时刻列表接口里同一个工作流是 2 次、4 次:只有列表接口按
Run.workflow_id 计数,详情、保存(PATCH)、恢复旧版、新建、复制直接返回数据库对象,落在 WorkflowOut 的默认值 0 上。
计数抽成 _run_counts,列表和返回单个工作流的接口(_workflow_out)都走它,次数一致;新建、复制出来的工作流照实是 0。
2026-10-06 23:56:31 +08:00
ThearchyHelios 79c9559079 K3b:质询模块说明的遮罩一节补上派生来源里分档、外层分组的标签列不算 2026-10-06 23:25:59 +08:00
ThearchyHelios c8127852a7 K3b:新测试文件去掉没用到的导入 2026-10-06 23:25:16 +08:00
ThearchyHelios 6ba4d9f099 K3b:反方审稿查数据源类型时直接用整份报告用到的源,不再逐件取工件
claim_sources 已经认全了查询的、合并查询合并的、指标输入取自的、表和字段的数据源,_kinds 不必再把目录里每件工件都取一遍。
2026-10-06 23:23:40 +08:00
ThearchyHelios 4b461e86e6 K3b:说明写清工具边界不按值换数值格的取舍;LATERAL 的 ON 条件里看得见它自己连的来源
整行引用找列时,在连接的来源本身里(LATERAL)只看得见前面的来源,在 ON 条件里连这个来源自己也看得见(_visible_sources),和 Postgres 的作用域一致。质询的已知局限补上:没设遮罩的列里存成数的号码工具边界不按值换、agent 看得到,要遮就在那个数据源上把那一列也设成遮罩;mask_query_result、模块说明按这一轮的改动重写,长行重新折好。
2026-10-06 23:17:15 +08:00
ThearchyHelios 888f746d31 K3b:交给核查 agent 的证据摘录先遮;反方审稿的理由过最后一道关
- 摘录由 judge.prepare 生成,以前 _hidden 只按列名遮:报告取数把遮罩列改了名字选出来(buyer_phone AS 手机号),质询、反方 agent 的任务里四个号码都在,模型照抄进理由。现在 prepare 收 kinds({数据源名: 类型},只有核查 agent 的摘录给),查询摘录、指标输入按 masking._derived_hidden 认出改了名字的遮罩列(和工具边界同一套规则,表结构按快照冻结的那份),认的过程出了意外整份的列都不给;结论句裁判自己的摘录不给 kinds,和以前逐字相同、复用键不变。反方审稿、审批质询再把摘录里别的列、SQL 里写着的遮罩值按值换成「已遮罩」(换了写法的号码也认),值同交给查询工具的那一份(报告快照里的,反方去掉这句结论原文里写着的);解析 SQL、按值换放进线程。
- 反方审稿的理由按值遮之后,正文、理由文档(正文、原文、目录里的列名)里还认得出遮罩值的,这句记为无法判断、理由换成系统说明、不存理由文档(_kept,同审批质询的 _leaked),遮不住的「被推翻」不再让出具降档。
补测试:质询、反方各一条端到端(任务里的摘录没有号码,手机号列不给,SQL 里的号码、备注里换了写法的号码换掉),反方理由里 agent 自己查出的号码换着写法也换掉,最后一道关;分别去掉按 SQL 认列、两处按值换、agent 查到的值、最后一道关,对应的测试都变红。
2026-10-06 23:11:22 +08:00
ThearchyHelios 6ba7babdf0 K3b:认不全的 SQL 写法整份遮——(表达式).* 展开、表名和 SQL 写在字符串里的函数、分析出意外或超出工作量上限;LATERAL 看不见它自己;一长串 CTE 不再卡住
- (表达式).* 展开(复合类型的列 (p.profile).*、记录函数 (jsonb_each_text(pj)).*、(jsonb_populate_record(…)).*、子查询里的匿名 ROW (q.r).*)以前被改写成对不上来源的 *,最外层、UNION 第一支、没写列名表的派生来源里按「照原名带出」认,字段名对不上遮罩列,号码原样交给模型、落库(K3 第 3 轮引入的回退,15 条新漏)。现在 _normalized 只把 (s).*、s 是这一层 FROM 里的来源时改写成 s.*,别的先记下表达式的值取自哪些列(整行引用已改写),读到遮罩列的整份遮。
- query_to_xml、table_to_xml、cursor_to_xml、schema_to_xml、database_to_xml 这一族、dblink、crosstab、DBMS_XMLGEN、DuckDB 的 query、OPENQUERY,按页按文件读原始数据的,pg_stats、sqlite_stat4、pg_stat_statements 这类存着值的统计、会话视图:按名字认出整份遮(_INDIRECT),解析不了的也一样。
- 整行引用由里往外找列时,LATERAL 里只看排在它前面的来源(_visible_sources),它自己的结果列碰巧叫 s 也不把整行 s 当成列。
- 一次分析里记下派生来源对应的那块查询和一项的值取自哪些列(_analysis,按线程分开),250 段一段套一段地整行引用的 CTE 从二十几秒降到零点几秒;超出工作量上限(ANALYSIS_STEPS)的整份遮。解析之后的分析出了意外也整份遮,不再退回只看文字(没写遮罩列、没有 * 的整行引用以前就放过去了)。
补测试:三种展开 × 最外层、子查询、CTE、LATERAL,端到端经核查 agent 的查询工具,九种函数和视图,LATERAL 自身,工作量上限、分析意外、长 CTE 的耗时;分别去掉每一处实现,对应的测试都变红。
2026-10-06 23:03:35 +08:00
ThearchyHelios 8c229c3d1a K3b:分档写在派生来源里、外层按它分组的标签列照常显示,和当分组键的 CASE 一样
以前分档写在子查询、WITH 里(SELECT *, CASE … END AS 档位 FROM sales),外层再按档位分组,工具边界把档位整列遮成「已遮罩」,模型分不清哪个数属于哪一档,质询回答成了「已遮罩有 3 笔,已遮罩有 2 笔」。现在 _derived_hidden 认出只装着标签(_label_columns)、又一档一行的结果列(这一层或派生来源里那一层的 GROUP BY 键,按位置、别名、照原样写都算,_grouped_labels),不算取自遮罩列:格子里只有标签、和遮罩列不同名才算,认不出的照旧整列遮;SQL 里照旧认得出它取自金额。工具边界、质询回答、质询工件用的是同一套。* 展开的表在冻结的表结构里查得到、没有同名列时标签照常认(原来就有),不知道表结构时照旧当它带得出(多遮)。测试 test_inquiry_masking 里「外层再分组照旧算」的两个用例按新规则改;补六种写法的工具边界用例、逐行和混了别的值的反例、质询端到端用例,去掉豁免后全部变红。_tainted 多交回一个「认不出、整份都算」的标记,整份遮时不豁免。
2026-10-06 22:52:28 +08:00
ThearchyHelios 3ced800cde K3b:工具边界的按值遮只换文字格,换过的格子记进快照;占位的短词不当遮罩值
以前工具边界按值遮扩到没设遮罩的数值列:访问次数正好等于某一笔被遮的金额,交给模型的、落库的都成了「已遮罩」,column_types 仍是 number、快照不留记录,盲算引用到时说成「不是数值」,存下的证据被改写。现在 _masked_view 在工具边界不按值换数值格(数、列类型记成 number 的格子),只换文字格(整格就是遮罩值的、文字里夹着的那一截),换过的格子随快照存下(masked_cells:[行, 列号],不交给模型),核查 agent 的格子引用指到它们时照实说是遮罩。回答、质询工件里的按值遮(第二道防线)照旧。遮罩列里占位的写法(未知、暂无、N/A……)不收进遮罩值,没设遮罩的区域、备注里的同一个词不再被换掉。补测试,分别去掉数值格的豁免、masked_cells 的记录、引用时的判断、占位词的过滤,对应的测试都变红。
2026-10-06 22:44:48 +08:00
ThearchyHelios 03209d7db5 K3b:工具边界的遮罩分析出了意外时整份遮,兜底不再经逐格换值那一步
mask_query_result 的兜底原来又调一遍 _masked_view:出意外的正是它时兜底跟着出意外,结果交不出去,模块说明里「分析出意外就整份遮」也没有测试证明。改成 _masked_whole 直接把每一格换成「已遮罩」、类型记成文本,masked_at_query 记全部的列。补测试:认遮罩列、认标签列、逐格换值三处各自出意外时,交给模型的工具结果、落库的快照、通用工件接口都只有「已遮罩」,格子引用照实说是遮罩列;把兜底改回原样放行,这几项全部变红。
2026-10-06 22:38:15 +08:00
ThearchyHelios 58a6759fe2 合入期 5 前置修复 K3:核查 agent 的查询结果在工具边界先遮——取自遮罩列的结果列(别名、函数、子查询、WITH、*、UNION、表值函数、整行引用都认)在交给模型的内容和落库的快照里都换成「已遮罩」,模型看不到遮罩值;按值遮降为第二道防线;换了写法的长号码(空格、连字符、点、括号、全角、国家码)在回答、理由、工件和最后一道关里都认得出;认遮罩列的共用部分挪到 engine/masking.py 2026-10-06 22:19:42 +08:00
ThearchyHelios c1e1fcddfe 合入期 5 前置修复 L2:可验证报告里设了遮罩的数据源上、能证明没取遮罩列的查询快照照常随文件附带,引用它的数字可以核对;解析不了、可能展开出遮罩列、整行引用的照旧只给哈希,说明写清原因 2026-10-06 22:19:34 +08:00
ThearchyHelios 39b4b6d608 合入期 5 前置修复 L1:上传表格建的数据源按导入配方里的逻辑类型认日期列,双盲复算的选择能写成业务日期;报告的结论句策略要求核对、正文却写在代码块里(结论句为 0)时,出口契约记一条缺口、不再判完整出具;compose_doc 的说明照实写 source 来自调用方 2026-10-06 22:19:34 +08:00
ThearchyHelios b4edcadc88 合入期 5 前置修复 L3:记录页的工件列表把双盲复算对照、反方审稿记录、质询回答归到「核查记录」一组,窄屏下说明换到第二行、序号不被截掉;运行对比的默认基准接口带上 previous,运行详情的对比入口在基准不是紧挨着的上一次时写明「与上一次成功的运行对比」,和触发历史一致 2026-10-06 22:19:25 +08:00
ThearchyHelios b0fdf56eed K3 复核修正(第 3 轮):写了库名的表按「库名.表名」查表结构;UNION 第一支按表结构数出的列名要和后面每一支一样宽才用;整行引用、函数参数里的 *、表的列名表、TABLE x、PIVOT 这类认得出
- 写了库名的表(复核 major 1):表结构以前按表名查,查询写的是另一个库里的同名表(archive.sales,列序不同)时照探查
  的那张数位置,号码原样交给模型、落库。现在表结构另按「库名.表名」记一份(库名取探查时记下的 schema、qualified 的
  前缀,masking._schema_keys),写了库名的引用按它查(_table_key),查不到的按数不清整段遮;探查时没设 schema、查询又
  写了库名的也查不到(多遮)。数位置(_star_columns、_branch_names)、认标签列(_table_lacks)都照这个查法
- 过期的表结构(复核 major 2,对 099473c 的回退):UNION 第一支是 * 时,按表结构数出的列名以前不核对列数就拿来对后一支
  的位置,表结构过期、列数已经变了也照对,遮罩值记到错的列名上。现在后面每一支数得出的列数都和它一样宽才用
  (_source_names、_width),对不上、数不清的退回整段遮
- 整行和别的 * 写法(复核 major 3):写在函数参数、表达式里的 *、s.*(to_json(s.*)、concat_ws(',', s.*)、ROW(s.*)、
  Oracle 的 JSON_OBJECT(*))按它展开的来源认(_value_columns、_scoped_star_inputs,相关子查询、FROM 里的子查询照样
  找得到外层的来源)。几种写法先改写语法树再认(_normalized,原文不动):Postgres 的整行引用(row_to_json(s)、
  json_agg(t)、SELECT s、s::text)按 Postgres 先认列的规矩,不是列的改成 ROW(s.*);带列名表的表(FROM sales AS
  s(i, r, p),引用 CTE 时写的也算)改成派生来源;解析器读错的 TABLE x 改成 SELECT * FROM x;选出项套了括号的 *
  ((s.*)、(s).*)按 * 数位置。PIVOT、UNPIVOT、MATCH_RECOGNIZE 读到遮罩列的整份遮。SQL 解析不了、里面有 *(COUNT(*)
  除外)或 TABLE x 的(UNION ALL TABLE x、带库名的 TABLE),同写着遮罩列一样整份遮(_implicit)
- Postgres 的列名表只写前几列时,不按表结构补全:别的方言里列名表比列少只能是表结构过期了,补全就丢了列数这道核对;
  这时改了名的几列整段遮(多遮),照原名的按名字认
- inquiry 模块说明的已知局限补上:写了库名的表、过期表结构的列数核对、Postgres 列名表只写前几列时整段遮、探查的不是
  默认库时没写库名的同名表可能数错、整行引用按表结构先认列(查不到时按整行多遮)、解析不了的 SQL 里的整行引用认不出

测试:新增 test_masking_whole_rows(整行、函数参数里的 *、列名表、TABLE x、Oracle 的 JSON_OBJECT(*) / UNPIVOT / PIVOT /
MATCH_RECOGNIZE、解析不了的 SQL、不碰遮罩列的照常);test_masking_star_renames 补写了库名的表(SQLite ATTACH 真跑)、
过期的表结构;test_verify_agent_masking 补核查 agent 的真实查询工具:过期的表结构,Postgres / Oracle 形状的结果(换掉
驱动)交给模型、落库、通用工件接口都遮好。每处实现都做过去掉之后测试变红的验证;复核给的 Postgres 18 探针、端到端
在临时库上重跑,都不再漏。
2026-10-06 21:50:48 +08:00
ThearchyHelios 6a7e51b81b K3 复核修正(第 2 轮):* 把遮罩列带进别的列名时按展开落到的位置遮;函数参数里的 JSON 箭头认作遮罩列;agent 查到的遮罩值和报告里的分开记,上限收满也不丢
- * 改名(复核点名的 blocker):WITH t(a, b, …) AS (SELECT * …) 这类列名表、UNION 后一支的 *、FROM 子查询或 LATERAL
  的列名表、标量子查询和 ARRAY 子查询里的 *,以前 * 本身不算取自哪一列,原值原样交给核查 agent、落库,第二道防线也
  认不出。现在 * 带出遮罩列的值时,它展开落到的那几列都算(masking._star_places):只展开一个来源、每一列都数得清的
  按位置只遮取自遮罩列的那几列——子查询按选出项数,数据源里的表按查询当时冻结的表结构数(_schema_columns;工具边界
  用数据源的 schema_cache,事后按快照的 schema_artifact,是同一份),几张列一样的月度表 SELECT * 连起来汇总,金额照常;
  数不清的(没有表结构、列数对不上、几个来源)整段遮。表结构只用来数位置,不用来断定表里没有遮罩列。标量子查询里的 *
  按 _value_columns 展开(展开数据源里的表一律算),EXISTS 里的 * 不算;递归的 CTE 不会绕不出来;带 EXCLUDE / REPLACE
  的 * 不按位置数
- JSON 箭头:函数参数里没写表名的 col->'path' 被 sqlglot 解析成以 col 为参数的 lambda,_value_columns 把 lambda 的
  参数名照列名收。SQLite json_each(profile->'$.tels')、Postgres jsonb_array_elements_text(profile->'tels')、MySQL
  JSON_UNQUOTE(profile->'$.tel') 这几种拆开、取出来的值在工具边界就遮
- 上限:run_verify_agent 以前先把调用方交进来的报告快照值并进 seen,收满 SECRETS_MAX 之后 agent 查到的值并不进去,后面
  的查询、回答时的第二道防线都认不出。现在两份分开记(masking.SeenValues:given / found),查询工具按值遮、交回调用方
  (VerifyOutcome.masked_values)时合起来,agent 查到的在前;反方审稿理由的按值遮也改成 agent 查到的先收
- 已知局限写进 inquiry 模块说明:表结构过期、列数没变却换了次序或改了名时可能数错;EXCEPT 后一支照 UNION 算(多遮)

测试:新增 test_masking_star_renames(真跑 SQLite 的各种 * 改名、按表结构数位置、表结构对不上、快照记着的表结构),
工具边界(交给模型的、落库的、通用工件接口)和审批质询端到端的 * 改名、JSON 箭头、上限收满;每处实现都做过去掉之后
测试变红的验证。
2026-10-06 20:29:58 +08:00
ThearchyHelios 6d9d31395b 出口契约认藏在代码块里的正文改为逐行标出代码:写明是文字的块不再因为一行 SQL、一行像 YAML 的键或 + / - 要点就整块当代码,公式、JSON、目录树这类像数据的行照代码看
上一轮先判整块:只要有一行以 SQL / Python 关键字或小写键开头,或者每行都以 + - 空格 @ 开头,整块就当代码,只认去掉
注释、字符串之后还成句的行。写成 markdown、text 的块里,+ / - 开头的正负面要点、正文后面另嵌的 ~~~sql、缩进的 select、
一行「- note: …」都让不带句读的结论认不出,藏进代码块反倒完整出具。反过来,写明是文字的块不去字符串,六字连写、结论词
又对公式、JSON、目录树、print、流程箭头这些行生效:结论句为 0 的报告贴一段带「占比」的公式就降档,缺口还说正文写在
代码块里。

- 逐行标出代码(_code_lines),不再整块判:块里再嵌、收得了的代码块(~~~sql、````,开了没收的那一行照普通的行看);
  SQL / Python 语句从开头那一行到这一段结束,续行也算;整段是 YAML 的(第一行是小写键,其余是键或缩进的行;写明是
  文字的块里另要有值为空的键,一行一个「- note: 一句话」是正文里的标签);像数据、公式的行(前面有字的等号和箭头、
  画目录树的制表符、JSON 的开头和「"键":」、函数调用、文件名、逗号隔开的表头)。代码的行只认去掉注释、字符串之后
  成句的,别的行照旧:写明是文字的按 _written,信息串为空的按 _prose_line
- diff:写明是文字的块里 + - 后面空一格的是列表项,紧挨着字写的才是 diff;信息串为空的块里第一个改动行是 + 的是
  正负面要点(diff 先列删掉的行),有 @@ 块头的照旧是 diff
- 一段到空行为止,只写着块注释的行不算空行;写明是文字的块里顶格的标题另起一段。块注释、三个引号括起的多行字符串
  涂成空格,行数不变
- SQL 关键字只认全小写、全大写,def / class 要写成定义的样子:「From 数据看……」「class A 门店……」是正文。代码的行里
  -- // 紧挨着前面的字也算注释
- 取舍写进 _fenced_body 的说明:一块里只要有一行在写正文就记缺口,拿不准的行宁可当代码,藏起来的正文总有别的行认得出

测试:复核里的写法逐一补上:+ / - 要点(markdown、text、信息串为空)、嵌 ~~~sql、缩进的 select、小写键的标签,正式
运行降档并只带这条缺口;带「占比」「增长率」的公式、写成 text 的 JSON 和目录树,在结论句为 0 的报告里照常完整出具。
单元参数另补 print、groupby、流程箭头、文件名、逗号表头、JSON 数组、多行 SQL 夹块注释、docstring、嵌套的 YAML、开了
没收和收尾更长的嵌套围栏、行首箭头、首字母大写的关键字、先列 + 的带 @@ diff,并钉住两条取舍:写成 text 又空一格的
diff 照列表看;信息串为空、整段都是小写键的照 YAML 看。长行耗时用例扩到七种长行、两种多行围栏、两种信息串。逐一去掉
36 处规则(diff、嵌套围栏、语句、关键字大小写、def / class、YAML、分段、像数据的七种写法、注释、块注释、多行字符串、
正文判法、结构片段、挂钩),各有用例变红。
2026-10-06 19:51:29 +08:00
ThearchyHelios 7255ae98a5 模型接入报错收尾(第 1 轮复核,后端):交给设置的核查、裁判落到演示模型照实说;给不出修复时说卡在哪里;接入名恰好是模型名那句按这一处检查器里有哪几栏写;修复规划只重算被改的节点;助手栏那一格另给一份说法;几处措辞对齐
- 「不指定接入」交给设置(核查模型、裁判模型)时:启用的接入全是演示类,设置这一级不管写的是什么,执行时落到的都是
  演示模型,说明照实带上「(演示用,不调用真实模型)」、候选带 demo 标记;一个启用的接入都没有时不算落得稳(同别处
  「换成内置演示模型」不算)。以前 land() 在判演示之前就把交给设置的一处当成落稳返回了;
- 检查器里没有下拉框、发布前检查又给不出修复时,提示末尾补「无法改用其他接入:」加第一处落不稳的地方(Landing.why,
  比如「成员「研究员」指定的模型「小模型」所属的模型接入「旧接入」已停用」),不再只剩「核对名称」让人去设置里找一个
  找不到的名字;
- 点名的接入名恰好是某个启用接入的模型:画布、发布前检查按这一处在检查器里有哪几栏写(provider_refs._model_note)——
  模型调用一类在节点的「模型」中选择它,接入在发布前检查里改用这个接入或选择「不指定接入」(修复里有哪项说哪项);
  协作成员、全图默认只说可在发布前检查里改用;核查节点、报告自己的结论句裁判两栏都在,照设置页写这两栏的叫法。
  不再叫人去一个没有的「接入」栏留空。运行时报错不分节点类型,「填写」改为「选择它」;
- 修复候选修完以后落到演示类接入的带 demo 标记,发布弹窗据此在选项名旁加醒目的标签;
- 性能:改一个节点的配置只影响它自己取模型的那几处,修复规划(autofix._ProviderPlan)每试一个候选只重算这个节点
  (provider_refs.node_uses),不再深拷贝、重算整张图;问题提示只问给不给得出(provider_fixable,找到第一个落得稳的
  就停);接入问题的引用整张图只算一遍。60 个节点都点名同一个停用接入时,画布校验里这一部分约 0.35 秒降到约 0.002 秒,
  发布前检查约 0.72 秒降到约 0.01 秒,一次应用 30 条修复约 2.7 秒降到约 0.09 秒;
- 助手:GET /api/copilot/model 另给 panel_error(写在助手栏「助手使用的模型」那一格下面,去向写「上方」,不把人指回这个
  下拉框本身);接入名恰好是模型名时并成一句,不再两次提「助手使用的模型」;PUT /api/settings 里助手那一项的 422 用
  助手报错那一句(那里只有一个选模型的下拉框,没有「换一个接入」的地方);
- 裁判设置的格式检查写「留空表示跟随助手的模型」,同设置页空选项(engine/judge.py 一行,同核查那条的改法);「交给助手」
  后只剩接入问题时写「助手无法处理」,有修复时另起半句「也可在各条问题下方选择改用的模型接入」。

测试:test_provider_ref_wording 加演示落在设置上、demo 标记、给不出修复时卡在哪里(含一个启用接入都没有)、各处的
接入名恰好是模型名、助手栏那一格的说法、助手那一项的 422、裁判格式检查、规划开销随节点数线性增长(数试算时算了多少处
取模型的地方);其余几个文件跟上新说法。新加的核心用例都做过去掉实现、测试变红的验证。
2026-10-06 19:43:44 +08:00
ThearchyHelios e7fb8f6c1c 可验证报告复核修复(第 2 轮):SQL 解析不了的快照,口径卡同样整张只给 id;整行引用补全函数记法、按表函数的先后认;拒绝名单补别的会话的 SQL 原文
复核查出三处:口径卡和快照对 SQL 解析不了的结论仍不一致;整行引用的函数记法名单不全,表函数参数里「先按列认」不看先后;
拒绝名单里「别的会话的 SQL 原文」一类不全。逐条修:

- 口径卡和快照按同一个结论判(复核的第一条):SQL 每种读法都解析不了时快照只给哈希,卡这条路以前退回按 SQL 认遮罩列的
  那一套(inquiry._derived_hidden)。那一套解析不了时只看 SQL 里写没写着遮罩列,没写就只认遮罩列本身,卡上取自整行的值
  照样整张嵌入。例如 PG17 起的 JSON_QUERY(to_jsonb(m), '$.*' WITH WRAPPER) AS tel,以及 ORDER BY … USING < 这类 sqlglot
  读不了的写法,号码就这样原样进了文件。现在 _derived_columns 对不是合并查询的快照,没有一种读法读得通时整份的列都算,
  整张卡只给 id。没有 SQL、SQL 是空的也一样处理:这时快照同样只给哈希。合并查询照旧不看 SQL。
- 整行引用:
  - 函数记法的名单补上 record_out、record_send、any_value、json_agg_strict、jsonb_agg_strict。另照 PG 18 的 pg_proc
    逐个对过,补上 max、min:m.max 就是 max(m),交回最大的那一整行,复核没点到,本地实测号码在里面。补了 max、min 之后,
    写了表名的名字同样先按列认:派生来源明写出了叫 max 的列,t.max 就是那一列,按地区取最大值的子查询不受影响。数据源里
    的表有哪些列这里看不到,碰巧叫 max、min 的列宁可多挡。
  - 没写表名的名字写在表函数的参数里时,「先按列认」只看排在这个表函数前面的派生来源(_holder)。
    FROM members m, jsonb_each_text(to_jsonb(m)), (SELECT 1 AS m) s 里,s 对函数不可见,m 是整行,以前按列认,快照照常嵌入。
    没有改成「表函数参数里一律不按列认」:排在前面的派生来源明写出了同名列时,那确实是列,补了对照用例。
- 拒绝名单(不点列名也读得出表里内容的写法):
  - 别的会话的 SQL 原文:Postgres 的 pg_stat_get_activity、pg_stat_get_backend_activity、pg_stat_statements、
    pg_prepared_statements(asyncpg 连接池里的连接留着前几次查询的原文,本地 PG 18 实测);MySQL sys 库的 session、
    processlist、innodb_lock_waits、schema_table_lock_waits 及各自的 x$ 版本(只认这几张,Oracle 常写的 sys.dual 不受
    影响,补了对照用例),企业版的 audit_log_read;Oracle 的 V$OPEN_CURSOR 和审计记录(*_AUDIT_TRAIL、
    UNIFIED_AUDIT_TRAIL、AUD$、FGA_LOG$)。
  - 复核脚本里同样照常嵌入、和已拦的写法读的是同一份数据的,顺手补上:pageinspect 按索引名读页的 bt_page_items、
    pg_walinspect 的 pg_get_wal_block_info、SQLite 的 readfile、Oracle 直方图和列统计的底表 histgrm$、hist_head$、
    MariaDB 的 column_stats。大对象(lo_get、pg_largeobject)里存的不是表里的数据,没有加。

测试:口径卡补 SQL 解析不了(三种 sqlglot 读不了的真实写法)、没有 SQL、SQL 为空的用例,卡上取 tel 和 spend 都只给 id;
另补函数记法 record_out、表函数后面同名派生来源两种整行写法。查询快照补七个函数记法、WITH 里 * 加函数记法、两种表函数
先后的写法,以及二十项拒绝名单。对照组补派生来源明写出 max 列、表函数前面的派生来源明写出同名列、sys.dual,这三种照常
附带。夹具里的 SQL 都在本地 PG 18.6 上跑过,结果和用例说的一致。逐处去掉实现,相关测试都变红:
- 解析不了的退回那一套 4 项,没有 SQL 的 1 项;
- 名单不补 9 项,只去掉 max、min 2 项;
- 写了表名的不先按列认 1 项;
- 表函数参数不看先后 3 项,一律不先按列认 1 项;
- 别的会话的 SQL 不补 14 项,原始数据、统计信息不补 6 项;
- sys 整个拦掉时 sys.dual 对照变红。

后端全量 10238 项通过、11 项跳过;check-all bundle 183 项通过;check-copy 通过。
2026-10-06 19:21:23 +08:00
ThearchyHelios 099473cd6d K3 复核修正(第 1 轮):后面的查询按此前遮掉的值遮、质询工件照回答时记下的位置重放;表值函数拆开遮罩列也认;汇总遮罩列的指标不派盲算;反方理由按值遮
复核发现质询工件接口相对基线回退:agent 自己的查询快照在工具边界遮好之后,事后取质询工件收不到它另一次查询里的号码,
没设遮罩的备注、联系方式列原样给出。另有几处模型照样看得到遮罩值、或者遮多了的地方。

- 工具边界(tools/datasource、engine/masking.mask_query_result):此前已经认出的遮罩值(这个 agent 前面的查询遮掉的、
  调用方交进来的)也用来按值遮后面的结果,数据源没设遮罩的也一样。审批质询、反方审稿在派 agent 之前把报告快照里的
  遮罩值交进来(inquiry.snapshot_secrets,run_verify_agent 的 masked_values),模型从第一条查询起就看不到。已经摆在
  读者眼前的值(提问人写的、报告指标、被审的结论原文)跟着并进来的值一起免遮(_Secrets.protected);这份结果自己写
  的分档标签不按前面收的值换。
- 先查备注、后查出号码的:回答时只靠内存里的值才换掉的地方按快照记成位置(不含值,masking.view_edits),写进质询
  事件的 masked_cells,质询工件接口先照着重放(replay_edits)再按原来的办法遮。工具边界按值换掉的整格照样画成遮罩列。
  agent 查到的值先收、报告快照的后收:上限收满时不丢 agent 自己查到的。
- _derived_hidden 认 FROM、JOIN、LATERAL 里拆开遮罩列的表值函数(json_each、json_tree、unnest、jsonb_each_text、
  regexp_split_to_table、JSON_TABLE、LATERAL VALUES):按出列名认,不认得的函数又没写列名表的整份遮。
- 汇总里的 CASE 只在条件里用到遮罩列的不算取自遮罩列(同写在 WHERE 里)。双盲复算按同一套认法认出指标取自遮罩列
  (SUM(amount)、amount 设了遮罩)时,派盲算之前就记为无法复算并说清原因,不白花一次盲算(nodes/dual_check 的
  _query_input,改动在文件所有权之外,只加这一处判断)。
- 反方审稿:理由正文和理由文档按值换掉遮罩值(换了写法的也认),这句结论原文里写着的不换;重新组装时引用到的格子按
  遮好的快照渲染。
- 换了写法的号码另认不换行空格、窄空格、细空格、制表符、减号,最多四个分隔符;SQL 的字符串、带引号的名字、注释里
  也认(不带引号的数照旧是算式),字符串里换掉的号码同一条 SQL 里写成阈值的也不留;列名同样认。
- 查询失败时报错原话里的值换掉(mask_error_text:引号里、不带引号的长数,SQL 里写着的照留)。
- 快照的 mask_columns 只记数据源设的列名,改了名字的结果列只在 masked_at_query:证据面板并各份快照的这一项从表的
  字段清单里去列,混进别名会误藏同源别的表里同名的真实字段。
- * 展开数据源里的表时按冻结的表结构认它带不带同名列,没有的照常认分档标签(J2 收紧之后的多遮)。
- 格子引用指到按值换掉的格子时照实说;上万个值的合并、免遮放进线程。

测试:质询 agent 先查号码后查备注、先查备注后查号码、另一张表的号码列两种先后,质询工件、通用工件接口、事件都没有
原值;报告快照里的值在第一条查询就遮;表值函数(SQLite、Postgres、MySQL 的写法)、汇总里 CASE 的条件、* 连着的表按
表结构认标签、新的分隔符、SQL 字符串和注释和列名、报错原话、mask_columns 不混别名、遮罩列汇总的指标不派盲算、反方
理由按值遮且免遮结论原文、上限收满时 agent 的值先收。逐处去掉实现(24 处)对应的用例都会变红。
2026-10-06 19:11:14 +08:00
ThearchyHelios 46403a0aac 出口契约认藏在代码块里的正文改为逐行看字、不看列表标记;信息串为空的块里 YAML、diff 不再当成正文
上一轮按列表标记认不带句读的行,只认 - * + • 和「1.」「1)」:中文报告常见的「1、」「一、」「(1)」、· ● 开头的行和
不带标记的行藏进代码块仍是 0 句、完整出具,不包反倒降档。同一条规则对信息串为空的块都生效,结论句为 0 的报告里贴一段
写着中文列名的 YAML 或改口径的 diff 会降档,缺口还说正文写在代码块里。

- 不再按标记认:标记写法太多,补一种漏一种。一行按正文的规矩会算一句结论(evidence._classify 认文字结论的那条:
  方向词、因果词,又不只是在下定义,且有两个以上汉字)就算在写正文,不论标记,与不包代码块时的判法一致
- 信息串写明是文字(markdown、text 这类)、里面也看不出是代码的块:另认一行里连着六个以上汉字的(表格的列名、
  门店名这类标签多在五个字以内);不当代码看,# -- 和半角引号都是正文里的符号;标题不算(正文里的标题也从不算结论)
- 块里看得出是代码(SQL、Python 开头的行,小写开头的 YAML 键,整块是 diff),不论信息串写的是什么,只认去掉注释、
  字符串之后成句的:值里、别名里的「最高」「占比」这类词不算;正文里首字母大写的「Note:」是标签,不算 YAML 的键
- 信息串为空、看不出是代码的块:去掉注释、字符串之后成句,或按正文的规矩会算结论;不带句读也不像结论的列名不算
- 围栏那两行是挂在代码块上的结构片段,判断时只看块里写的内容

测试:1、 一、 (1) · ● 和不带标记的写法藏进代码块(另有信息串为空的一种)正式运行降档并只带这条缺口;YAML 列表、
diff 在结论句为 0 的报告里照常完整出具;单元参数补上引用块、表格、粗体、短结论、正文里的 # -- 引号,以及 YAML 键、
diff、SQL 别名、写成 text / markdown 的代码、不到六个字的标签、只有标题。逐一去掉六字连写、两处结论词、代码判断
(YAML、diff 各一次)、不当代码看、结构片段过滤、小写键、标题跳过、挂钩,以及换回按标记认的旧规则,各有用例变红。
2026-10-06 18:44:21 +08:00
ThearchyHelios 8c0c3b8eeb 可验证报告复核修复:遮罩源上的查询快照再堵几条带出号码的路,口径卡和快照按同一个结论判
复核查出,上一轮放开「没碰遮罩列的快照照常附带」之后,有几种写法 SQL 里不写 phone、结果列名也对不上,结果里照样是
号码,快照原字节和查询事件的预览会随文件附带出去。这一轮逐条堵上:

- 整行引用认全(复核的两个阻断项):Postgres 的 FROM 里的表函数不写 LATERAL 也看得见前面的来源,
  `FROM members m, jsonb_each_text(to_jsonb(m))` 这类逐列画像的写法(逗号、CROSS JOIN、JOIN … ON TRUE、LEFT JOIN、
  ROWS FROM、不带别名的 UNNEST、套在 FROM 的子查询里)以前按看不见算,整行引用认不出。现在 FROM、JOIN 里只有真的
  子查询和 WITH 里的一段看不见同一层;括号括起来的一组连接((members m CROSS JOIN f(m)))也摊开按同一层认。另外认
  函数记法 m.to_jsonb、m.row_to_json(等于 to_jsonb(m)),自己建的函数认不出。
- 不点列名也读得出表里内容的写法(_INDIRECT_READS):SQL、表名写在字符串里交给函数执行或整表导出(table_to_xml、
  query_to_xml 一族、ts_stat、crosstab、DBMS_XMLGEN、XQuery 的 ora:view),统计信息里的采样值(pg_stats、
  sqlite_stat4、column_statistics、Oracle 的直方图和 *_tab_columns),按页按文件读的原始数据(get_raw_page、
  pg_read_file、LOAD_FILE、sqlite_dbpage、逻辑解码),别的会话的 SQL 原文(pg_stat_activity、general_log、V$SQL),
  系统 schema(pg_catalog、information_schema、performance_schema)。SQL 原文里出现就只给哈希;函数名要跟着左括号,
  列名碰巧叫 crosstab、pg_status 的不受影响。
- 这次运行里查到过的遮罩值写进了别的查询的字面量(模型拿看到的号码去查别的表):导出时收齐封存范围内每份快照里
  取自遮罩列的格子(inquiry._Secrets 的按值认得出的写法),只看 SQL 的字面量,碰上就只给哈希、查询事件也只给哈希。
  这次运行里没查到过的值(提问人写进问题的)认不出。
- MariaDB 会执行的注释 /*M! … */ 和 /*! … */ 一样按语法树认不准处理。
- 口径卡和快照按同一个结论判:快照因列名表、整行引用、不照原名展开的 *(UNION 一支里按位置对上列名的、装进一列
  的)、吃进遮罩列的表函数、语法树认不准或不点列名也读得出内容而只给哈希时,卡上取自这份快照任一列的输入都按遮罩列
  算,整张卡只给 id。以前卡这条路只认改了名字的遮罩列,快照只给哈希,卡上记着的号码却原样进了文件。最外层直接写的 *
  照原名展开,按名字认就够,卡照常嵌入。
- check-bundle 的 masked-source 样本点数字之前先看有没有核对通过的数字,没有就记一项不通过、不去点:以前点空了要
  等满 30 秒抛错,同一节后面的 nan、stray 样本都不跑了。换回改动前的 build.py 实测,现在只多记两项不通过,后面照常跑。

有意没改的一条:SQL 写到遮罩列、结果里却没有号码的(COUNT(DISTINCT phone)、phone IS NOT NULL)照旧只给哈希,比规格
的四条更严。FROM 里的表函数吃进遮罩列时(unnest(ARRAY[phone]))它交出的列取自哪一列认不出,只能看 SQL 写没写到;
补了这一例的测试,说明写在 _untouched 和测试里。

测试:新增整行引用(表函数、括号里的一组连接、函数记法)、不点列名的读法、字面量里的遮罩值、MariaDB 注释、口径卡
七种认不出来历的写法各自的用例,以及对照组(列名像函数名的、表函数只吃没设遮罩的列的、括号连接按名字取列的、最外层
* 的卡)。合并查询那一条判断补了只带认得的键的夹具,结果列名那一条补了按 SQL 认遮罩列的那一套什么都没认出来时的
用例,两条现在都能单独证明——上一轮提交说明里「每一条规则都做过去掉实现测试变红」对这两条不成立。这一轮 15 处改动
逐一去掉实现,相关测试都变红。
2026-10-06 18:33:02 +08:00
ThearchyHelios b7da8a9af9 出口契约认藏在代码块里的正文:只挂 [[see:]] 依据的、不带句读的列表项也认,代码里的注释和字符串不算句子,英文句子改成线性扫描
上一轮只看片段里的引用和带全角句读的行,几种写法仍会让正文藏在代码块里照样完整出具,另有几种代码被当成正文:
- [[see:]] 记在句子的 see 上、不在片段里:只用 see 挂依据的列表现在也认
- 中文报告的列表项常不带句读:以 - * + • 或「1.」「1)」开头、后面有两个以上汉字的行算成句;信息串为空、看得出是
  SQL / Python 的块里(有 SELECT、FROM、def、import 这类开头的行),以 * + - 开头的是运算的续行,不当列表项
- 信息串写成 plaintext、txt、plain 的和 text 一样算写文字的代码块
- 判句之前去掉字符串字面量(可带 f、r、b、u、N 前缀;英文的 it's 这类不动)和行尾注释(-- // #),# 开头的行、
  跨行的 /* … */ 块注释都不算:SQL 行尾的中文注释、print 里带全角标点的字符串不再让 0 句的报告降档
- 认英文句子的正则对很长、不带句末标点的一行是平方级回溯(出口复核在事件循环上同步执行),改成按词串逐个数单词,
  耗时随行长线性增长

check-decode 补一项:从 io.py 读出 GAP_FENCED_BODY,钉住它归「校验不完整」、不进核查的两组。补参数化用例和端到端
用例:只挂 see 的列表、不带句读的列表、plaintext 包住正文的正式运行降档并带这条缺口;带行尾注释的 SQL 照常完整出具;
四万多字的长行一秒内判完。
2026-10-06 18:03:12 +08:00
ThearchyHelios 04606cfe94 双盲复算写业务日期:真写按写入时的版本重读上传表格导入时认作日期的列,几个列表块写同一张表时每一块都记为日期才算
record_choices 的表结构是写入时现读的,候选里的 upload_dates 却是发起审批时读的:审批停着时上传源换了版本、把日期列
改成文字,两份拼在一起会写出一条哪个版本单独看都不会给出的人工确认。现在候选里带 upload_dates 的,真写在读数据源的
同一个会话里按当前版本重读、覆盖候选里的那份,和表结构出自同一个版本,版本没变时预告和真写仍一字不差;候选里没有
这一项的是升级前发起的审批,卡上预告的是不写,照旧不认。

recipe_dates 以前只要有一个列表块把某列记为日期就算日期列:两个工作表的列表块写同一张表、同一列一个日期一个文字时,
表形状一样(都存成 TEXT),配方校验不拦,按文字导入的那一块存的是单元格原文。现在写这一列的块都记为日期才算。

补测试:审批停着时修改配方把支付日期改成文字,裁决后照实说按文字保存、不写;没有 upload_dates 的老候选不重读、照旧
不写,带着的按写入时的版本重读后照写;两块类型不一致的列不算日期。
2026-10-06 18:03:12 +08:00
ThearchyHelios 450758185d K3:核查 agent 看到的查询结果先遮、存下的快照也遮
双盲复算、反方审稿、审批质询共用的 engine/verify_agent.py 以前把查询结果原样交给模型(db_query 自己落快照,ToolMessage
里是原样的结果),遮罩全靠审批质询事后按值认,期 4 起六轮复核每轮都找到新的漏法;运行详情「工件」页签走的通用接口
GET /api/artifacts/{id} 还把核查的查询快照原样给出。根本的修法是不让模型看到遮罩值。

- tools/datasource:查询工具加遮罩开关 mask_results(缺省关,Agent 节点、调用工具节点照旧)。打开时数据源设了遮罩的,
  先按 engine/masking.py 的 mask_query_result 遮好再交给模型、再落快照:列名直接对上的、按 SQL 认出取自遮罩列的
  (别名、函数、子查询、WITH、SELECT *,复用 _derived_hidden;SQL 解析不了又写着遮罩列的,整份结果的列都遮)整列
  换成「已遮罩」、类型记成文本;同一份结果里别的格子整格是遮罩值的换掉,文字格里夹着的那一截换掉(换了写法的号码
  也认);列名不换。结果里一列都没取自遮罩列的(数值的汇总、只数个数、只在条件里用到)原样。快照里 mask_columns
  另记下查询时遮了的、改了名字的结果列(证据面板、质询工件据此画成遮罩列),masked_at_query 记下查询时就整列遮掉的列。
  这一次遮掉的值并进 ToolContext.extra[MASKED_VALUES_KEY],只在内存里。
- verify_agent:建查询工具时和 force_readonly 一样打开遮罩开关;格子引用指到查询时就遮了的列,照实说是遮罩列、不采信;
  遮掉的值经 VerifyOutcome.masked_values 交回调用方(不进 as_dict、不往外交);系统提示补一句「已遮罩」是什么。
- 审批质询:按值遮降为第二道防线。agent 自己的快照查询时就遮好了,收不到遮罩值,改由 masked_values 并进来(_plan 的
  seen),回答、查询清单照旧按值遮;「已遮罩」本身不收成遮罩值。事后取质询工件时内存里的值已经不在,SQL 检查结果的
  片段、说明照事件里记下的那条 SQL 换(_recorded_swaps:逐词对出回答时换掉的字面量和 LIKE 里换掉的那一截)。
- 双盲复算取盲算值、反方审稿的引用、证据面板、可验证报告(带遮罩的快照只给哈希,照旧)、推演、运行对比(不收核查的
  查询)都按快照里已经是「已遮罩」照常工作,文件不用改。

测试:核查 agent 选出遮罩列(直接、别名、函数、子查询、WITH、SELECT *)时交给假模型的 ToolMessage、落库的快照、通用
工件接口、tool.end 预览、收尾交给模型的查询结果里都是「已遮罩」;没碰遮罩列的聚合查询原样、照常取值;文字格夹着的
号码换掉那一截;引用遮罩格子不采信;SQL 认不出时整份遮;开关缺省关、没设遮罩的源快照一字不差;列名不改;审批质询、
反方审稿、双盲复算各补一条端到端;质询工件的检查片段照记下的 SQL 换。逐处去掉实现(开关、并进内存的值、照记下的
SQL 换、跳过「已遮罩」、遮罩格子的说法、记下改了名字的列、文字格按子串换、不改列名)对应的用例都会变红。
2026-10-06 17:36:55 +08:00
ThearchyHelios 4673e51bb8 模型接入报错的说法与修复候选收尾(后端):修复规划只否决这次改动造成的失败、问题只认自己那一处的修复、没有修复时提示不指到发布前检查、落到演示类接入照实说;各处说法对齐
修复规划(autofix.provider_choice,原 _plan_provider 的主体,provider_refs 也用它判断给不给得出修复):
- 改动波及的一处,要是点名的接入没变、而那个接入本来就不存在或已停用,它落不稳不是这次改动造成的,不再拿它否决候选。
  以前两处互相牵连(节点点名的接入写错、模型取全图默认里停用接入的模型)时两条问题都没有修复,规划器自己卡住;
- 点名的模型接入一处一条修复,annotate 只认(编号、节点、字段)都对得上的那一条,认不出就是没有,不再退到同节点、
  同编号别处的修复(以前全图默认那条的 fix 会指到某个节点的修复上);
- 一个候选都落不稳时,问题提示不再写「或在「发布前检查」中改用其他接入」,只说去设置里核对、启用;
- 修完以后落到演示类接入(kind 为 mock)时,「不指定接入」的说明写明「演示用,不调用真实模型」,和「改用演示接入」
  那一项一致——K1 要防的就是悄悄换成演示模型。

说法:
- 双盲复算、反方审稿的问题写「……的「盲算模型 · 接入」指定的模型接入」,不说「盲算模型指定的接入」(修复标题仍按角色说);
- 点名的接入名恰好是某个启用接入的模型时,运行时报错、画布问题、保存设置的 422、助手的报错都补一句这是哪个接入的
  模型、怎么用它(factory.model_note;设置页写那一组模型、接入两栏的叫法),免得照「启用它」去启用那条旧接入;
- 保存设置 422:下拉框选的写「指定的」,不写「填写的」;去掉和设置页空选项对不上的留空半句;助手那一项叫「助手使用的
  模型」;核查的格式检查写「留空表示跟随证据裁判模型」,同设置页;
- 「交给助手」后只剩接入问题时的说明:一句一个意思、不再带冒号(界面上前面已有「助手:」),「核对名称」有宾语,不说
  「节点」;有一条带修复时才指到各条问题的修复;
- 助手的模型取不到时,提示指到真正能改的地方:「编排」页助手栏的「助手使用的模型」(设置页没有这一项);
- 节点点名了接入、没写模型时校验说「将使用接入「X」的默认模型」(编号 model.unset);X 本身不存在或已停用时画布校验、
  发布一类都去掉这一条,只留接入那条,不再一条说照常执行、一条说会失败。
2026-10-06 17:29:18 +08:00
ThearchyHelios 02e9bcd908 可验证报告:理顺 _contained 的说明(派生来源的列外层只能按名字取,或者再用一个 * 展开,那个 * 照样要过这一关) 2026-10-06 17:15:13 +08:00
ThearchyHelios 50f16aece5 可验证报告接口测试:真跑一次,遮罩库上只汇总消费额的查询快照随文件附带
照证据测试的夹具真跑:会员库遮了 phone 列,另加一次只汇总消费额的查询(没碰号码列)。导出的页面里,数据源查询工具
真落的这份快照按原字节附带、和服务端的文件一致,它的查询事件带规范字符串,报告里引用它的数字和快照里那一格对得上;
选出了号码的那份照旧只给 id,页面里找不到号码。这条也守着「认得这份快照」那一关:查询工具以后往快照里加了键,这里
会先变红,得同时想清楚可验证报告该不该认它。在改动前的 build.py 上这条是红的。
2026-10-06 17:12:56 +08:00
ThearchyHelios 8ede152b6f 可验证报告:口径卡取的列是 SQL 里改了名字的遮罩列时,整张卡同样只给哈希
口径卡上记着每个输入的值和代入式,有输入取自遮罩列的整张卡只给哈希;但以前只按列名认:查询写 phone AS tel、经子查询
或 WITH 转一道、套个函数,卡上取 tel 那一格就是号码,卡照样原样嵌进要发给别人的文件。现在那几列算不算遮罩列另按 SQL
认改了名字的(_Store.hidden → _derived_columns,用 inquiry._derived_hidden,和质询、双盲复算同一个认法;SQL 按每种
读得通的读法各认一遍合起来,都读不通的写着遮罩列就整份的列都算,出错时整份的列都算)。同一条查询里没取自遮罩列的
列照常嵌入。新测试覆盖别名、子查询、WITH 加函数三种改名,以及没改名的列、认的过程出错;每处都做过去掉实现测试变红的
验证。
2026-10-06 17:11:41 +08:00