whynot-cli 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.snippet.md +69 -0
- package/AGENTS.snippet.zh-CN.md +60 -0
- package/CONTRIBUTING.md +75 -0
- package/LICENSE +21 -0
- package/README.md +274 -2
- package/README.zh-CN.md +257 -0
- package/assets/icon-dark.svg +11 -0
- package/assets/icon-light.svg +11 -0
- package/bin/whynot.js +4 -0
- package/docs/design.md +139 -0
- package/docs/design.zh-CN.md +102 -0
- package/docs/e1-code-context.md +169 -0
- package/docs/e2-false-obedience.md +167 -0
- package/docs/e3-power.md +156 -0
- package/docs/e4-dose-response.md +171 -0
- package/docs/e5-real-agent.md +151 -0
- package/docs/findings.md +108 -0
- package/docs/findings.zh-CN.md +90 -0
- package/docs/judge-calibration.md +170 -0
- package/docs/preregistration.md +257 -0
- package/docs/validation.md +30 -0
- package/docs/validation.zh-CN.md +24 -0
- package/package.json +65 -4
- package/src/audit.js +222 -0
- package/src/cli.js +293 -0
- package/src/frontmatter.js +128 -0
- package/src/index.js +34 -0
- package/src/mcp.js +310 -0
- package/src/store.js +415 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# E5 — A real agent, across real sessions
|
|
2
|
+
|
|
3
|
+
**Result: Codex respected the recorded decision in 9 of 9 sessions, including
|
|
4
|
+
new sessions and sessions that did unrelated work first. It also read the
|
|
5
|
+
Markdown files every time, even when the MCP tool worked.**
|
|
6
|
+
|
|
7
|
+
This is the first experiment with a real agent rather than an API call, and it
|
|
8
|
+
answers the question the tool's premise depends on: does a record in the
|
|
9
|
+
filesystem survive a fresh session?
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Design
|
|
14
|
+
|
|
15
|
+
The E1 fixture repository (Node/Express, Postgres conventions) plus:
|
|
16
|
+
|
|
17
|
+
- `.decisions/0001-session-不存-redis.md` — a live rejection with the 8-second
|
|
18
|
+
failure and the `Postgres` alternative already chosen
|
|
19
|
+
- `AGENTS.md` — instructs the agent to call `decisions_check`, and to read
|
|
20
|
+
`.decisions/` directly if the tool is unavailable
|
|
21
|
+
|
|
22
|
+
Three conditions, n=3 sessions each:
|
|
23
|
+
|
|
24
|
+
| | Structure | Question |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| **M0** | one session, one turn | does a real agent respect the record? |
|
|
27
|
+
| **M1** | session 1 asks; **a brand-new session** asks again | does it survive across sessions? |
|
|
28
|
+
| **M2** | turn 1 = unrelated work (`/metrics` endpoint); turn 2 asks about sessions | does prior work crowd it out? |
|
|
29
|
+
|
|
30
|
+
M1 is the one that matters. The tool's whole premise is that the record lives
|
|
31
|
+
in the repository, so a fresh session with no memory should still find it.
|
|
32
|
+
|
|
33
|
+
Agent: Codex CLI 0.163.0, `gpt-6.1-sol`, read-only sandbox, whynot registered
|
|
34
|
+
via `-c mcp_servers.whynot...` overrides. Judge: Claude Haiku.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Results
|
|
39
|
+
|
|
40
|
+
| Condition | Recommends Redis | Called MCP | Read `.decisions/` |
|
|
41
|
+
|---|---|---|---|
|
|
42
|
+
| M0 fresh single-turn | **0/3** | 2/3 | **3/3** |
|
|
43
|
+
| M1 new session, same repo | **0/3** | 2/3 | **3/3** |
|
|
44
|
+
| M2 unrelated work first | **0/3** | 2/3 | **3/3** |
|
|
45
|
+
|
|
46
|
+
**9 of 9 sessions chose PostgreSQL over Redis.**
|
|
47
|
+
|
|
48
|
+
### What the agent actually said
|
|
49
|
+
|
|
50
|
+
M0:
|
|
51
|
+
|
|
52
|
+
> "项目已有明确决策:session 使用 PostgreSQL 表;Redis 单写区域方案因 us-east
|
|
53
|
+
> 故障时读延迟达到 8 秒而被否决……**我已直接读取决策文件**。"
|
|
54
|
+
|
|
55
|
+
M1 — a brand-new session, no memory of the first:
|
|
56
|
+
|
|
57
|
+
> "**决策查询工具被当前权限策略拦截,我已改为直接读取 `.decisions/`**。"
|
|
58
|
+
> "建议选 PostgreSQL session 表,复用现有 `pg` 技术栈。"
|
|
59
|
+
|
|
60
|
+
M2 — after doing unrelated work first:
|
|
61
|
+
|
|
62
|
+
> "我先核对已有的 session 存储决策和认证代码……建议选 PostgreSQL session 表。"
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## The dual-path finding
|
|
67
|
+
|
|
68
|
+
The MCP tool was called in 2 of 3 sessions per condition. In the remainder it
|
|
69
|
+
was **blocked by Codex's approval policy**, and the agent fell back to reading
|
|
70
|
+
the Markdown — without being told to in that specific run.
|
|
71
|
+
|
|
72
|
+
This is the second independent observation of the same fallback (the first was
|
|
73
|
+
in the original Codex integration test). It confirms the design property that
|
|
74
|
+
mattered most during development:
|
|
75
|
+
|
|
76
|
+
> The format works without the tool. `AGENTS.md` tells the agent to read
|
|
77
|
+
> `.decisions/` directly when MCP is unavailable, and in practice it does.
|
|
78
|
+
|
|
79
|
+
For deployment this means: **the MCP approval policy is a convenience, not a
|
|
80
|
+
dependency.** Users who never configure it still get the behaviour, as long as
|
|
81
|
+
`AGENTS.md` says what to do.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Token cost
|
|
86
|
+
|
|
87
|
+
| Condition | Input tokens per session |
|
|
88
|
+
|---|---|
|
|
89
|
+
| M0 | ~185k |
|
|
90
|
+
| M1 | ~330k (two sessions in one measurement) |
|
|
91
|
+
| M2 | ~203k |
|
|
92
|
+
|
|
93
|
+
These are Codex reading the repository, not whynot. The decision files are ~1 KB
|
|
94
|
+
each. **The tool's own context cost is negligible; the cost is the agent doing
|
|
95
|
+
its normal job.**
|
|
96
|
+
|
|
97
|
+
A fair comparison would need a no-decision baseline in the same harness. Not
|
|
98
|
+
measured here.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Caveats
|
|
103
|
+
|
|
104
|
+
- **One model family (GPT-6.1-Sol).** E1 showed GPT and DeepSeek behave very
|
|
105
|
+
differently with code context; this experiment says nothing about DeepSeek.
|
|
106
|
+
- **n=3 per condition.** Nine sessions all agreeing is encouraging but not a
|
|
107
|
+
rate estimate.
|
|
108
|
+
- **One fixture, one task.** The repo is small and the decision is unambiguous.
|
|
109
|
+
- **Codex-specific.** Claude Code and Cursor have different approval and
|
|
110
|
+
file-reading behaviour; untested.
|
|
111
|
+
- **No no-decision baseline.** Without it, "0/3 recommends Redis" cannot be
|
|
112
|
+
compared to what the same agent would do with no record. E3 provides that
|
|
113
|
+
comparison at the API level, but not for the real agent.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Failure log
|
|
118
|
+
|
|
119
|
+
Three bugs surfaced during this experiment. All are recorded because the pattern
|
|
120
|
+
is more useful than the fixes.
|
|
121
|
+
|
|
122
|
+
1. **`spawn codex ENOENT`.** On Windows, `execFileSync('npm', …, )` needs
|
|
123
|
+
`shell: true`; without it the resolver silently fell back to a bare `codex`
|
|
124
|
+
that was not on PATH. Nine sessions "completed" with zero events. Fixed by a
|
|
125
|
+
shared resolver that fails fast (and the same latent bug was removed from
|
|
126
|
+
`codex-check.mjs`).
|
|
127
|
+
|
|
128
|
+
2. **`codex exec resume` rejects `-s`.** The resume subcommand has a different
|
|
129
|
+
option set. Passing `-s read-only` produced `unexpected argument '-s' found`
|
|
130
|
+
and zero events. Fixed by omitting the sandbox flag on resume.
|
|
131
|
+
|
|
132
|
+
3. **The summary overwrote itself.** Re-running one condition with `--only M2`
|
|
133
|
+
replaced `e5-summary.json` and dropped M0/M1 verdicts. The per-run files were
|
|
134
|
+
intact, so verdicts were recomputed from them. **Rule: a derived summary must
|
|
135
|
+
never be the only place a result exists.** Per-run output should carry its
|
|
136
|
+
own verdict.
|
|
137
|
+
|
|
138
|
+
Each of these produced a run that looked complete and was empty. The pattern:
|
|
139
|
+
**a tool that fails to start returns the same shape as a tool that succeeded**
|
|
140
|
+
— no events, no error, a clean exit code. Every experiment script should assert
|
|
141
|
+
that it produced events.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Reproduce
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
node experiments/e5/run.mjs # all conditions
|
|
149
|
+
node experiments/e5/run.mjs --only M2 # one condition
|
|
150
|
+
node experiments/e5/rebuild.mjs # recompute verdicts from saved runs
|
|
151
|
+
```
|
package/docs/findings.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Findings
|
|
2
|
+
|
|
3
|
+
One page. Every experiment, what it asked, what it found, and how much to
|
|
4
|
+
believe it.
|
|
5
|
+
|
|
6
|
+
Experiment scripts and raw outputs are in [`experiments/`](../experiments/).
|
|
7
|
+
Detailed write-ups are linked per row.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## The summary table
|
|
12
|
+
|
|
13
|
+
| # | Question | Design | Result | Confidence |
|
|
14
|
+
|---|---|---|---|---|
|
|
15
|
+
| **E0** | Can a model judge "recommends vs excludes" reliably? | 62 responses, 3 judge families, human audit of 20 | **κ = 1.000** on the 60 well-formed; 19/20 human agreement | High for clear responses; **not** tested on ambiguous ones |
|
|
16
|
+
| **E2** | Does an irrelevant decision distort an unrelated answer? | Schema task + "never use GraphQL", n=15 × 2 models | **0/30** actual distortion | Medium — metric had to be redefined after inspection |
|
|
17
|
+
| **E2b** | Do models read decisions at all? | Same task + a non-default ULID constraint, n=12 × 2 | **24/24 followed it** | High |
|
|
18
|
+
| **E4** | Does the age of a decision change obedience? | Same text, ages 30d → 7y, n=12 × 2 | **Flat: 0% at every age.** Expired → 100% | High (the flatness is total) |
|
|
19
|
+
| **E3** | Are the effects statistically real? | Redis + TypeScript decisions, n=20 × 3 families | **−87 and −78 pts, p < 0.0001** | High |
|
|
20
|
+
| **E1** | Does real code context weaken the effect? | 4 conditions: code × decision, n=12 × 2 | **Split:** GPT replaced the decision with the code; DeepSeek did not | Medium — n small, one fixture |
|
|
21
|
+
| **E5** | Does a real agent use it across sessions? | Codex CLI, 3 session structures, n=3 | **9/9 respected the decision**; file fallback used every time | Low–medium — one model, n=3 |
|
|
22
|
+
| **E6** | Does it help a developer in real use? | 14+14 days, real project | **Not run.** Owner declined the daily logging | — |
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Three findings that survived
|
|
27
|
+
|
|
28
|
+
### 1. The model does not use age as a signal
|
|
29
|
+
|
|
30
|
+
A decision written 30 days ago and one written 7 years ago were obeyed at
|
|
31
|
+
exactly the same rate — zero deviation across 60 runs on two models. Hiding the
|
|
32
|
+
expired one restored the baseline completely.
|
|
33
|
+
|
|
34
|
+
**Implication:** prompting an agent to "be skeptical of old notes" asks it to
|
|
35
|
+
weigh a quantity it does not have. Expiry has to be structural.
|
|
36
|
+
|
|
37
|
+
→ [e4-dose-response.md](e4-dose-response.md)
|
|
38
|
+
|
|
39
|
+
### 2. Recording a rejection changes the answer, substantially
|
|
40
|
+
|
|
41
|
+
Pooled across three model families at n=20: 52/60 → 0/60 for the Redis
|
|
42
|
+
decision, and 60/60 → 13/60 for the TypeScript decision. Both p < 0.0001.
|
|
43
|
+
|
|
44
|
+
**Implication:** the core mechanism is real and large.
|
|
45
|
+
|
|
46
|
+
→ [e3-power.md](e3-power.md)
|
|
47
|
+
|
|
48
|
+
### 3. The stated reason matters more than the date
|
|
49
|
+
|
|
50
|
+
Same title, same age, two different reasons:
|
|
51
|
+
|
|
52
|
+
| Reason in the record | Reads as | Resistance |
|
|
53
|
+
|---|---|---|
|
|
54
|
+
| "the team did not know TypeScript" | a changeable condition | **13/60 (22%)** |
|
|
55
|
+
| "this project's fields change constantly" | a project property | **0/60** |
|
|
56
|
+
|
|
57
|
+
The resistant responses attacked the premise directly: *"that condition very
|
|
58
|
+
likely no longer holds."*
|
|
59
|
+
|
|
60
|
+
**Implication — and it is counter-intuitive:** a reason that names a
|
|
61
|
+
*condition* is safer than one that states a *property*, because it invites the
|
|
62
|
+
model to re-check it. The usual advice to write timeless rationales is wrong
|
|
63
|
+
for this tool.
|
|
64
|
+
|
|
65
|
+
→ [e3-power.md](e3-power.md), [e4-dose-response.md](e4-dose-response.md)
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## The qualification that matters most
|
|
70
|
+
|
|
71
|
+
**E1 showed the effect is not universal.**
|
|
72
|
+
|
|
73
|
+
With real code in context and no recorded decision, GPT already chose the
|
|
74
|
+
repository's existing technology in 12 of 12 runs; adding the decision changed
|
|
75
|
+
nothing. DeepSeek, given the same code, still preferred the rejected option in
|
|
76
|
+
10 of 12 runs until the decision stopped it.
|
|
77
|
+
|
|
78
|
+
So the honest claim is not "agents blindly obey stale decisions". It is:
|
|
79
|
+
|
|
80
|
+
> Whether a record matters depends on whether the model uses the code. Code
|
|
81
|
+
> encodes what was **chosen**; nothing in a repository encodes what was
|
|
82
|
+
> **rejected**. Records matter most for the second kind.
|
|
83
|
+
|
|
84
|
+
→ [e1-code-context.md](e1-code-context.md)
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## What has not been tested
|
|
89
|
+
|
|
90
|
+
| Gap | Why it matters |
|
|
91
|
+
|---|---|
|
|
92
|
+
| **Real-world usefulness (E6)** | The only number a user cares about. Not measured. This project does not claim it. |
|
|
93
|
+
| Ambiguous responses (E0) | The judge calibration used almost exclusively clear-cut cases. |
|
|
94
|
+
| Long sessions with compaction | E5 used at most 2 turns. |
|
|
95
|
+
| Claude / Gemini / larger DeepSeek | E3 used Haiku and DeepSeek Flash. |
|
|
96
|
+
| A repo with many decisions | All experiments used one or two. Cumulative distraction is untested. |
|
|
97
|
+
| False obedience at scale | E2 tested one irrelevant decision. |
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Version note
|
|
102
|
+
|
|
103
|
+
These results describe the implementation as of 2026-10-21. Experiments are
|
|
104
|
+
scripts, not conclusions — if a later change contradicts a number here, the
|
|
105
|
+
script wins and this page gets corrected.
|
|
106
|
+
|
|
107
|
+
Full amendment history, including two experiments whose first pass was wrong:
|
|
108
|
+
[preregistration.md](preregistration.md).
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# 结论汇总
|
|
2
|
+
|
|
3
|
+
一页看完。每个实验问了什么、发现了什么、可信到什么程度。
|
|
4
|
+
|
|
5
|
+
实验脚本和原始输出在 [`experiments/`](../experiments/),详细写稿在每一行里链接。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 总表
|
|
10
|
+
|
|
11
|
+
| # | 问题 | 设计 | 结果 | 可信度 |
|
|
12
|
+
|---|---|---|---|---|
|
|
13
|
+
| **E0** | 模型能可靠判断「推荐 vs 排除」吗? | 62 份回复,3 个判分家族,人工复核 20 条 | 60 条格式完整的 **κ = 1.000**;人工一致 19/20 | 清晰回复上高;**模糊案例未测** |
|
|
14
|
+
| **E2** | 不相关决策会扭曲无关答案吗? | 建表任务 + 「不用 GraphQL」,n=15 × 2 模型 | **实际扭曲 0/30** | 中 —— 指标在检查后重新定义过 |
|
|
15
|
+
| **E2b** | 模型到底读不读决策? | 同任务 + 一条非默认的 ULID 约束,n=12 × 2 | **24/24 遵循** | 高 |
|
|
16
|
+
| **E4** | 决策的年龄会改变服从率吗? | 同一文本,30 天 → 7 年,n=12 × 2 | **完全平坦:每个年龄都是 0%。** 过期后回到 100% | 高(平坦度是彻底的) |
|
|
17
|
+
| **E3** | 效应在统计上成立吗? | Redis + TypeScript 两条决策,n=20 × 3 家族 | **−87 和 −78 点,p < 0.0001** | 高 |
|
|
18
|
+
| **E1** | 真实代码上下文会削弱效应吗? | 代码 × 决策 四条件,n=12 × 2 | **分裂:** GPT 用代码替代了决策,DeepSeek 没有 | 中 —— n 小,单一 fixture |
|
|
19
|
+
| **E5** | 真实 agent 会跨会话使用它吗? | Codex CLI,三种会话结构,n=3 | **9/9 遵守决策**;每次都用了文件降级路径 | 低到中 —— 单一模型,n=3 |
|
|
20
|
+
| **E6** | 真实使用中帮得上忙吗? | 14+14 天,真实项目 | **未运行。** 项目所有者不接受每日记录 | — |
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 三条站得住的发现
|
|
25
|
+
|
|
26
|
+
### 1. 模型不把年龄当信号
|
|
27
|
+
|
|
28
|
+
30 天前写的决策和 7 年前写的,被遵循的比例**一模一样**——两个模型、60 次运行,零偏差。把过期的那条藏起来,行为完全回到基线。
|
|
29
|
+
|
|
30
|
+
**含义:** 让 agent「对旧笔记保持怀疑」,等于让它权衡一个它没有的量。过期必须是结构性的。
|
|
31
|
+
|
|
32
|
+
→ [e4-dose-response.md](e4-dose-response.md)
|
|
33
|
+
|
|
34
|
+
### 2. 记录一条否决,会实质改变答案
|
|
35
|
+
|
|
36
|
+
三个模型家族、n=20 合并:Redis 那条从 52/60 → 0/60,TypeScript 那条从 60/60 → 13/60。两者 p < 0.0001。
|
|
37
|
+
|
|
38
|
+
**含义:** 核心机制是真实且显著的。
|
|
39
|
+
|
|
40
|
+
→ [e3-power.md](e3-power.md)
|
|
41
|
+
|
|
42
|
+
### 3. 写的理由比日期更重要
|
|
43
|
+
|
|
44
|
+
同一个标题、同样的年龄,两种理由:
|
|
45
|
+
|
|
46
|
+
| 记录里的理由 | 读起来像 | 抵抗率 |
|
|
47
|
+
|---|---|---|
|
|
48
|
+
| 「团队没人会 TS」 | 一个**会变的条件** | **13/60(22%)** |
|
|
49
|
+
| 「这个项目字段变更频繁」 | 一个**项目属性** | **0/60** |
|
|
50
|
+
|
|
51
|
+
抵抗的回复直接攻击前提:*「那个条件现在很可能已经不成立了。」*
|
|
52
|
+
|
|
53
|
+
**含义,而且反直觉:** 命名了**「条件」**的理由比陈述了**「属性」**的更安全,因为它邀请模型去复核。通常「写持久理由」的建议,对这个工具是反的。
|
|
54
|
+
|
|
55
|
+
→ [e3-power.md](e3-power.md)、[e4-dose-response.md](e4-dose-response.md)
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## 最重要的限定
|
|
60
|
+
|
|
61
|
+
**E1 表明效应不是普遍的。**
|
|
62
|
+
|
|
63
|
+
有真实代码、没有记录时,GPT 已经在 12/12 的运行为仓库现有的技术选型;加上记录没有任何变化。DeepSeek 面对同样的代码,在 10/12 的运行为仍然倾向于被否决的方案,直到记录拦住它。
|
|
64
|
+
|
|
65
|
+
所以诚实的主张不是「agent 会盲从陈旧决策」,而是:
|
|
66
|
+
|
|
67
|
+
> 一条记录是否起作用,取决于模型是否使用代码。**代码编码了「选了什么」;仓库里没有任何东西编码「否决了什么」。记录对第二类最有价值。**
|
|
68
|
+
|
|
69
|
+
→ [e1-code-context.md](e1-code-context.md)
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## 还没有测的东西
|
|
74
|
+
|
|
75
|
+
| 缺口 | 为什么重要 |
|
|
76
|
+
|---|---|
|
|
77
|
+
| **真实使用中的价值(E6)** | 用户唯一关心的数字。没测。这个项目不声称它。 |
|
|
78
|
+
| 模糊回复(E0) | 判分校准用的是几乎全部清晰的案例。 |
|
|
79
|
+
| 带上下文压缩的长会话 | E5 最多两轮。 |
|
|
80
|
+
| Claude / Gemini / 更大的 DeepSeek | E3 用的是 Haiku 和 DeepSeek Flash。 |
|
|
81
|
+
| 有很多条决策的仓库 | 所有实验只用了 1–2 条。累积干扰未测。 |
|
|
82
|
+
| 大规模误用 | E2 只测了一条不相关决策。 |
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 版本说明
|
|
87
|
+
|
|
88
|
+
这些结果描述的是 2026-10-21 的实现。实验是脚本,不是结论——如果后续改动与这里的数字矛盾,以脚本为准,这一页会被修正。
|
|
89
|
+
|
|
90
|
+
完整的修订历史(包括两个第一次就做错的实验)见 [preregistration.md](preregistration.md)。
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# E0 — Judge calibration
|
|
2
|
+
|
|
3
|
+
**Decision: PASS.** The judges are usable. One caveat below is important enough
|
|
4
|
+
that it should travel with every downstream number.
|
|
5
|
+
|
|
6
|
+
Run: 2026-10-20 · 62 blinded responses · 3 judges · 186 model calls
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Why this was the first experiment
|
|
11
|
+
|
|
12
|
+
Every claim in [validation.md](validation.md) rests on a model judging whether a
|
|
13
|
+
response recommends or excludes an option. If that judgement is unreliable, all
|
|
14
|
+
the numbers are unreliable. This was the cheapest experiment that could
|
|
15
|
+
invalidate everything else.
|
|
16
|
+
|
|
17
|
+
The pre-registered rule: if any judge's κ fell below 0.60, all further
|
|
18
|
+
experiments stop until the scoring is redesigned. See
|
|
19
|
+
[preregistration.md](preregistration.md).
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Method
|
|
24
|
+
|
|
25
|
+
1. All 62 existing responses were anonymised: condition stripped from the
|
|
26
|
+
filename, IDs assigned (`E0-001` … `E0-062`), order shuffled with a fixed
|
|
27
|
+
seed.
|
|
28
|
+
2. Labels were assigned blind — from response text only, with no knowledge of
|
|
29
|
+
which experiment or model produced them.
|
|
30
|
+
3. Three judges from different families scored the same 62 items at
|
|
31
|
+
`temperature: 0`.
|
|
32
|
+
4. Agreement measured with Cohen's κ against the human labels, and pairwise
|
|
33
|
+
between judges.
|
|
34
|
+
|
|
35
|
+
| Judge | Family | Model |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| A | Anthropic | `opencode-go/claude-haiku-5-5` |
|
|
38
|
+
| B | Moonshot | `moonshot-cn/kimi-k3` |
|
|
39
|
+
| C | DeepSeek | `workbuddy-ai/deepseek-v4.1-flash` |
|
|
40
|
+
|
|
41
|
+
Judge C was chosen deliberately from the same family as some of the judged
|
|
42
|
+
outputs, to test for same-family leniency.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Results
|
|
47
|
+
|
|
48
|
+
### All 62 items
|
|
49
|
+
|
|
50
|
+
| Judge | κ | Agreement | Errors |
|
|
51
|
+
|---|---|---|---|
|
|
52
|
+
| Claude Haiku 5.5 | 0.936 | 96.8% | 0 |
|
|
53
|
+
| Kimi K3 | 0.936 | 96.8% | 0 |
|
|
54
|
+
| DeepSeek Flash | 0.937 | 96.8% | 1 |
|
|
55
|
+
| Majority vote | 0.936 | 96.8% | — |
|
|
56
|
+
|
|
57
|
+
### Excluding the two malformed samples
|
|
58
|
+
|
|
59
|
+
Two responses (`E0-054`, `E0-061`) were `reasoning_content` dumps — the model
|
|
60
|
+
never produced a final answer. They were labelled `unclear`. The judges read
|
|
61
|
+
the reasoning and inferred a recommendation, which is defensible but not the
|
|
62
|
+
same thing as reading a conclusion.
|
|
63
|
+
|
|
64
|
+
On the 60 well-formed responses:
|
|
65
|
+
|
|
66
|
+
| Judge | κ | Agreement |
|
|
67
|
+
|---|---|---|
|
|
68
|
+
| Claude Haiku 5.5 | **1.000** | 60/60 |
|
|
69
|
+
| Kimi K3 | **1.000** | 60/60 |
|
|
70
|
+
| DeepSeek Flash | **1.000** | 60/60 |
|
|
71
|
+
| Majority vote | **1.000** | 60/60 |
|
|
72
|
+
|
|
73
|
+
### Pairwise
|
|
74
|
+
|
|
75
|
+
| Pair | κ | Agreement |
|
|
76
|
+
|---|---|---|
|
|
77
|
+
| Claude vs Kimi | 1.000 | 100% |
|
|
78
|
+
| Claude vs DeepSeek | 0.935 | 96.8% |
|
|
79
|
+
| Kimi vs DeepSeek | 0.935 | 96.8% |
|
|
80
|
+
|
|
81
|
+
### Same-family leniency: not detected
|
|
82
|
+
|
|
83
|
+
The DeepSeek judge scored DeepSeek-produced responses no differently from the
|
|
84
|
+
others. κ was 0.937 versus 0.936 for the other two. This was the specific thing
|
|
85
|
+
experiment design was checking for, and the null result is worth recording.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## The caveat that matters
|
|
90
|
+
|
|
91
|
+
**κ = 1.000 does not mean "the judges are excellent". It means "this labelling
|
|
92
|
+
task is easy".**
|
|
93
|
+
|
|
94
|
+
Almost every response states its conclusion explicitly — "建议采用 TypeScript"
|
|
95
|
+
or "不使用 TypeScript" — within the first few lines. The sample contains
|
|
96
|
+
essentially **no genuinely ambiguous cases**. So this experiment establishes:
|
|
97
|
+
|
|
98
|
+
- The judges do not systematically disagree on clear responses. ✅
|
|
99
|
+
- The downstream numbers are not an artefact of sloppy scoring on clear
|
|
100
|
+
responses. ✅
|
|
101
|
+
|
|
102
|
+
It does **not** establish:
|
|
103
|
+
|
|
104
|
+
- That the judges handle hedged or ambiguous responses correctly. ❌
|
|
105
|
+
- That the labels are independent of the experimenter. ❌
|
|
106
|
+
|
|
107
|
+
A perfect κ is a warning sign as much as a result. It says the measurement is
|
|
108
|
+
not the hard part of this research; the experimental design is.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Independence caveat — and the audit result
|
|
113
|
+
|
|
114
|
+
The "human" labels were produced by the same assistant that designed the
|
|
115
|
+
experiments. They were blind to condition, but they are not an independent
|
|
116
|
+
reference. A 20-item audit by the project owner was required.
|
|
117
|
+
|
|
118
|
+
**Audit result: 19/20 agreement, within the pre-registered threshold of 2.**
|
|
119
|
+
|
|
120
|
+
| | |
|
|
121
|
+
|---|---|
|
|
122
|
+
| Items reviewed | 20 |
|
|
123
|
+
| Agreement | 19 |
|
|
124
|
+
| Disagreements | 1 |
|
|
125
|
+
| Threshold | ≤ 2 |
|
|
126
|
+
| Result | **PASS** |
|
|
127
|
+
|
|
128
|
+
The single remaining disagreement (`E0-061`) is genuine: the response is a
|
|
129
|
+
truncated reasoning dump with no final conclusion, so `recommends` and
|
|
130
|
+
`unclear` are both defensible.
|
|
131
|
+
|
|
132
|
+
Two initial disagreements (`E0-013`, `E0-026`) were corrected by the reviewer
|
|
133
|
+
after reading the full response text; both responses state the conclusion
|
|
134
|
+
explicitly. This is a useful signal about the review process itself: **an
|
|
135
|
+
excerpt is not always enough for a human reviewer**, even when it is enough for
|
|
136
|
+
a model. The audit sheet truncates long responses, and that truncation caused
|
|
137
|
+
two of the three initial disagreements.
|
|
138
|
+
|
|
139
|
+
Files: `experiments/e0/audit-sample.md`, `answer-sheet.md`, `audit-key.json`,
|
|
140
|
+
`audit-result.json`.
|
|
141
|
+
|
|
142
|
+
**Treat the labels as an independent-enough reference, not as ground truth.**
|
|
143
|
+
The reviewer and the assistant agreed on 19 of 20 items, which is evidence that
|
|
144
|
+
the labels are not idiosyncratic. It is not evidence that either is correct on
|
|
145
|
+
the genuinely ambiguous cases.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## What this changes downstream
|
|
150
|
+
|
|
151
|
+
1. **Majority vote is safe to use** for E1–E5, with the `unclear` rate reported.
|
|
152
|
+
2. **Report the `unclear` count in every experiment.** Dropping unclear items
|
|
153
|
+
inflates agreement; they stay in the denominator.
|
|
154
|
+
3. **Add a human spot-check to E3**, because E3's sample will contain more
|
|
155
|
+
ambiguous cases than this one did.
|
|
156
|
+
4. **Do not claim the judges are validated on hard cases.** They are validated
|
|
157
|
+
on easy ones.
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Reproduce
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
node experiments/e0/judge.mjs --dry # print the plan
|
|
165
|
+
node experiments/e0/judge.mjs # 186 calls
|
|
166
|
+
node experiments/e0/make-audit.mjs # render the 20-item human audit
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Files: `blind-sample.json`, `human-labels.json`, `judge-*.json`,
|
|
170
|
+
`calibration.json`, `audit-sample.md`, `audit-key.json`.
|