CodeEraser

工作原理

CodeEraser 用确定性计算去审计语言模型的非确定性产出。模型往一个长命仓库里写代码, 会漂向堆叠而不是编辑——同一个函数实现两遍、同一条事实在第三个文件里重述一次、一次更新以追加的 形式落地。顺手的做法是再拉一个模型来审计这份漂移。本项目的承诺正好相反。

下面每一条判决都是对树上实测事实的算术:token 指纹、树编辑距离、图入度、git 窗口计数, 以及针对写下来的阈值所做的整数与有理数比较。证据与判决之间没有任何采样温度, 判决层里没有浮点数,回路里没有模型。同一棵树在任何机器、任何时刻都产出同样的字节;因此关于代码 健康度的分歧靠重跑那个数、读它指向的 file:line 来收场。

下文每节都是短版。完整推导——每个常数都追溯到实现它的源码行——在 docs/reference/methodology.md, 本页每个小标题都直接链进去。

三条泳道:Rust 侧的实测事实(token 指纹 k=25 w=26、后序 AST 树、引用图、git 窗口、LOC 多重集、文档 shingle),Haskell 侧的判决计算及其公式(winnowing 保证 t=50、TSED 85/100、精确 Jaccard 80/100、软线 S=clamp(floor(m*r^k),[200,500])、软区凸罚、存活性闭包、熵与卡方散度、最小二乘斜率、分数折叠),以及门(ADR-006 棘轮、离散棘轮、dedup 预算、--fail-under 地板、守卫档位阶梯)
度量、判决、把门。泳道 1 在 Rust 侧抽取事实;泳道 2 在 Haskell 侧用图上所示公式把 它们变成判决;泳道 3 决定退出码。图上每一个数字都是下文各节引用的常数。

十二个判决家族

01T1/T2 克隆检测——winnowing 指纹索引

找出精确与参数化的重复 token 段:改名的变量、换掉的常量算克隆,改掉的语法不算。

t = window + kgram - 1 = 26 + 25 - 1 = 50 tokens

任何长度至少为 t 的归一化公共 token 子串,必然包含 t - k + 1 = w 个连续 k-gram——即一整个完整窗口;而窗口最小值的选取只依赖该窗口 内部的内容,所以两份拷贝必选出同一个最小值。因此必然至少共享一个指纹:这是 Schleimer 等人 SIGMOD'03 的 no-miss 下界,作为正确性契约而非估计。滚动哈希是 Rabin-Karp, h = (h - t[i-k]*top)*BASE + t[i],跑在 FNV-1a 叶子哈希之上。

02T3 近似克隆——树编辑距离(TSED)

判决两个 AST 结构上几乎相同、但 token 流并不相同的单元——被改形、被重写的拷贝。

TSED(a, b) = (max(n1, n2) - ted(a, b)) / max(n1, n2)
clone      ⇔ TSED >= 0.85
cloneDecidesWith (num, den) t n1 n2 = (mx - t) * den >= num * mx  where mx = max n1 n2

ted 是单位代价的 Zhang-Shasha(删 = 插 = 1,kind 码相同时 relabel = 0,否则 1),恒为整数,所以比较是精确的交叉相乘,边界两侧都可判定:在 max = 100ted 15 是克隆、ted 16 不是。两条可证 可采纳的 O(1) 预筛在任何 TED 之前就砍掉候选对——对 q ∈ {min(n1,n2), I}q · tsedDen < tsedNum · max,其中 I = Σ_label min(c1, c2)——而预筛 用的正是判决将要用的那个 85/100,这个同一性恰恰就是它可采纳的原因。

03文档重复——shingling + MinHash/LSH

判定两段文档文本——markdown 段落、注释块、docstring——是否互为近似重复。

dupDecidesWith num den inter union  =  inter * den >= num * union

dupVerdictWith (num, den, vfloor) inter union run
  =  dupDecidesWith num den inter union  ||  run >= vfloor

生产绑定为 (80, 100, 50):Jaccard 达到 0.80(整数精确判定), 或者存在至少 50 词的逐字连续段。interunion 由核自己 从升序去重的 shingle 集合算出——过 wire 的是原始计数、绝不是比值,否则"复核发生在 Haskell" 就是一句空话。MinHash/LSH 只是粗筛,且无 RNG:置换下标本身就是盐。R 个 shingle 的连续段横跨 R + k - 1 个词。

