dsh-experience 0.0.0-stage → 1.0.0

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/CHANGELOG.md ADDED
@@ -0,0 +1,507 @@
1
+ # Changelog
2
+
3
+
4
+ ## [1.0.0] — 2026-11-09 — 稳定版(版本线重置)
5
+
6
+ 维护者要求把当前状态作为稳定版发布,版本号重置为 1.0.0。相对 1.2.2,内容差异只有本轮稳定性修复。
7
+
8
+ **修复**
9
+ - 载入即清洗脏数据:`experiences` / `archive` 里的 `null`、数字、字符串会被丢弃;`normalizeStore` 对 `archive`(200)与 `evictions`(500)强制封顶。手改坏或历史遗留的超大文件不再让插件在第一次召回前崩掉。
10
+ - `/memory review` 最多显示 10 条候选,其余提示「另有 N 条未显示」——40 条候选刷满屏幕等于没有 review。
11
+ - `/memory review discard` 补进命令帮助文本。
12
+
13
+ **稳定性验证**
14
+ - 新增 `tools/_stable-probe.mjs`(23 项:损坏 store、缺失文件、脏数组、配置强转、空库渲染、60 条库的检索耗时、超龄 verified 不归档、G8、写盘缓存、注入预算)。
15
+ - `selftest 108` + `e2e 25` + `stable-probe 23` 全部通过。
16
+
17
+ **版本说明**:1.0.0 低于此前的 1.2.x 发布线,npm 上的 `latest` 会指向 1.0.0;按 `^1.0.0` 安装仍会解析到更高的 1.2.2。这是维护者的显式决定,不是事故。
18
+
19
+ ## [1.2.2] — 2026-11-09 — 体验修复:采集可靠性、候选可读性、旧经验降权
20
+
21
+ **采集**
22
+ - `tools/result` 与 turn 生命周期统一用 session id 对齐(原先工具侧取 `agent.id`、轮末取 `session.id`,不一致时整轮调用被记到另一个 key,表现为 `G1: 成功工具调用 < 2` 而实际跑了很多工具)。
23
+ - 单 key 兜底搬运 + `trace.resultsSeen` / `trace.keyFallback`;G1 拒绝但确实收到过结果时写入 `capture.diagnostic`,不再静默。
24
+
25
+ **召回**
26
+ - 无 platform 标签的历史记录排序 ×0.85,注入块里标注「平台:未标注」。
27
+ - `score()` 收到非字符串直接抛 `TypeError`(此前把记录对象传进去会静默得 0,看起来像"不相关")。
28
+
29
+ **候选可读性**
30
+ - 标题带项目名:`新增 truncate(acme-app)`、`跑 run(cross-a)`。
31
+ - 触发条件写真实验证命令,不再三条候选共用一句模板。
32
+ - `stepLabel` 去掉 `cd …;` 前缀;`cd` / `Start-Sleep` 计入噪声。
33
+ - 新增 `/memory review discard`:一次性拒绝当前所有候选(可审计,非删除)。
34
+
35
+ **衰减**
36
+ - 容量未达阈值时不再输出"观察项"清单(小库时那 5 条就是全部,纯噪声)。
37
+
38
+ **测试**:`selftest 106` + `e2e 25` = 131 全部通过。
39
+
40
+ **已知限制**:采集可靠性改动只经过单测与结构验证;真实轮次的效果需要下一个长轮次的 `capture.diagnostic` 来确认。
41
+
42
+ ## [1.2.1] — 2026-11-09 — 审计修复:召回语料、id 回收、模拟淘汰、恢复、G8 守卫
43
+
44
+ ### 修复
45
+ - **召回漏传 IDF 语料**:`index.js` 召回处只传两个参数,`scoreExperience` 的第三参为空,所有词权重 1,常见词与稀有词同权——无关记录可能越过阈值被注入。现在与合并路径一致。
46
+ - **`nextId` 不与现有 id 对账**:载入时与 `experiences`/`archive` 的最大 `exp-N` 对齐,不再复用已存在的 id(否则 `forget`/`archive` 会打到旧记录)。
47
+ - **`decay --age N --apply` 用模拟未来时间真归档**:现在直接拒绝,`--age` 只用于展示。
48
+ - **`restore` 是空操作**:恢复时把 `lastUsedAt` 置为当前时刻,记录不会立刻重新判为 dormant。
49
+ - **淘汰会毁掉 G8 守卫**:`superseded` 不再进入淘汰候选(归档后同一指纹会被重新收录)。
50
+ - **“没什么可抓”被记成失败**:只有真的出现失败调用、且指纹与记录一致时,G3 拒绝才写 failure,普通对话不再毒化证据链。
51
+ - **临时文件名跨进程冲突**:`store.json.<pid>.tmp`,两个 DSH 进程同时写不再互相覆盖。
52
+ - **`pitfalls` 别名**:删除 `if (existing.pitfalls)`(`[]` 为真)导致 pitfalls 指向 reverse_examples 的写法。
53
+
54
+ ### 测试
55
+ - 新增 T87–T91、T63、T64;`node tools/selftest.mjs` 101 pass / 0 fail,`node tools/e2e.mjs` 25 pass / 0 fail。
56
+
57
+ ### 仍未做
58
+ - 工具在写盘失败时仍回“成功”(`persist` 吞异常);语料每轮仍重建 3 次;历史记录未回填 `pitfalls` 别名。
59
+
60
+ ## [1.2.0] — 2026-11-09 — 计划任务召回与已知问题清单
61
+
62
+ ### 修复
63
+ - **计划任务的轮次召回为空**:这类会话的第一条消息是 `[SCHEDULE REMINDER] … reminder_prompt_json: "…"`,
64
+ 整段被当成查询词,搜到的是 "schedule"。新增 `stripReminder()`,从包装里取出真实 prompt 再分词;
65
+ 原文保留在 `trace.queryRaw`。测试 T84。
66
+
67
+ ### 新增
68
+ - `docs/KNOWN-ISSUES.md`:八条已知问题,按"会不会让经验变错"排序,每条写症状/影响/现状/绕法
69
+ (含 shell 落盘不算写、`set_bundle` 不重载、召回只在第 1 步、纯词面检索、单机单用户等)。
70
+
71
+ ### 测试
72
+ - `selftest 94 pass / 0 fail`、`e2e 23 pass / 0 fail`。
73
+
74
+ ## [1.1.1] — 2026-11-09 — 构建标识修正
75
+
76
+ - `BUILD` 从 `r20-scope-tools` 改为 `r21-platform`。1.1.0 的代码已含平台隔离,但包内构建标识还是上一个版本号,
77
+ 排查问题时会让 `~/.dsh/experience/applied.json` 显示错误的构建。无行为变更。
78
+
79
+ ## [1.1.0] — 2026-11-09 — 平台隔离与适用边界
80
+
81
+ ### 新增
82
+ - **平台标签**:`buildCandidate()` 写入 `platform: process.platform`;`platformConflicts()` 在召回与合并两条路径上拦截跨 OS 记录。
83
+ 起因:`& "<node.exe>"` 这类 win32 经验在 darwin 上不成立,却会被当通用知识注入。
84
+ - **README「适用边界」**:写明单用户、单机、按 OS 隔离、需人批准、不替代 AGENTS.md 五条边界。
85
+ - 新增文档徽章改为 GitHub Actions 工作流徽章(不再手写测试数字)。
86
+
87
+ ### 测试
88
+ - T82:捕获的记录带 `platform`;`platformOf` 对无标签记录返回空串。
89
+ - T83:异平台记录 `platformConflicts=true`,同平台与无标签均为 `false`。
90
+ - `selftest 93 pass / 0 fail`、`e2e 23 pass / 0 fail`。
91
+
92
+ ### 兼容
93
+ - 旧记录没有 `platform` 字段,按「与本机兼容」处理,不会被误判为异平台。
94
+
95
+ ## [1.0.14] — 2026-11-09 — A/B 效率实测记录
96
+
97
+ ### 记录
98
+ - `docs/TESTING.md` 新增第 7 节:冷/热两轮对照(`recallThreshold` 1.0 vs 0.15)的完整表格、
99
+ 两轮原始调用清单、`inj-52` 注入记录,以及六条偏差声明(n=1、顺序效应、编辑粒度、夹具复用、
100
+ 同族不同题、环境一致)。
101
+ - 实测:调用 18 → 9(−50%)、耗时 80.4s → 32.3s(−60%)、注入 524 token、重复踩坑 0 次。
102
+
103
+ ### 明确不宣称
104
+ - 不把单轮对照当作"平均提升"。要支持平均值需要每格 ≥5 轮并随机化顺序。
105
+
106
+ ### 维护
107
+ - 清理验收期夹具目录(cache-lab、cross-A/B、intent-test、exp-bench2/3/4 等),保留 `exp-bench`
108
+ 作为 v0.1 闭环的历史证据。
109
+
110
+ ## [1.0.13] — 2026-11-09 — 跨项目合并回归:scope 被工具链 token 桥接
111
+
112
+ ### 修复
113
+ - 三轮实跑验证发现:同配方换项目仍被「结果指纹」合并。根因是 `mentionedDirs()` 把
114
+ `…\node\bin\node.exe` 的最深段 `node` 也当作目录,而 `scopeConflicts()` 用集合交集判断同项目,
115
+ 于是 `[cross-a, node]` 与 `[cross-b, node]` 因共享 `node` 被判为同一项目。
116
+ - `GENERIC_DIRS` 增补工具链目录(node/python/npm/pnpm/git/pwsh/deno/runtime/dependencies/dsh…)。
117
+ - 新增 `projectScope(record)`:比较前先滤掉通用目录;`scopeConflicts()` 改用它。
118
+ - run 型记录标题改为始终「跑 <script>」(此前仅 `runOnly` 时为该格式,混合轨迹会退回「跑通 node <name>」)。
119
+
120
+ ### 测试
121
+ - T80:`cross-a` 与 `cross-b` 共享 `node` 时仍判冲突;只知工具链的记录保持中立。
122
+ - T81:run 型标题恒为「跑 run」。
123
+ - e2e T62:同一配方在两个项目里必须各自建档,不得合并。
124
+ - `selftest 91 pass / 0 fail`、`e2e 23 pass / 0 fail`。
125
+
126
+ ### 说明
127
+ - v1.0.12 及更早版本中「跨目录已验证」的结论不成立,本次修正后需重跑一次轮 2 才算复验。
128
+ - 文档:`docs/TESTING.md` 第 6 节、`ACCEPTANCE.md` 第 10 节记录了三轮原始日志与本次回归。
129
+
130
+ ## [1.0.12] — 2026-11-09 — 纯 run 轨迹可以被捕获了
131
+
132
+ ### 修复
133
+ - 此前 `stepPriority` 只把「写」算 3 分、「测试执行」算 4 分,普通命令只有 2 分,
134
+ 所以「写一个文件 + 跑一次」永远只有 1 个有效步骤,G7 直接拦截——`scope` 与 `intent` 两道闸门根本轮不到执行。
135
+ - `shouldCapture(trace, counts, config, intent)`:`intent === "run"` 时,只要成功执行 ≥2 次(建目录/读文件不计)即可过 G3。
136
+ - `gateStepQuality(..., intent)`:run 型轨迹改用 `runSteps()` 计量,理由串写明「可复用执行(建目录/读文件不计)」。
137
+ - `buildCandidate()`:run 型记录标题改为「跑 `<script>`」,产物名取命令里真实的脚本名。
138
+ - 指纹新增 `commands`(`commandHeads()`),记录实际启动的程序首词:`& "…\node.exe" x.mjs` → `node`。
139
+
140
+ ### 测试
141
+ - T78:纯 run 轨迹(mkdir + 两次 node 执行)过 G3/G7,标题 `跑 run`,指纹 `commands: ["node"]`。
142
+ - T79:只有 mkdir/Get-Content 的轨迹仍被 G3 拒;write 型轨迹仍要求两个有效步骤(回归)。
143
+ - e2e T61:整轮只有两次 `pwsh` 执行时,capture 通过并落库。
144
+ - `selftest 89 pass / 0 fail`、`e2e 22 pass / 0 fail`。
145
+
146
+ ### 说明
147
+ - 这是「保守版」:只放宽 run 意图,write 意图的阈值一字未动;mkd/读文件仍被 `isNoiseStep` 挡在计量之外。
148
+ - 夹具 `cross-A` / `cross-B` 已就绪(`src/math.js` + `test/math.test.mjs` + `run.mjs`),用于验证 scope 与 intent 拦截。
149
+
150
+ ## [1.0.11] — 2026-11-09 — rejected 记录退出计数与容量口径
151
+
152
+ ### 体验问题
153
+ - 被 /memory reject 掉的记录仍被算进「库 N 条」和衰减报告,把数字撑得不像实话。
154
+
155
+ ### 修复
156
+ - `/memory debug` 的库计数改为活动库条数,并单列 `rejected N`。
157
+ - `/memory decay` 报告与 `selectEvictions()` 都跳过 `rejected`:它们不参与排序、不占容量、永不归档。
158
+ 报告末尾显示「已排除 N 条 rejected(审计留档…)」。
159
+ - `/memory list` 默认隐藏 rejected;`/memory list --all` 才显示,隐藏时给出提示行。
160
+
161
+ ### 测试
162
+ - T77:库计数只算 2 条、报告出现「已排除 1 条」、rejected 不进排行、不会被 selectEvictions 选中。
163
+ - e2e T60:reject 后 list 不显示、`list --all` 显示、debug 出现 `rejected 1`。
164
+ - `selftest 87 pass / 0 fail`、`e2e 21 pass / 0 fail`。
165
+
166
+ ### 说明
167
+ - rejected 仍保留在 store.json 里作为审计证据,也能被 `supersede`/`restore` 流程引用;只是不再参与任何统计与容量判断。
168
+
169
+ ## [1.0.10] — 2026-11-09 — 意图桶分离:run 与 write 不再互相吸收
170
+
171
+ ### 修复
172
+ - 合并前先比意图类:`R.intentOf()` 把文本归为 run(跑 / 运行 / 执行)或 write(新增 / 实现 / 修改),
173
+ `R.intentConflict()` 只在两侧都有明确意图且不同时返回 true。
174
+ - 两条合并路径(结果指纹、词面计分)都先过 `intentBlocked()`;被拦下时按新任务建档,
175
+ reason 追加「意图不同(run vs write),不并入 exp-N」。
176
+ - 任一侧意图含糊(同时出现 run 与 write,或都没有)不否决——不确定时宁可合并,避免库碎裂。
177
+
178
+ ### 测试
179
+ - T75:run 配方与 write 任务互相冲突,同类不冲突。
180
+ - T76:含糊 / 空文本永不否决。
181
+ - e2e T59:库里已有 run 型 verified 经验时,write 型任务照旧新建,且 reason 写明意图不同。
182
+ - `selftest 86 pass / 0 fail`、`e2e 20 pass / 0 fail`。
183
+
184
+ ### 说明
185
+ - 这条闸门针对「同一批名词、不同活」的误合并(例如「运行图片处理脚本」与「写一个图片处理脚本」)。
186
+ 它不替代产物类型闸门:类型冲突先拦,意图冲突再拦。
187
+ - exp-30 因两次误合并导致 occurrences 虚高,已归档(`store.archive` + `evictions` 审计,可 `/memory restore`)。
188
+
189
+ ## [1.0.9] — 2026-11-09 — 产物类型闸门(跨类型不合并)
190
+
191
+ 现象:一个「把图中 54+188 改成 91」的图片编辑任务,被合并进一条已有经验的轨迹里,
192
+ occ 从 1 变 2。产出「全绿」加上词面相似度就足以命中合并,**没人在看产出是什么类型**。
193
+
194
+ 根因(两条路径都要挡):
195
+
196
+ 1. 指纹匹配只看文件名与 green,.png 产物不入 targets,于是「双方都有绿 + 某个名字相同」即可合并;
197
+ 2. 词面合并(mergeThreshold)根本不关心产物类型——本次真实合并其实是这条路
198
+ (byFingerprint: false,score 0.761 / 0.891),不是指纹。
199
+
200
+ 修法:
201
+
202
+ - 新增 artifactKindOf() / artifactKindsOf() / artifactTypesConflict():按扩展名把产物分成
203
+ code / image / doc / binary 四类;两侧都有类型证据且**无交集**即判冲突。
204
+ - outcomeFingerprint() 现在记录 kinds(仍保留 targets 的代码文件语义,兼容旧记录)。
205
+ - fingerprintsMatch() 增加类型前置条件:同名不同类型不算同一结果。
206
+ - runCapture() 对**词面合并**同样加类型闸门,冲突时不合并、按新任务建档,
207
+ 并在 reason 里写明「产物类型不同(code vs image),不并入 exp-N(相似度 X)」。
208
+ - 旧记录在 loadStore() 时由 targets 反推 kinds(幂等),让闸门对历史库同样生效。
209
+
210
+ 测试:T72(.png 结果不得与 .py 配方指纹合并)、T73(仅一侧有类型证据时不否决)、
211
+ T74(outcomeFingerprint 正确记录 kinds 与 targets)。合计 selftest 84 pass、e2e 19 pass。
212
+
213
+ ## [1.0.8] — 2026-11-09 — CI 测试与持久化回归
214
+
215
+ ### 新增
216
+
217
+ - `.github/workflows/test.yml`:每次 push 到 `main` 与每个 pull request 都跑
218
+ `node tools/selftest.mjs` 与 `node tools/e2e.mjs`,矩阵覆盖 Ubuntu/Windows × Node 20/22。
219
+ 此前仓库只有「打 tag 时发布 npm」的流水线,代码从不经过测试。
220
+ - `lib/store.js`:把 store 读写从 `index.js` 抽出来,成为可测试的纯模块
221
+ (`emptyStore / normalizeStore / readStore / writeStore`,写入走临时文件 + rename)。
222
+ - `T69`:写入 → 重载 → 断言 `archive` / `evictions` / `nextId` 全部还在。
223
+ - `T70`:未知字段(未来版本新增的表)在旧版本 reload 时不被丢弃。
224
+ - `T71`:损坏 JSON 返回 `{error, store}` 而不是抛异常;缺失文件标记 `missing`。
225
+
226
+ ### 修复
227
+
228
+ - `loadStore()` 不再自己重建字段,改走 `normalizeStore()`——这正是 1.0.7 里
229
+ `archive` / `evictions` 被静默抹掉的根因。
230
+ - `e2e` T46 原先断言 `store.archive === undefined`,等于把上面那个 bug 写进了测试;
231
+ 改为断言「归档条数为 0」。
232
+
233
+ ### 测试
234
+
235
+ `selftest 81 pass / 0 fail`、`e2e 19 pass / 0 fail`(合计 100)。
236
+
237
+ ## [1.0.7] — 2026-11-09 — 检索:把「坑」纳入档案,修流水账挤掉垂直经验
238
+
239
+ 现象:真实任务(plugin-test-5 写 .mjs、require 不存在的模块、报错后排查修复)
240
+ 检索 PASS 且 top=0.26,但注入的是 exp-23/24(写文件流水账),exp-26(node.exe / MODULE_NOT_FOUND 避坑指南)
241
+ 连前五都没进。
242
+
243
+ 根因两条:
244
+
245
+ 1. **打分只看 title/when/keywords,不看 pitfalls/reverse_examples**。经验真正的价值在坑里
246
+ (`MODULE_NOT_FOUND`、`CommandNotFoundException`),这些词根本没进索引。
247
+ 2. **特征桶权重太平均**。`#error` 和 `#node` 与 `#file` 同权,一句「报错」压不过一堆「文件/新增」。
248
+
249
+ 修法:
250
+
251
+ - `profileText()` 把 `pitfalls` 与 `reverse_examples` 一起入索引。
252
+ - 特征桶分级权重:`#error` 3、`#node` 2.6、`#module` 2.2、`#shell`/`#debug` 2、`#run`/`#write` 1.8、其余 1.4/1.2。
253
+ - 关键特征(`#error`/`#node`/`#module`)同时命中时再给 0.08/个 的确定性加分。
254
+ - 新增工具 `experience_archive`:把记录移入 `store.archive`(可 `/memory restore` 恢复,写 `evictions` 审计),
255
+ 用于清理已批准的流水账;不删除数据。
256
+
257
+ 实测(真实库,清理 exp-22..25 后剩下 4 条):四条真实失败查询上 exp-26 全部排第 1
258
+ (0.364 / 0.373 / 0.502 / 0.502),无关查询仍为 0。测试:selftest 78 pass、e2e 19 pass。
259
+
260
+ ## [1.0.6] — 2026-11-09 — 检索:停用词、特征桶与 idf 加权
261
+
262
+ 现象:一条噪声较大的任务描述("跑一个会踩坑的 .mjs 脚本…内置 node.exe 模块报错")
263
+ 在旧打分下只有 0.07,exp-26 根本没被召回。
264
+
265
+ 修法:
266
+
267
+ - 停用词扩到口语填充词(随便/故意/场景/试试…),噪声不再稀释覆盖率。
268
+ - 新增特征桶(`#node` `#shell` `#error` `#module` `#test` `#file` `#path` `#run` `#write` `#debug` …),
269
+ 在原始文本上判定,词表不重合也能命中;`#run`/`#write` 区分"跑脚本"与"加函数"这两类意图。
270
+ - 打分改为 idf 加权:跨经验常见的词(报错/模块/脚本)权重低,稀有词(node.exe/MODULE_NOT_FOUND)权重高;
271
+ 中文 bigram 权重 ×0.45(它们最容易误撞)。
272
+ - 索引文件名 basename,"truncate.test.mjs" 也能匹配 "test/truncate.test.mjs"。
273
+ - 阈值可配:`/memory config set recallThreshold 0.1`(默认仍 0.15;实测无关查询得分为 0,
274
+ 噪声查询下第二名 0.143,降到 0.05 会把第二名一起放进来)。
275
+
276
+ 实测(真实库 7 条):噪声查询 exp-26=0.159 排第一;"新增纯函数+用例" exp-1=0.852;
277
+ "重命名图片" 全部 0。测试 77 pass(selftest)/ 19 pass(e2e)。
278
+
279
+ ## [1.0.5] — 2026-11-09 — 第一个 npm 版本
280
+
281
+ - 发布到 npm:`dsh-experience@1.0.4` 是首个公开版本,`1.0.5` 修正安装说明。
282
+ - `README.md`:安装段从 `file:`/`link:` 改为 `install_bundle target=dsh-experience@<版本>`,并补升级方式(Web 插件页 Update / 重新 install)与“npm 安装是复制、改代码不生效”的说明。
283
+ - 新增 `tools/pack-check.mjs`(发布前按 `files` 白名单核对入包内容)与 `.github/workflows/publish.yml`(推 `v*` tag 校验版本一致、跑 selftest+e2e 后 `npm publish`)。
284
+
285
+ ## [1.0.4] — 2026-11-09 — npm 发布通道
286
+
287
+ - 新增 `.github/workflows/publish.yml`:推送 `v*` tag 时先校验 tag 与 `package.json` 版本一致、跑 selftest 与 e2e,再 `npm publish --access public`。
288
+ - 新增 `tools/pack-check.mjs`:发布前按 `files` 白名单解析实际入包文件,检查必需项缺失与运行时产物(`store.json`/`config.json`/`applied.json`/`probe.json`)泄露。
289
+ - `package.json`:新增 `publishConfig.access=public`,`files` 纳入 `tools/e2e.mjs`;发布后其他人可用 `install_bundle dsh-experience@<版本>` 安装并在插件页原地升级。
290
+
291
+ ## [1.0.3] — 2026-11-09 — superseded 状态与 G8 闸门
292
+
293
+ 自动捕获能学到经验,但学不到"这条经验已经被规则否决过"。于是出现过一个循环:某条经验与当前
294
+ AGENTS.md 冲突(例如沙箱里必须直跑测试,而经验写的是 `node --test`),人工 `reject` 之后,
295
+ 下次同样的任务又被自动捕获成新候选,等人再拒一次。
296
+
297
+ ### 新增
298
+ - **`superseded` 状态**:`/memory supersede <id> [原因]` 把经验标记为"与当前规则冲突"。
299
+ 不删除(保留证据),写入 `supersede: { by, at, rule }`,清除 `verify`。
300
+ - **G8 闸门**:新轨迹若与某条 `superseded` 记录**结果指纹相同**,直接拒绝收录,理由写成
301
+ `G8: 与已否决经验 exp-N 指纹相同(原因)→ 不再收录`。改回 `verified` 后闸门自然解除。
302
+ - **`/memory review` 单列已否决记录**:不再把 `superseded` 混进候选,也不再提供 approve。
303
+ - `lib/capture.js` 导出 `gateSuperseded()`,纯函数,可单测。
304
+
305
+ ### 测试
306
+ - `tools/selftest.mjs` → **72 pass / 0 fail**(新增 T61 G8 判定、T62 review 展示)。
307
+ - `tools/e2e.mjs` → **19 pass / 0 fail**(新增 T58:否决后不再注入、同指纹轨迹被 G8 拦下)。
308
+ - 合计 **91 pass / 0 fail**。
309
+
310
+ ### 已知限制
311
+ - G8 依赖结果指纹,只对"同一组产物文件"生效;换了文件名或换了目录不会命中(这是刻意的:
312
+ 同名产物才说明是同一条配方)。旧记录若没有 `fingerprint`,G8 不会触发。
313
+
314
+ ## [1.0.2] — 2026-11-09 — 整机测试(e2e)与红线修复
315
+
316
+ 这一版把 `index.js` 的接线第一次纳入自动化测试:新增 `tools/e2e.mjs`,用假的 Cordis context 驱动真实的 `apply()`、`pre-step`、`tools/result`、`turn-stopping` 与 `/memory` 命令,全部落在临时 `DSH_HOME` 上,不碰真实档案。18 条用例抓出 6 个真实缺陷。
317
+
318
+ ### 修复
319
+ - **scope 闸门一直是瞎的(P0)**:`recordCall()` 用 `C.clip()` 存 hint,而 `clip()` 会脱敏路径,于是 `buildCandidate()` 拿到的已经是 `<path>/pad.js`。结果 `record.scope` 全为空数组、`record.fingerprint.targets` 只剩占位符——`scopeMustMatch` 与指纹合并在自动捕获上从未生效。改为 `hint` 存**未脱敏**原文(`C.clipRaw()`),只在写进 `store.turns` 前统一脱敏;渲染字段仍走 `clip()`。
320
+ - **恢复型轨迹无法按指纹合并**:`outcomeFingerprint().green` 原要求"本次没有任何测试失败",导致"先失败后跑通"的轨迹被判非全绿、退回 lexcial 合并而新建重复记录。改为**以最后一次测试结果为准**,失败本身进 `reverse_examples`。
321
+ - **反例只有异常类名**:`findRecoveries()` 只写 `(TypeError)`,丢掉了消息。新增 `errorDetail()`,写入 `失败(TypeError: pad is not a function)`。
322
+ - **`rendered` 标记不落盘**:`markRendered()` 只改内存,进程在组装与 turn 收尾之间崩溃就丢失"是否真的注入过"的唯一证据。改为立即 `persist()`。
323
+ - **新建捕获没有诊断理由**:`runCapture()` 的新建分支不写 `outcome.reason`,`/memory debug` 只显示 `PASS exp-N`。现在写明 `新建 exp-N(scope …;指纹 … 全绿)`。
324
+ - **scope 会录进用户名**:`mentionedDirs()` 取最深段但仍保留祖先段,路径里的用户目录名会被记成项目名。改为**只保留每段路径的最深非通用目录**,并忽略 `node-project/feature` 这类任务类型形状。
325
+ - **旧档案仍带绝对路径**:新增 `C.scrubRecord()`,`loadStore()` 时对历史记录做一次幂等脱敏(记录数会打一行 warn)。
326
+
327
+ ### 测试
328
+ - 新增 `tools/e2e.mjs`(`npm run test:e2e`):18 条,覆盖注册/冷启动/捕获/G7 拒绝/批准后注入/部分命中查找/只读衰减报告/teach 钉住/agent 生命周期/重复配方合并/跨目录隔离/无关查询不注入/删除保护/review 只读/config 类型强转/双 agent 隔离/持久层无绝对路径/新建理由。
329
+ - `tools/selftest.mjs` 增至 70 条(新增 T58 目录识别、T59 幂等脱敏、T60 下划线产物命名)。
330
+ - 合计 **88 pass / 0 fail**。
331
+
332
+ ### 运行时验证
333
+ - `applied.json` = `{"build":"r10-scope-fix"}`(重载后 `application: applied`)。
334
+ - 发布树重建:`leak scan: clean (0 hits)`,`tools/e2e.mjs` 已加入白名单。
335
+
336
+ ### 已知限制
337
+ - 旧记录(本机 exp-1/5/13/15/16/17)的 `fingerprint.targets` 仍是 `<path>/…` 占位符,与新记录的裸 basename 不互通;它们会随时间衰减,不做回填。
338
+ - `experience_forget` 收单个 `id`,`experience_lookup` 收 `ids[]`,参数形状不一致(历史原因,已在 README 标注)。
339
+
340
+ ## [1.0.1] — 2026-11-09 — r9 实测修复
341
+
342
+ ### 修复
343
+ - **`experience_lookup` 部分命中不再静默**(P2,`694bd23`):新增 `lib/lookup.js` 的 `splitLookup()/renderLookup()`。命中项保持 store 顺序(与旧输出逐字一致),缺失 id 按请求顺序去重后追加 `未找到:exp-999`。全命中/全不中措辞未变。
344
+
345
+ ### 新增
346
+ - **G7 步骤质量闸门**(P1,`903e4a6`):自动捕获不再把脚手架命令当配方。
347
+ - `summarizeCalls()` 丢弃 `、`New-Item`、`Test-Path`、`Get-ChildItem`、`Set-Location`、`Write-Host` 等前缀与裸赋值;
348
+ - `effectiveSteps()` 只数写操作与测试执行,有效步骤 < `minEffectiveSteps`(新配置键,默认 2)即放弃;
349
+ - `actionableTitle()` 要求标题是「动作 + 具体对象」,`写 <path>/<name>.js` 一类碎片被拒;
350
+ - `buildCandidate()` 优先用原始 basename 生成标题(`truncate.js` → `新增 truncate`),归一化占位符不再充当产物名;
351
+ - 判定失败写 `trace.skipReason = 'G7: …'`,不影响 G1–G6 短路链。原 10 个数值默认值未改动。
352
+
353
+ ### 验证
354
+ - `node tools/selftest.mjs` → **67 pass / 0 fail**(T32 lookup 六种输入;T33 脚手架被拒;T34 真实配方通过;T35 标题规则;T36 1000 调用 < 20ms)。
355
+ - 运行时 `applied.json` = `{"build":"r9-step-gate"}`,`config` 含 `minEffectiveSteps: 2`。
356
+ - 已知:G7 的拒绝分支目前只有单测覆盖,真实 `capture: SKIP G7` 需一个纯脚手架 turn 才会出现。
357
+
358
+ ## [0.2.0-m2] — 2026-11-09
359
+
360
+ ### 新增
361
+ - **结果指纹合并(r7)**:`lib/capture.js` 的 `outcomeFingerprint()/fingerprintsMatch()`;写文件 basename 相同且本次测试全绿 → 直接认定同一终态,合并时不再看 stepsJaccard、也不看 mergeThreshold。
362
+ - **指纹等效证据**:`lib/evidence.js` 的 `markFingerprintEquivalent()`;`promotionState()` 里 `effective()` 让 `record.fingerprint.green` 或 `byFingerprint` 的运行稳定性记 1.0,detail 追加「指纹等效 N」。
363
+ - **G3 验证型放行**:全绿验证轨迹(无写操作)也算独立证据,`verificationOnly` 随 run 落库。
364
+ - **scope 硬闸门**:不同目标目录不合并(`scopeMustMatch`),`mergeThreshold` 默认 0.75。
365
+ - `/memory reevaluate [id]`、`/memory review --auto`;`[2.5 证据]` 层打印「为什么还没升级」。
366
+
367
+ ### 修复
368
+ - 用户报 exp-11 指纹合并后仍被判 `stability 0.2 < 0.7`:判据从「仅比 steps」改为「指纹命中优先」。现在 `exp-11` 显示 `stability=1(原始 0.2)· ready`。
369
+ - 配置类型:`/memory config set` 走 `coerceConfigValue`,`"true"` 不再存成字符串。
370
+
371
+ ### 事故与恢复(必须记录)
372
+ - **自伤**:用 `Get-Content -Raw | Set-Content` 改 `index.js` 帮助文案,按非 UTF-8 写回,90 处中文变 U+FFFD;后续用 codepage 936 反推进一步损坏,源文件不可逆。
373
+ - **恢复**:从会话日志(zstd 多帧 `session.v4.jsonl.zstd`)解出全部 `tool/call`,按 seq 重放 `write`/`edit` 得到 `index.js` 底线(r3.3 谱系),再用 `tools/_restore-r7.mjs` 一次性补回 16 处 r4–r7 接线(import evidence / migrateStore / isRecallable / noteEvidence / noteFailure / 指纹优先合并 / scope 否决 / `[2.5 证据]` / review --auto / config 强转 / 计数口径)。
374
+ - **教训(已写进规则)**:源码只允许 `write`/`edit` 工具或 `fs.writeFileSync(..., 'utf8')`;禁止 shell 文本重写。恢复类脚本统一放 `tools/`,每个锚点做唯一性断言。
375
+
376
+ ### 验证
377
+ - `node --check index.js` 通过;`tools/selftest.mjs` **55 pass / 0 fail**。
378
+ - 运行时 `applied.json` = `{"build":"r7-fingerprint"}`;`experience_debug` 输出含 `[2.5 证据]` 与新计数口径。
379
+
380
+ ## [0.1.0] — 2026-11-09 — MVP 封版
381
+
382
+ **状态**:已验收(用户 m00389 通过)。快照见 `archive/v0.1/`,安装方式为 Junction(`link:`),改动即热更。
383
+
384
+ ### 交付
385
+
386
+ - 纯 Host bundle,零运行时依赖(只用 `node:fs/os/path/crypto`),`plugin_manager install_bundle target=link:...` 安装。
387
+ - 生命周期介入点:`agent/pre-step`(waterfall,装配后准入前)、`agent/turn-stopping`(trace 落盘)。
388
+ - 注入通道:`agent.ctx.systemPrompt.context({ name: 'experience-recall-v2', order: 9000, required: true })`,`text()` 每次准入前重读。
389
+ - 工具:`experience_record` / `experience_lookup` / `experience_forget` / `experience_debug`(Inspect `Tool.listTools` 已核验)。
390
+ - 命令:`/memory list|show|approve|reject|teach|why|debug`。
391
+ - 存储:`${DSH_HOME:-~/.dsh}/experience/store.json`,含 `experiences[] / injections[] / turns[]`。
392
+ - 六层诊断:存储 → 可召回 → 检索 → 注入 → 模型可见 → 生效,逐层 PASS/FAIL + `hintFor()`。
393
+
394
+ ### 已核验证据
395
+
396
+ - `applied.json` 自证:`{"build":"r2-candidate-scoring","pid":360,...}`。
397
+ - 注入链:`inj-1 nonce=exp-22c77b rendered=true tokens=297`,同一 step 模型可见并回显 nonce。
398
+ - 检索:`exp-1=0.619(verified)`,阈值 0.15。
399
+ - 效率:首次任务 7 次调用(3 发现 + 2 写 + 2 测试);复用后 3 次调用(2 写 + 1 测试),零发现、零踩坑。
400
+
401
+ ### 归档文件(SHA256 前 12 位)
402
+
403
+ | 文件 | 字节 | SHA256[:12] |
404
+ | --- | --- | --- |
405
+ | index.js | 25428 | 7C4714B9AAA7 |
406
+ | package.json | 363 | ABEF3E840327 |
407
+ | cordis.patch.yml | 113 | ADF70F518A12 |
408
+ | README.md | 2870 | 0EA5BEB4677A |
409
+ | ACCEPTANCE.md | 5508 | BCB02B5F31C9 |
410
+
411
+ ### 已知限制(不修,进 v0.2 规划)
412
+
413
+ - 召回只在 turn 的 `step === 1` 触发;新 turn 首条消息必须是任务本身。
414
+ - `candidate` 永不自动召回;升级只能靠人工 `/memory approve`(Agent 无入口)。
415
+ - 沙箱 shell 对 `~/.dsh` 只读(写入 EPERM),改库只能走插件工具。
416
+ - token / 墙钟无同口径基线,效率只报调用数与测试次数。
417
+ - `file:` 安装是复制快照,务必用 `link:`,否则编辑不生效(已踩坑,见 ACCEPTANCE.md 第 0 节)。
418
+
419
+ ## [0.3.0-m3.1-m3.3] — 2026-11-09
420
+
421
+ - 新增 `lib/decay.js`:`rankOf / freshnessOf / utilityOf / stateOf / isPinned / describeDecay / selectEvictions / markDecay / decayLine / renderDecayReport / applyEvictions / restoreRecord`,全部纯函数、`now` 注入。
422
+ - `[2.6 衰减]` 层接入 `experience_debug` 与 `/memory why`;`/memory debug` 追加整库衰减报告。
423
+ - `active → dormant`:每轮 `agent/turn-stopping` 写入 `record.decay`(只写判定字段,不动 score/status/steps);`recallable()` 跳过 dormant(pinned 豁免)。
424
+ - `/memory decay [--age N] [--apply]`:默认只列 Top-5 建议;`--apply` 移入 `store.archive` 并写 `store.evictions` 审计,**不物理删除**;`/memory restore <id>` 恢复。
425
+ - 单测 62 条全绿(新增 T29/T30/T31)。
426
+
427
+ ## [0.2.0-m1] — 2026-11-09 — L1 自动捕获(已验收)
428
+
429
+ **状态**:用户实测 T1/T2/T3 通过(m00541)。运行 build `r3.3-steps-priority`;快照 `archive/v0.2-m1/`,哈希见该目录 `MANIFEST.md`。
430
+
431
+ ### 交付
432
+
433
+ - 采集点:`tools/result`(emit,零 dispatch 延迟)→ `liveCalls[agentId]`;抽取在 `agent/turn-stopping`,纯字符串,无 IO、无模型调用。
434
+ - 六闸门(G1 成功调用≥2 / G2 非 `/memory`·非 `experience_*` / G3 有成功写或恢复链 / G4 同 taskType 且 score≥0.6 合并 / G5 配额 / G6 序列化≥120 字符),每次给出 `trace.capture.reason`。
435
+ - 脱敏:`sk-`/`ghp_`/`AKIA`/`xox`/Bearer/key=value/base64(混合字符) + Windows 用户目录与绝对路径归一化。
436
+ - 性能熔断:抽取 > `captureBudgetMs`(20ms) 即放弃;配额 `dailyLimit=10` / `libraryLimit=200` / `mergeThreshold=0.6` 全部走 `~/.dsh/experience/config.json`,`/memory config set` 可改。
437
+ - 命令:`/memory review [--approve-all | approve <n> | reject <n>]`、`/memory config`、`/memory stats`;工具 `experience_review` 只读、不可自批。
438
+ - 探针:首次 `tools/result` 把 `ToolExecution` / `ToolExecutionResult` 的真实字段写入 `probe.json`,据此收敛 `pickOk`/`pickError`。
439
+ - 观测:trace 增加 `calls[]`、`recallMs`、`capture{ok,reason,ms,id,merged,added}`。
440
+
441
+ ### 已核验证据(用户实测)
442
+
443
+ - `recallMs=0`;`capture: PASS exp-5 (0ms)`——捕获对每轮耗时无可观测影响。
444
+ - `[2 可召回] PASS (verified=2)`;注入 `rendered=true tokens=388 ids=exp-1,exp-5`(双经验同时注入)。
445
+ - 步骤数 9 → 6。
446
+
447
+ ### 修复
448
+
449
+ - `summarizeCalls` 曾把探索当核心步骤(exp-5 记录了 `跑 Get-ChildItem`、`跑 Get-Content …/slugify.js`)。新增 `stepPriority` 阶梯:测试执行 4 / 写 3 / 其他真实命令 2 / 探索 0(`Get-ChildItem`、`Get-Content`、`dir`、`grep`、`read_file` 等一律剔除)。补充 5 条用例,T0/T4 由 31 条升至 **35 pass / 0 fail**。
450
+
451
+ ### 未做(进 M2)
452
+
453
+ - 自动/半自动升级仍不存在:`candidate` 只能由人批准。
454
+ - 无衰减、无版本化、无跨会话证据累积。
455
+
456
+ ## [0.2.0-m2] — 2026-11-09 — L2 证据升级(M2.0–M2.2)
457
+
458
+ **状态**:代码与合成测试完成;真实链路(M2.5)等用户手动触发。
459
+
460
+ ### 交付
461
+
462
+ - **schema v2(M2.0)**:每条经验新增 `proof = { runs[], distinctSessions, minStability, lastSuccessAt, lastFailureAt }`;`loadStore()` 读入即跑幂等迁移并回写,`version: 2`。
463
+ - **证据纯函数(M2.1)**:`lib/evidence.js` —— `emptyProof / ensureProof / stepsHash / jaccardSteps / recordEvidence / promotionState / promote / migrateStore`,零依赖、无 IO、不调模型。
464
+ - **接线(M2.2)**:`runCapture` 三条路径都记账——新记录、G4 合并、G3 失败(按 `mergeThreshold` 找最匹配经验记 `failure`);证据状态只由这段确定性代码写。
465
+ - **升级链**:`candidate →(≥2 个独立 session 成功 + 步骤 Jaccard ≥ 0.7 + 成功之后无失败)→ auto-verified →(人工)→ verified`。`promote()` 最多写 `auto-verified`(`verify.by = 'evidence'`,confidence 0.55),**永不写 `verified`**。
466
+ - **召回闸门**:`isRecallable()` —— 只有 `verified`;`auto-verified` 仅在 `allowAutoVerified === true` 时放行,默认 false。
467
+ - **诊断(M2.4)**:`/memory debug` 与 `experience_debug` 新增 `[2.5 证据]` 行,直接打印不升级的理由(`autoPromote=false:策略关闭…` / `未满 2 个独立 session;独立 session 1/2…` / `步骤稳定性 0.5 < 0.7,视为不同变体` / `最后一次成功之后出现失败,证据链作废`)。
468
+ - 配置新增 `autoPromote / minDistinctSessions / stepsJaccard / allowAutoVerified`,全部可经 `/memory config set` 读写。
469
+
470
+ ### 已核验证据
471
+
472
+ - `node --check index.js lib/evidence.js` 均 exit 0。
473
+ - `tools/selftest.mjs`:**43 pass / 0 fail**(原 35 + M2 八条:T5 两 session 升级、T6 同 session 不独立、T7 失败作废、T8 步骤漂移、T9 策略关闭文案、T10 不碰已验证、T11 迁移幂等、T12 哈希/Jaccard)。
474
+ - 运行时:`applied.json` = `{"build":"r4-l2-evidence","pid":360}`;`store.json` 已迁移为 `"version": 2`,exp-1/exp-5 均补齐 `proof`;`experience_debug` 输出 `build: r4-l2-evidence`、四项新配置、`[2.5 证据]` 行。
475
+
476
+ ### 未做(进 M2.3/M2.5)
477
+
478
+ - `/memory review --auto` 还没有单独视图(`reviewTargets` 已含 `auto-verified`,可被批量批准)。
479
+ - 真实链路(两次真实任务攒够证据)未跑,等用户触发;`autoPromote` 与 `allowAutoVerified` 默认关闭,本次不改变默认值。
480
+
481
+ ## [0.2.0-m2.1] — 2026-11-09 — M2 缺陷修复(配置类型 / 计数口径 / 证据基线)
482
+
483
+ 用户实测触发三个真实缺陷,全部修复并有单测。
484
+
485
+ ### 修复
486
+
487
+ - **配置类型未转换**:旧逻辑 `Number.isFinite(Number('true'))` 为 false,`autoPromote` 落库成字符串 `"true"`;`isRecallable()` 用 `=== true` 比较,字符串永远不成立。新增 `C.coerceConfigValue(key, raw)`:按 `DEFAULT_CONFIG` 的类型把 `true/false/yes/no/on/off` 转布尔、数字串转 Number,类型不符直接报错;`loadConfig()` 对已落库的字符串配置同样纠正并回写(实测 `config.json` 由 `"autoPromote": "true"` 变为 `true`)。
488
+ - **`auto` 计数误导**:`experiences: … auto 1` 统计的是 `source.auto`(自动捕获条数),不是 `auto-verified`。现在分开打印 `auto-verified N, auto-captured M`。`distinctSessions` 本身累加正确(exp-5 已到 2)。
489
+ - **证据基线被历史污染**:`stability` 原以记录自身的 `steps` 为基线,而 exp-5 的 steps 来自 r4 前的探索污染(含 `跑 Get-ChildItem`、`跑 Get-Content`),首轮即算出 `0.182`,此后永远 `drift`。改为:
490
+ - 每次运行存 `steps` 快照;`stability = Jaccard(上一次成功的 steps, 本次 steps)`;
491
+ - 无快照的历史运行不参与比较(`minStability` 只取有快照运行之间的相邻比较);
492
+ - 快照不足时返回新状态 `needs-baseline` 并写明“还需 N 次同类成功运行”,不再伪造 `1`;
493
+ - `[2.5 证据]` 行同时打印 `runs/sessions/stability`,无快照显示 `n/a`。
494
+
495
+ ### 已核验证据
496
+
497
+ - `tools/selftest.mjs`:**47 pass / 0 fail**(原 43 + T13 配置转换、T14 相邻运行基线、T16 无快照历史运行)。
498
+ - 运行时代码:`build: r4-l2-evidence`,`config: {…"autoPromote":true…}`(布尔),`[2.5 证据] WAIT exp-6 … runs=0 sessions=0 stability=n/a → 尚无结构化证据…`(冒烟后已删除 exp-6)。
499
+ - 纯函数实测:同一记录两次不同 session 成功后 `promotionState → code:ready / eligible:true`,`promote()` 只写 `auto-verified`。
500
+
501
+ ### 未变
502
+
503
+ - `autoPromote` 仍默认 `false`,`allowAutoVerified` 默认 `false`:auto-verified 只是更高优先级的候选池,人工不点头就不进召回。
504
+
505
+ ## 规划
506
+
507
+ - v0.2:经验库自动扩展(L1 自动捕获 / L2 证据升级)+ 衰减机制 → `docs/ROADMAP-v0.2.md`
@@ -0,0 +1,87 @@
1
+ # Contributing to dsh-experience
2
+
3
+ Thanks for taking a look. This is a small, zero-dependency plugin; the bar for a PR is
4
+ "adds a test, keeps all tests green, and does not add a runtime dependency".
5
+
6
+ ## Setup
7
+
8
+ ```bash
9
+ git clone https://github.com/Aatzer0/dsh-experience.git
10
+ cd dsh-experience
11
+ node --version # needs >= 20
12
+ node tools/selftest.mjs
13
+ ```
14
+
15
+ There is no build step and no `npm install` — the plugin only uses `node:` builtins.
16
+ For live development against a running Harness instance, install it by link:
17
+
18
+ ```text
19
+ plugin_manager install_bundle target=link:<absolute path to this checkout>
20
+ ```
21
+
22
+ Then reload after edits:
23
+
24
+ ```text
25
+ plugin_manager set_bundle dsh-experience enabled=true
26
+ ```
27
+
28
+ `~/.dsh/experience/applied.json` contains the build id, PID and timestamp of the
29
+ currently loaded code — the fastest way to prove a reload actually happened.
30
+
31
+ ## What to work on
32
+
33
+ - Bug reports with a reproducible trace are the most valuable contribution.
34
+ - New capture signals / gates: open an issue first. Every gate changes what gets
35
+ remembered, and a wrong gate silently poisons the library.
36
+ - Anything that makes the plugin call out to the network or add a dependency:
37
+ these need a design discussion, not a PR.
38
+
39
+ ## Coding conventions
40
+
41
+ - **No runtime dependencies.** `node:fs`, `node:path`, `node:crypto` only.
42
+ - **Pure logic goes in `lib/`**, side effects stay in `index.js`. All `lib/` modules
43
+ must be importable without a Harness runtime so they stay unit-testable.
44
+ - **Never write source files through a shell pipeline.** On Windows,
45
+ `Set-Content -Encoding UTF8` writes a BOM and CRLF and corrupts non-ASCII text;
46
+ use an editor or `fs.writeFileSync(path, text, 'utf8')`.
47
+ - **Redaction is part of the contract.** If you capture a new field, run it through
48
+ the redactor and add a test asserting a secret-looking value does not survive.
49
+ - Comments in code are English; user-facing strings (command output, tool
50
+ descriptions, docs) are Simplified Chinese, matching the `/memory` surface.
51
+
52
+ ## Tests
53
+
54
+ `tools/selftest.mjs` is a single-file, `node:assert`-based suite with two kinds of cases:
55
+
56
+ 1. **T0/T4 synthetic** — pure function behaviour: scoring, gates, merge, fingerprints,
57
+ evidence promotion, decay math, eviction/restore.
58
+ 2. **Regression fixtures** — every bug ever fixed gets a case named after the symptom.
59
+
60
+ Run it before and after your change:
61
+
62
+ ```bash
63
+ node tools/selftest.mjs
64
+ ```
65
+
66
+ The suite must stay at `0 fail`. If you change a default in `lib/capture.js`
67
+ (`DEFAULT_CONFIG`), update the deep-equal assertion in the suite as part of the same commit.
68
+
69
+ ## Pull requests
70
+
71
+ 1. Fork, branch from `main` (`feat/...`, `fix/...`).
72
+ 2. Keep the diff focused: one behaviour per PR.
73
+ 3. Include:
74
+ - what changed and why (one paragraph, not a changelog),
75
+ - the failing case that motivated it, if it is a fix,
76
+ - the selftest output line (`N pass / 0 fail`).
77
+ 4. Update `CHANGELOG.md` under `## [Unreleased]`.
78
+
79
+ ## Commit messages
80
+
81
+ Conventional Commits, imperative mood, English:
82
+
83
+ ```text
84
+ fix(capture): skip verification-only turns when the suite is red
85
+ feat(decay): add --age simulation flag
86
+ docs: clarify recall-only-on-step-1 constraint
87
+ ```
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Aatzer0
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.