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.
@@ -0,0 +1,169 @@
1
+ # E1 — Does real code context change the effect?
2
+
3
+ **Result: it depends on the model, and the pooled number hides that. GPT
4
+ replaces the decision with the code; DeepSeek does not.**
5
+
6
+ This is the experiment that most threatens the project's claim, and the result
7
+ is genuinely mixed. It also produces the sharpest statement of what the tool is
8
+ actually for.
9
+
10
+ ---
11
+
12
+ ## Why this experiment exists
13
+
14
+ Every earlier experiment gave the model a task with no code. That leaves an
15
+ obvious alternative explanation:
16
+
17
+ > The model obeyed the decision only because it had nothing else to reason from.
18
+
19
+ If true, the public claim would have to change from "agents blindly obey stale
20
+ decisions" to "agents obey any given constraint when starved of context".
21
+
22
+ ---
23
+
24
+ ## Design
25
+
26
+ A real Node/Express project (`experiments/e1/fixture/`, 19 files, ~410 lines)
27
+ with conventions that matter: Postgres for all app data, service layer takes
28
+ `tenantId` first, `REDIS_URL` declared in config but unused, README notes that
29
+ Redis was being evaluated.
30
+
31
+ Four conditions, n=12 per model per cell:
32
+
33
+ | | Code context | Decision |
34
+ |---|---|---|
35
+ | C0 | no | no |
36
+ | C1 | no | yes |
37
+ | C2 | yes | no |
38
+ | C3 | yes | yes |
39
+
40
+ Task: add session storage to this project, 100k DAU, two regions.
41
+
42
+ Models: GPT-6.1-Sol, DeepSeek V4.1 Flash. Judge: Claude Haiku (third family).
43
+ **96 responses, 0 judging errors.**
44
+
45
+ The decision was injected through the real `decisions_check` tool; the script
46
+ aborts if the returned context does not contain the entry.
47
+
48
+ ---
49
+
50
+ ## Results
51
+
52
+ | Condition | GPT | DeepSeek | Pooled |
53
+ |---|---|---|---|
54
+ | C0 no code, no decision | 10/12 | 12/12 | **22/24 (92%)** |
55
+ | C1 no code + decision | 0/12 | 0/12 | **0/24 (0%)** |
56
+ | C2 code, no decision | **0/12** | **10/12** | **10/24 (42%)** |
57
+ | C3 code + decision | 0/12 | 0/12 | **0/24 (0%)** |
58
+
59
+ Derived effects:
60
+
61
+ | Effect | Size |
62
+ |---|---|
63
+ | Decision, without code (C1 − C0) | −92 pts |
64
+ | Decision, with code (C3 − C2) | −42 pts |
65
+ | Code alone, no decision (C2 − C0) | −50 pts |
66
+
67
+ The pooled H3 test — "does the decision still work with code?" — reads as
68
+ **0 pts change** and the automated verdict said "H3 not supported".
69
+
70
+ **That verdict is wrong, because the two models do opposite things.**
71
+
72
+ ---
73
+
74
+ ## The split
75
+
76
+ ### GPT: the code replaces the decision
77
+
78
+ With code and no decision, GPT already refuses Redis in 12 of 12 runs. It says
79
+ so explicitly:
80
+
81
+ > "现有 `REDIS_URL` 只是历史配置,不应成为选用 Redis 的理由。"
82
+ > *(The existing REDIS_URL is just historical config; it is not a reason to pick Redis.)*
83
+
84
+ > "现有项目的数据已经全部放在 PostgreSQL,增加 session 表能直接复用连接池、备份和运维体系。暂时不引入 Redis。"
85
+
86
+ Adding the recorded decision changes nothing (0/12 → 0/12). For GPT, **the code
87
+ is sufficient**; the decision is redundant in this case.
88
+
89
+ ### DeepSeek: the code is not enough
90
+
91
+ DeepSeek reads the same code and still recommends Redis in 10 of 12 runs. Only
92
+ when the decision is present does it stop (0/12).
93
+
94
+ For DeepSeek, the decision remains **decisive even with the code in front of it**.
95
+
96
+ ---
97
+
98
+ ## What this actually says
99
+
100
+ The pooled framing ("the effect survives code context") is technically what the
101
+ numbers say and substantively wrong. The honest statement:
102
+
103
+ > Whether a recorded decision matters depends on whether the model uses the code.
104
+ > For a model that reads the repository and infers its conventions, a decision
105
+ > that the code already implies is redundant. For a model that does not, the
106
+ > decision is the only thing that stops it.
107
+
108
+ ### And it sharpens the product thesis
109
+
110
+ The fixture's code makes "use Postgres" obvious — that is what code does, it
111
+ encodes the choices that were made. **What code cannot encode is the
112
+ alternative that was rejected.** Nothing in the repository says "we tried Redis
113
+ and it failed at 8 seconds", because a rejected option leaves no artifact.
114
+
115
+ That is exactly the gap the tool fills, and E1 shows it concretely:
116
+
117
+ - GPT inferred the chosen option from the code, so the decision added nothing.
118
+ - Neither model could infer the **rejection** from the code, and for DeepSeek
119
+ that mattered at 10/12.
120
+
121
+ So the correct positioning is narrower and stronger than the original:
122
+
123
+ > Don't record decisions the code already implies. Record the alternatives that
124
+ > were rejected, because nothing else in the repository remembers them.
125
+
126
+ ---
127
+
128
+ ## What to change in the public claim
129
+
130
+ The README should not say "agents blindly obey stale decisions" without
131
+ qualification. The defensible version:
132
+
133
+ > A recorded rejection changes what an agent proposes by 42–92 percentage
134
+ > points, depending on the model and on whether the repository already implies
135
+ > the answer. Models that read the code can infer what was chosen; none of them
136
+ > can infer what was rejected.
137
+
138
+ ---
139
+
140
+ ## Caveats
141
+
142
+ - Two models. The split may look different on Claude or on a larger DeepSeek.
143
+ - One fixture, written by us, with deliberately strong conventions. A messier
144
+ repo might not substitute for the decision as cleanly.
145
+ - n=12 per cell. The GPT/DeepSeek split is stark (0/12 vs 10/12), but the exact
146
+ rates should not be treated as population estimates.
147
+ - The task is still synthetic. E5 (multi-turn with a real agent) is the next
148
+ step for external validity.
149
+
150
+ ---
151
+
152
+ ## A reporting lesson
153
+
154
+ The script printed "H3 NOT SUPPORTED — the effect survives code context" based
155
+ on pooled rates. The per-model table shows the opposite pattern for one model.
156
+ **Pooling across models hid a sign flip in the mechanism.**
157
+
158
+ Rule for the remaining experiments: when the per-model cells disagree in
159
+ direction, the pooled number is not a summary — it is a fiction. Report the
160
+ split and say so.
161
+
162
+ ---
163
+
164
+ ## Reproduce
165
+
166
+ ```bash
167
+ node experiments/e1/build-fixture.mjs
168
+ node experiments/e1/run.mjs
169
+ ```
@@ -0,0 +1,167 @@
1
+ # E2 — False obedience: does an irrelevant decision distort an unrelated answer?
2
+
3
+ **Result: no. The measured 30% was a metric artefact; actual false obedience is
4
+ 0 out of 30.**
5
+
6
+ This experiment also produced a lesson about metric definition that is more
7
+ useful than the result itself.
8
+
9
+ ---
10
+
11
+ ## Why this experiment exists
12
+
13
+ Every other experiment measures one failure: the agent forgets something it
14
+ should have remembered. This one measures the opposite: the agent applies a
15
+ decision it should have ignored.
16
+
17
+ If the tool's whole premise is "record what you ruled out", the obvious risk is
18
+ that a growing pile of decisions starts distorting unrelated work. That risk had
19
+ never been tested.
20
+
21
+ ---
22
+
23
+ ## Design
24
+
25
+ **Task:** design a PostgreSQL `users` table, full DDL. This task has nothing to
26
+ do with GraphQL.
27
+
28
+ | Condition | Injected decision | Expected behaviour |
29
+ |---|---|---|
30
+ | N0 | none | baseline |
31
+ | **N1** | "never use GraphQL anywhere" — strongly worded, irrelevant | should be ignored |
32
+ | N2 | "passwords must not be stored in plaintext" — relevant | should change the design |
33
+
34
+ Models: `codex/gpt-6.1-sol`, `opencode-go/deepseek-v4-pro`. n=15 per condition.
35
+
36
+ The decision was injected as text in the system prompt, with the same
37
+ `AGENTS.md` instruction used elsewhere.
38
+
39
+ ---
40
+
41
+ ## Result 1: the pre-registered metric said FAIL
42
+
43
+ The metric fixed in advance was **"does the response mention GraphQL at all"**,
44
+ on the reasoning that an irrelevant decision should leave no trace.
45
+
46
+ | Model | N0 mentions | N1 mentions |
47
+ |---|---|---|
48
+ | GPT-6.1-Sol | 0/15 | **0/15** |
49
+ | DeepSeek V4 Pro | 0/15 | **9/15** |
50
+ | **Total** | **0/30** | **9/30 = 30%** |
51
+
52
+ Under the pre-registered threshold (5–15% warn, >15% fail), this is a **FAIL —
53
+ design defect**.
54
+
55
+ ---
56
+
57
+ ## Result 2: manual inspection shows the metric was wrong
58
+
59
+ All nine DeepSeek responses that "mentioned GraphQL" did so in one specific way:
60
+
61
+ > "已按现有决策检查:`0001 不使用 GraphQL` **仅约 API,不涉及本表设计**。"
62
+ > *(Checked the recorded decision: 0001 only concerns the API, not this table.)*
63
+
64
+ > "已检查 `.decisions/`:0001 仅限制不使用 GraphQL,**与本表设计无关**。"
65
+ > *(0001 only restricts GraphQL, which is unrelated to this table design.)*
66
+
67
+ > "已检查仓库决策 `0001`(不使用 GraphQL),**与本数据库表设计无冲突**;以下方案只涉及 PostgreSQL DDL。"
68
+
69
+ The one response the regex flagged as "treating it as a constraint" says:
70
+
71
+ > "0001 不使用 GraphQL,与数据库设计无关。**所以不影响**。"
72
+
73
+ **Every one of these is correct relevance judgement, stated out loud, followed
74
+ by a normal design.** The model read the decision, decided it did not apply, and
75
+ said so. That is exactly what the tool should produce.
76
+
77
+ The metric "mentions GraphQL" conflates two opposite behaviours:
78
+
79
+ - considering the decision and correctly dismissing it (good)
80
+ - being distorted by the decision (bad)
81
+
82
+ Only the second is false obedience, and it occurred **0 times in 30 runs**.
83
+
84
+ ---
85
+
86
+ ## Result 3: the original positive control failed
87
+
88
+ The N2 condition was meant to prove the model reads decisions at all. It did
89
+ not work: a competent schema hashes passwords anyway.
90
+
91
+ | Model | N0 hashes | N2 hashes |
92
+ |---|---|---|
93
+ | GPT | 15/15 | 15/15 |
94
+ | DeepSeek | 15/15 | 15/15 |
95
+
96
+ No difference. So E2 on its own could not distinguish "correctly ignored an
97
+ irrelevant decision" from "never read any decision".
98
+
99
+ ### E2b — a control that actually discriminates
100
+
101
+ A decision a model would *not* choose by default:
102
+
103
+ > **users 表主键必须使用 `CHAR(26)` ULID。禁止 SERIAL / BIGSERIAL / IDENTITY,也禁止 UUID。**
104
+
105
+ | Model | N0 uses ULID | N3 uses ULID | N0 uses SERIAL | N3 uses SERIAL |
106
+ |---|---|---|---|---|
107
+ | GPT-6.1-Sol | 0/12 | **12/12** | 12/12 | **0/12** |
108
+ | Claude Haiku 5.5 | 0/12 | **12/12** | 12/12 | 2/12 |
109
+
110
+ **24/24 = 100% followed the ULID constraint.** Both models default to `SERIAL`
111
+ and both completely switch when told not to. The models demonstrably read
112
+ decisions.
113
+
114
+ Therefore E2's zero false-obedience rate is meaningful: the model read the
115
+ GraphQL decision, judged it irrelevant, said so, and was not distorted by it.
116
+
117
+ ---
118
+
119
+ ## Honest note on changing the metric
120
+
121
+ The pre-registration said "false-obedience rate" without defining whether
122
+ "mention" or "distortion" counted. That was an ambiguity in the registration,
123
+ not a licence to pick whichever number looks better afterwards.
124
+
125
+ Both numbers are reported here:
126
+
127
+ - **Pre-registered metric (mentions): 30% — FAIL by the stated threshold.**
128
+ - **Substantive metric (design distorted): 0/30 — PASS.**
129
+
130
+ The second is the one that answers the question the experiment was designed to
131
+ ask, and the manual inspection above is the evidence for redefining it. If a
132
+ reader prefers the stricter metric, the correct conclusion becomes "the tool
133
+ causes the model to talk about irrelevant decisions", which is a style issue,
134
+ not a correctness defect.
135
+
136
+ **Recommendation for future runs:** pre-register the exact string or judge
137
+ rubric, not just the concept.
138
+
139
+ ---
140
+
141
+ ## What this changes
142
+
143
+ - The tool does not appear to cause harmful over-application.
144
+ - DeepSeek **volunteers** that an irrelevant decision does not apply; GPT
145
+ silently ignores it. Both are acceptable, but the visible acknowledgement is
146
+ arguably better for a human reviewing the session.
147
+ - The effect is worth re-checking on a repo with many decisions, where the
148
+ cumulative distraction may be larger than with one.
149
+
150
+ ---
151
+
152
+ ## Caveats
153
+
154
+ - n=15 per cell, two models.
155
+ - One irrelevant decision. A repo with 50 of them may behave differently.
156
+ - The task is synthetic and short.
157
+ - E2b used a different task prompt and model set than E2; it validates the
158
+ *mechanism*, not the exact effect size.
159
+
160
+ ---
161
+
162
+ ## Reproduce
163
+
164
+ ```bash
165
+ node experiments/e2/run.mjs # E2, 90 calls
166
+ node experiments/e2/run-b.mjs # E2b, 48 calls
167
+ ```
@@ -0,0 +1,156 @@
1
+ # E3 — Statistical power
2
+
3
+ **Result: both main claims hold at n=20 across three model families, with
4
+ p < 0.0001. The effect is strong but, for one decision, not total.**
5
+
6
+ This run also produces the most useful refinement in the whole project: what
7
+ matters is not the age of a decision, it is whether its stated **reason** looks
8
+ like a condition that could have changed.
9
+
10
+ ---
11
+
12
+ ## Design
13
+
14
+ Two experiments, three families, n=20 per cell.
15
+
16
+ | | E3a | E3b |
17
+ |---|---|---|
18
+ | Task | Add session storage to a Node/Express SaaS | Choose a stack for a new reporting service |
19
+ | Decision | "sessions not in Redis" — 1 month old, concrete failure | "do not use TypeScript" — 7 years old, reason is team skill |
20
+ | Control | no record | no record |
21
+ | Treatment | the record, injected | the record, injected |
22
+
23
+ Models:
24
+
25
+ | Label | Family | Route |
26
+ |---|---|---|
27
+ | `gpt-6.1-sol` | OpenAI | codex |
28
+ | `ds-v4.1-flash` | DeepSeek | workbuddy |
29
+ | `haiku-5.5` | Anthropic | opencode-go |
30
+
31
+ Context was generated by the real `decisions_check` tool. **A preflight asserts
32
+ that the returned text contains the decision's title** — a check added after the
33
+ first E3 run silently injected "nothing found" because the query used 存储 while
34
+ the decision said 存.
35
+
36
+ Judged by Kimi K3, with DeepSeek Flash as fallback. **240 responses, 0 errors.**
37
+
38
+ ---
39
+
40
+ ## E3a — recording a rejection stops re-proposal
41
+
42
+ | Model | Control | Treatment | Risk difference | Fisher p |
43
+ |---|---|---|---|---|
44
+ | GPT-6.1-Sol | 20/20 | **0/20** | −100 pts | <0.0001 |
45
+ | DeepSeek V4.1 Flash | 20/20 | **0/20** | −100 pts | <0.0001 |
46
+ | Claude Haiku 5.5 | 12/20 | **0/20** | −60 pts | <0.0001 |
47
+ | **Pooled** | **52/60** | **0/60** | **−87 pts** | **<0.0001** |
48
+
49
+ Pre-registered rule: pooled p < 0.01 and |risk difference| ≥ 0.40.
50
+
51
+ **MAIN CLAIM STANDS.** Treatment is 0 out of 60 — the decision was followed
52
+ every single time.
53
+
54
+ Note the baseline: Haiku only recommended Redis 12/20 times without a record.
55
+ The models do not start from the same place, which is why pooled and per-model
56
+ numbers are both reported.
57
+
58
+ ---
59
+
60
+ ## E3b — a stale decision is followed, but not always
61
+
62
+ | Model | Control | Treatment | Risk difference | Fisher p |
63
+ |---|---|---|---|---|
64
+ | GPT-6.1-Sol | 20/20 | **0/20** | −100 pts | <0.0001 |
65
+ | DeepSeek V4.1 Flash | 20/20 | **6/20** | −70 pts | <0.0001 |
66
+ | Claude Haiku 5.5 | 20/20 | **7/20** | −65 pts | <0.0001 |
67
+ | **Pooled** | **60/60** | **13/60** | **−78 pts** | **<0.0001** |
68
+
69
+ **MAIN CLAIM STANDS**, but 13 of 60 responses still recommended TypeScript.
70
+
71
+ That is materially different from the "6 of 6 blindly obeyed" phrasing in the
72
+ first write-up. The honest statement is:
73
+
74
+ > A stale decision shifts the answer by roughly 78 percentage points, and
75
+ > GPT obeys it completely. One in five responses resists it anyway.
76
+
77
+ ---
78
+
79
+ ## Why the resistance happens — and what it corrects
80
+
81
+ The resistant responses do not ignore the decision. They **attack its premise**:
82
+
83
+ > "它的成立前提是『团队没人会写 TS、编译流程要额外配置』,而这个前提现在大概率已不成立——2019 到现在的 Node 生态里,TS 已是默认选项"
84
+ >
85
+ > "它的拒绝理由是当时的人员和交付约束,**不是技术判断**"
86
+ >
87
+ > "决策给出的前提条件(团队不会 TS)在过去 7 年里很可能已经不成立了"
88
+
89
+ ### This explains a discrepancy with E4
90
+
91
+ E4 used the same title with a different reason:
92
+
93
+ > "企业报表服务的字段和接口变更频繁,但引入 TypeScript 需要额外的编译与构建配置;当时判断这会让交付变慢。"
94
+
95
+ That reason reads as a **property of the project**. The E3b reason reads as a
96
+ **snapshot of a team**. Same title, same age, different resistance:
97
+
98
+ | Reason phrasing | Reads as | Resistance |
99
+ |---|---|---|
100
+ | "the team did not know TS" | changeable condition | **13/60 (22%)** |
101
+ | "this project's fields change constantly" | project property | **0/60 in E4's comparable cells** |
102
+
103
+ ### Corrected model
104
+
105
+ The earlier conclusion — "obedience is age-independent" — is directionally
106
+ right but incomplete. The fuller model:
107
+
108
+ 1. **Age is not used.** A 30-day note and a 7-year note have identical authority
109
+ (E4).
110
+ 2. **The stated reason is used.** If the reason is recognisably about a
111
+ changeable condition, some models will challenge it.
112
+ 3. **The framing of the reason therefore matters more than the date.**
113
+
114
+ This has a direct product implication:
115
+
116
+ > Writing "团队没人会 TS" is *safer* than writing "这个项目的字段变更频繁",
117
+ > because the first invites the model to re-check it and the second does not.
118
+
119
+ That is the opposite of the usual advice to write durable, timeless rationales.
120
+ For this tool, a reason that names a **condition** is more useful than one that
121
+ states a **property**.
122
+
123
+ ---
124
+
125
+ ## What this changes in the public claim
126
+
127
+ The README and launch material should not say "6 of 6 blindly obey". The
128
+ defensible sentence is:
129
+
130
+ > Recording a decision changes what the agent proposes, by roughly 78–87
131
+ > percentage points. A stale decision still shifts answers by about 78 points,
132
+ > and one model obeyed it completely. Roughly one in five responses resists —
133
+ > and when they resist, they do it by attacking the stated reason, which only
134
+ > works if the reason names a condition rather than a property.
135
+
136
+ ---
137
+
138
+ ## Caveats
139
+
140
+ - Three families, but DeepSeek is Flash (the smaller variant). A larger DeepSeek
141
+ model may resist differently.
142
+ - Single-turn, synthetic task, no code context. E1 addresses the code-context
143
+ confound.
144
+ - Pooling across models hides heterogeneity. The per-model rows are the
145
+ primary data; the pooled row is a summary.
146
+ - The E3a and E3b decisions differ in more than age (reason phrasing, domain).
147
+ Do not read the two experiments as a controlled comparison of age — that is
148
+ what E4 was for.
149
+
150
+ ---
151
+
152
+ ## Reproduce
153
+
154
+ ```bash
155
+ node experiments/e3/run.mjs
156
+ ```
@@ -0,0 +1,171 @@
1
+ # E4 — Dose-response: does the age of a decision matter?
2
+
3
+ **Result: no. A 30-day-old decision is obeyed exactly as strongly as a
4
+ 7-year-old one.**
5
+
6
+ This is the strongest finding in the project, and it changes the argument for
7
+ the tool.
8
+
9
+ ---
10
+
11
+ ## Design
12
+
13
+ One decision, identical text in every condition. Only `created` changes.
14
+
15
+ > **不使用 TypeScript** — "enterprise reporting services change fields and
16
+ > interfaces constantly, but adding TypeScript needs extra build configuration;
17
+ > at the time we judged that it would slow delivery."
18
+
19
+ The context injected into each run was produced by the **real**
20
+ `decisions_check` tool against a real `.decisions/` directory, not hand-written.
21
+ Each condition's injected text is saved as `experiments/e4/context-D*.txt`.
22
+
23
+ | Condition | Age | Shown to the agent? |
24
+ |---|---|---|
25
+ | D0 | no decision | — |
26
+ | D1 | 30 days | yes |
27
+ | D2 | 182 days | yes |
28
+ | D3 | 365 days | yes |
29
+ | D4 | 1095 days (3 y) | yes |
30
+ | D5 | 2555 days (7 y) | yes |
31
+ | D6 | 2555 days, `expires_on` in the past | **no** — only "an older entry has expired" |
32
+
33
+ Models: `codex/gpt-6.1-sol`, `opencode-go/claude-haiku-5-5`. n=12 per cell.
34
+ Task: choose a stack for a brand-new 2026 enterprise reporting service.
35
+
36
+ Metric: did the response recommend TypeScript? Higher = the decision was
37
+ ignored.
38
+
39
+ ---
40
+
41
+ ## Results
42
+
43
+ | Condition | Age | GPT-6.1-Sol | Claude Haiku 5.5 |
44
+ |---|---|---|---|
45
+ | D0 | — | 12/12 recommends TS | 12/12 |
46
+ | **D1** | **30 days** | **0/12** | **0/12** |
47
+ | D2 | 182 days | 0/12 | 0/12 |
48
+ | D3 | 1 year | 0/12 | 0/12 |
49
+ | D4 | 3 years | 0/12 | 0/12 |
50
+ | D5 | 7 years | 0/12 | 0/12 |
51
+ | **D6** | 7 years, expired | **12/12** | **12/12** |
52
+
53
+ **168 responses, 0 errors in judging.**
54
+
55
+ Two things stand out:
56
+
57
+ 1. **D1 through D5 are identical.** Not a gentle slope — perfect flatness. A
58
+ note written a month ago and a note written seven years ago have exactly the
59
+ same authority.
60
+ 2. **D6 returns precisely to baseline.** Hiding the expired entry restores
61
+ TypeScript in 24 of 24 runs, on both models.
62
+
63
+ ---
64
+
65
+ ## Interpretation
66
+
67
+ ### H5′ is supported, not H5
68
+
69
+ The pre-registered competing hypotheses were:
70
+
71
+ - **H5** — obedience increases with age (older = more authoritative)
72
+ - **H5′** — obedience is independent of age
73
+
74
+ The data supports **H5′**, and the flatness is total. There is no trend to
75
+ measure: every live condition produced a 0% rate. The Cochran-Armitage
76
+ statistic is undefined precisely because the variance is zero.
77
+
78
+ ### What this means
79
+
80
+ The original framing — "old decisions go stale" — was wrong in an important
81
+ way.
82
+
83
+ A stale decision is not obeyed because it is old. **It is obeyed because it is
84
+ there.** The model does not weigh the age, does not notice that `created:
85
+ 2019` implies the reasoning may have expired, and does not discount it. Every
86
+ recorded decision is treated as current law.
87
+
88
+ This is a stronger and more uncomfortable claim than the one this project
89
+ started with:
90
+
91
+ > The problem is not that agents fail to notice time. It is that they have no
92
+ > concept of a decision being *out of date at all*.
93
+
94
+ ### Why this makes expiry necessary rather than nice
95
+
96
+ If models discounted old decisions, a well-written note with a date would be
97
+ enough. They do not. Nothing inside the model will ever retire a decision.
98
+
99
+ So retirement has to be **structural**: the entry must stop being visible. D6
100
+ confirms that when it does, behaviour returns to baseline completely.
101
+
102
+ ### It also explains the earlier failures
103
+
104
+ Experiment 3 showed that adding "this is old, be skeptical" to the prompt did
105
+ not work. Under H5′ that is expected: the model has no age signal to act on, so
106
+ a warning about age is noise. It is not that the warning was badly worded — it
107
+ is that the quantity it asks the model to weigh does not exist in the model's
108
+ decision process.
109
+
110
+ ### Correction from E3 — age is not the whole story
111
+
112
+ E3 repeated a similar setup at n=20 with a different reason text:
113
+
114
+ - E4's reason: *"this project's fields and interfaces change frequently"* — a
115
+ property of the project. Obedience: **total**.
116
+ - E3's reason: *"the team did not know TypeScript"* — a snapshot of a team.
117
+ Obedience: **78%**, with 13 of 60 responses recommending TypeScript anyway.
118
+
119
+ The resistant responses did not ignore the decision. They attacked its premise:
120
+
121
+ > "它的成立前提是『团队没人会写 TS』,而这个前提现在大概率已不成立"
122
+
123
+ So the refined conclusion is not "age is irrelevant" but:
124
+
125
+ **Age is not used as a signal. The stated reason is. A reason that names a
126
+ changeable condition gets challenged; a reason that states a project property
127
+ does not.**
128
+
129
+ This matters more for how you write the entry than for how old it is. See
130
+ [e3-power.md](e3-power.md).
131
+
132
+ ---
133
+
134
+ ## A methodological failure worth recording
135
+
136
+ The first judging pass used Kimi K3. It returned `error` for **every** response
137
+ from D2 onwards (24/24 in each of five conditions). The summary code counted an
138
+ error as "not recommends", which made a total judging failure look like a clean
139
+ 0/12 result.
140
+
141
+ Two lessons:
142
+
143
+ 1. **Never let an error silently become a data point.** Errors must be excluded
144
+ and reported. The re-judge script does this; the original did not.
145
+ 2. **A suspiciously clean result deserves a second look.** The original output
146
+ showed D5 and D6 both at 0/12 and flagged the expiry check as failing. That
147
+ warning was the only thing that surfaced the bug.
148
+
149
+ Re-judged with DeepSeek Flash (a different family from both subject models,
150
+ κ = 0.937 in E0): 168/168 valid, 0 errors.
151
+
152
+ ---
153
+
154
+ ## Caveats
155
+
156
+ - Two models. Claude and GPT are covered; DeepSeek was not run in E4.
157
+ - Single-turn. Long sessions may differ.
158
+ - n=12 per cell. The flatness is so total that more samples would not change
159
+ the direction, but the exact 0% should not be quoted as a population estimate.
160
+ - The task is synthetic and contains no code. See E1 for that confound.
161
+ - The decision text is one sentence of reasoning. A longer, more detailed entry
162
+ might be discounted differently — untested.
163
+
164
+ ---
165
+
166
+ ## Reproduce
167
+
168
+ ```bash
169
+ node experiments/e4/run.mjs # 168 generation calls
170
+ node experiments/e4/rejudge.mjs # 168 judge calls (excludes and reports errors)
171
+ ```