04结构判决——树尺度熵,七条判轴

判的是树而不是文件:目录几何、命名分布、引用局部性、文档覆盖、文档陈旧、冗余。

tsallis2 cs     = 1 - Σ (c/N)^2            -- and = 0 when N == 0
tsallis2Norm cs = tsallis2 cs / (1 - 1/n)  -- n = nonzero bins, n > 1
chi2 pairs      = Σ_{r > 0} (p - q)^2 / q  -- p = o/Σo, q = r/Σr
perMille r      = floor (r * 1000)
raw   = Σ_axes (penalty * violCost)
score = max 0 (scale - raw `div` judgedAxisCount)

Shannon 熵与 KL 散度需要对数——无理数,因而不可精确判定——所以本家族改为发布 同族中在有理数下封闭的成员:Tsallis-2 多样性与 χ² f-散度,全程跑在 Data.Ratio 上。 若某个 bin 参考质量为 0 而观测质量非 0,χ² 返回 Nothing:那是一次拒绝而不是一个零, 报告会点名那些目录。每条判轴的罚分都是目录计数;judgedAxisCount 依可选 事实表是否上 wire 取 5、6 或 7。

05评分与 ADR-006 棘轮

把七条判轴折叠成一个 0–1000 的分数,并对着一份只准收紧的基线银行把门。

raw     = sum_i (w_i * p_i * violCost)
wTotal  = sum_i w_i                            -- derived, never a literal
score   = max 0 (scoreScale - raw `div` wTotal)
p(x) = 0                              if x <= S
     = pMax                           if H <= S          -- degenerate fallback
     = pMax * ((x - S) / (H - S))^2   otherwise

m = median(x)
r = median( max(x/m, m/x) )                    -- >= 1 by construction
S = clamp(floor(m * r^k), [softMin, softMax])
tolerated(c) = max (c * tolNum `div` tolDen) (c + tolAbs)

added   = current \ baseline        -- non-empty => fail
removed = baseline \ current        -- informational; drives the shrink

判轴 0 是唯一一条不是计数的轴:对文件尺寸的凸罚,精确 Rational, 越过硬线之后仍单调上升——pMax落在 H 上的取值,不是上限。 软线 S 是仓库自身冻结 LOC 分布的一个统计量,即把 S = clamp(median + k·MAD, …) 改写成乘性形式,从而全程不取对数;它只在 establish 时导出,随后冻进基线。fail 位是四个具名条件的析取——ratchet_overdiscrete_addedfloordedup_budget——回包同时带回究竟 是哪几个成立。

06图存活性与死代码判决

只回答一个问题——哪些文件是任何活物都够不着的?——在一张节点身份即行下标、 wire 上没有任何文本的引用图上作答。

arcs  = { (s,d) | [s,d,_kind,rung] ∈ edges,  rung <= minRung }
reach = ⋃ { reachable(G, s) | s ∈ entries(entryMask, flags) }
public     = testBit flags 0
referenced = indeg >= 1 over kept arcs
judged     = i ∉ reach
code       = 1 + public + 2*referenced    -- the lookup table is the authority

四个码,结构上互相隔离,使"已导出但无人引用的 API"永远不会塌进普通的 dead: 1 unref_private2 unref_public3 unreach_private4 unreach_public。解析从不猜:一个 site 按本语言的梯级依次向上走,第一个产出 恰好一个在域候选的梯级胜出;多于一个即 Unresolved,而 External 是正确的终局答案、不是漏判。环只报不判——没有入口种子的环岛仅凭可达性 就已判死,无需特例。import 边精度实测 38/40 = 0.95,跨五个钉扎语料,对着 ≥ 0.90 的门;样本在任何解析器存在之前就已冻结。

07三信号 join

把相似度 × 图位置 × 变动史合成四个候选码之一——然后刻意拒绝据此行动。

(1, [1,2,3,4], [])    -- merge_candidate:  sim + graph + both referenced + distinct SCCs
(2, [1,2,5],   [6])   -- delete_candidate: sim + graph + dead flank, RG10 guard clear
(3, [1,2,7,8], [])    -- churn_hotspot:    sim + graph + cochange + rewrite
rewriteHot  = total > 0 && (rewrote_a + rewrote_b) * rewriteDen >= total * rewriteNum
cochangeHot = cochange >= cochangeFloor
legsMask    = legSim .|. (if graphBoth then legGraph else 0) .|. legChurn

优先级是数据而不是守卫顺序:第一行"required 位全部成立且 forbidden 位 全部为空"的规则胜出,否则码为 0。把顺序做成数据,正是让性质电池能够证伪它——把表旋转一下再判 同一行,答案必须翻转。每条会 gate 的规则都要求 graph 位,所以掩码 5(图腿缺席)只可能带码 0: 缺一条图腿就拒绝 gate,而不是假装入度为 0。join 产出的是候选;fail 位里不出现任何 verdict 码,ce join 恒以成功退出。

08拆分 ROI 缝价(四条腿)

用一个数而不是一句口号回答"这个长文件值不值得拆、从哪儿拆"——它是建议, 永不进结构分数、也永不进 fail 位。

benefitMilli(u) = max 0 (floor (1000 * (p(total) - p(end_u) - p(total - end_u))))

costMilli(u)    = crossRefs(u)      * roiRefMilli
                + cutClones(end_u)  * roiCloneMilli
                + crossChurn(u)     * roiChurnMilli
                + roiPhiMilli

viable          ⇔ b >= c            -- ROI >= 1, evaluated without division

收益是拆分退回来的那部分软区罚分,算在与判决家族同一条凸曲线上——直接 import 而不是重新推一遍;因为 p 是凸的且 p(0) = 0,故超可加,所以 那个方括号非负。最佳缝的选取是对 ROI 的精确有理数 argmax,用交叉相乘的 b % c 比较。 一个有 n 个顶层单元的文件产出 n - 1 条缝。长而内聚会得到一条带 数字的豁免,长而可拆则得到一条切割线。

09编辑四分类(更新监督)

把每一次被监督的编辑归约成逐文件对的四个整数计数——matched / novel / moved / deleted——从而让"这次更新是堆上去的,不是改上去的"成为一次测量。

siteOpens s n  =  n * movedCost + s  <  n * plainCost

destFloor      =  least n with siteOpens siteCostCross n   =  2
accepted   = isStart && distinctEvidence >= destFloor && anchored
anchored   = any (\(_,_,w) -> w >= anchorFloor) evidence

跨文件证据下限是推出来的而不是调出来的:siteCostCross = 2 使单条跨文件行恰好打平(1*1 + 2 = 3 = 1*3),而打平不开站,于是 destFloor 求值为 2——那个平局就是巧合拒斥本身。唯一一个"拍板而非推导"的 常数是 anchorFloor = 19:在双语料影子消融里,被凭空发明出来的站点其最宽锚点实测 16 个 alnum 字符,而最细的真实锚点是 19,故 19 是那个"杀掉全部实测巧合、同时保住全部实测真站" 窗口的上沿。L2 的 delta 只在一个方向上单调——普通行可以被改判为 moved,反向绝不可以。

10分数轨迹——趋势斜率判决

对仓库历史只回答一个问题:check 分数在涨、在平、还是在跌,每天跌多少。

x_i   = ts_i % 86400                 -- seconds to days, exact ratio
y_i   = (score_i * 1000000) % scale_i

slope = (n * Σ(x_i*y_i) - Σx_i * Σy_i) / (n * Σ(x_i²) - (Σx_i)²)
slope < -band  → 2  (degrading)
slope >  band  → 0  (improving)
otherwise      → 1  (flat)        where band = floorMicro

这里的 %Data.Ratio 的精确比构造子,不是取模; y 把每次提交重新归一到固定的 10⁶ 满量程网格上,使不同 scoreScale 下 测得的行可以通约。行序被刻意不加约束——最小二乘与顺序无关,而 first-parent 序是拓扑序而非 时间序,所以 rebase 过的提交是合法输入。低于 minPoints、或时间戳方差为零时,斜率是 Nothing——是缺席,不是编造出来的"持平"——且 fail 位保持 false。在默认地板 0 下, "下滑"可以被报出来但不能判负。

FPR 纪律——一条规则凭什么获得 deny 的资格

11FPR 纪律与守卫档位阶梯

一道架在非确定性写手之上的确定性门:守卫挂在 Write|EditPreToolUse 上,用精确算术回答每一次待落盘的写——而一条规则类只有先付清代价, 才有资格执法。

TIERS = ["observe", "warn", "ask", "deny"]
PROMOTED_DEFAULT = "deny"
档位permissionDecision效果
observe无——打印前即返回只写一行 feed,不注入任何文本
warnallow编辑照常落盘,理由作为可见告警浮出
askask弹给用户确认
denydeny写被拒,理由指向已存在的 file:line

确定性是靠重放这次写而不是估计它买来的:resulting_lines 算出精确的写后行数;任何本来就会失败的工具调用——文件不存在、非 replace_all 的 匹配不唯一——都返回 None,规则保持沉默。门永远不去判决一次落不了盘的写。无法识别的 [guard] mode 会解析成 observe (ce.toml ERROR: …) 字符串而不是原样透传, 因为一个透传的拼写错误曾经把所有执法路径全部解除武装,而会话横幅仍在打印"已武装"。

为什么不能直接把一条规则类设成 deny。这道阶梯是写进计划的 路线,所以默认既不能永远停在 warn、也不能一上来就是 deny。 准入是定量的:M3 验收判据是500 次真实正常编辑中误拦 ≤ 1 次,且明确不许拿 N=1 的演示充数;M4 主门是500 次真实正常编辑上 FPR ≤ 1%,评估集须在 实现之前预注册冻结,≥ 200 个编辑样本,≥ 50% 取自真实 agent 记录。样本纯度是门的一部分:只有 observe 模式与无守卫时期的会话可被采样,守卫已经介入过的编辑必须排除——否则 FPR 会被守卫自己的 塑形效应压低,deny 准入就成了自我背书。到今天为止只有两类付清了:T1/T2 精确重复写入,以及 文件 > 750 行的硬预算突破。其余每一条规则都因为没有自己的记录而停在 observe。

在册的重放把 git 线性历史当作真实编辑流——先探测、后落盘,跑在 t = 50min_distinct = 7 这两个出厂默认档上:630 个事件、 35 个块、仲裁后假阳 0 → 每 500 次 0.00。那 35 个自仓块全部仲裁为真阳并已被清理, 克隆块预算同步下棘 251 → 211 → 209 → 205 → 202。

cap      = thresholds.file_lines_fail          // default 750
breach   ⇔ cap != 0 && lines > cap

permille = (lines - S) * 1000 / (H - S)
0   ..= 249  →  observe
250 ..= 750  →  warn
751 ..       →  ask

区内档位映射就是把同一套纪律用在自己身上:[guard] zone_tiers 默认关闭,所以区规则只写 feed、不注入任何东西。它还没有自己的 FPR 台账, 因此默认不执法——zone 这个 feed 事件的存在意义,恰恰就是"将来任何一次区→档位的 提档必须据以论证 FPR 的那份逐规则记录"。告警按 (规则, 文件, 会话) 限一次并按 token 预算裁剪; 执法不限流,因为一次 deny 不是上下文膨胀。

12确定性擦除——安全谓词

判决擦除计划的行,覆盖三个可证明安全的类——dead_fileverbatim_doct1_twin;其余行都以具名 reason code 拒绝。

class  = 0 dead_file | 1 verbatim_doc | 2 t1_twin
reason = 0 eraseable | 1 language_unresolved | 2 not_full_segment
         3 bytes_differ | 4 copy_not_dead | 5 unit_not_covered

Rust 从三个来源家族组装整数事实;Haskell 执行固定的首个失败谓词。 超过上限的降级应答不授权任何擦除,而安全谓词没有可调旋钮。

两条诚实边界

PreToolUse 是一层行为塑形不是安全边界—— agent 可以用 Bash: echo >>sed -i 绕过它;兜底是跑在 git diff 上、与写入工具无关的 Stop 审计,加上 CI 门。而这个 hook 是 刻意fail-open 的:任何内部失败都放行该次编辑,降级的那次运行落进 observe feed, 而不是被悄悄读成"没有重复"。这两条都不是"以后再补"的缺口——它们都写进了计划,因为一道对自己 覆盖范围撒谎的门,比一道如实声明覆盖范围的门更糟。