真缺陷(上一片登记):`maxResults` 在面板上**根本改不了** —— ① 字段表里没有这个 字段(UI 不显示输入框);② `saveProvider` 只并 provider/apiKey/baseUrl/enabled; ③ `mergeSelectionPatch` 只处理 primary/fallback ⇒ **手工塞进 values 也会被静默丢弃** (文件不变、也不报错)。「我改了它却没变」正是要消灭的那种假话。 - `panel.go`:表单加 `{Key: FieldMaxResults, Type: sdk.FieldNumber, Min: 1}`(**SDK 本就有 数字类型**,未自造;前端本就渲染 number ⇒ **不需要改前端**)。刻意不设 `Default` (设了会让一次「只改 key」的保存把默认值固化进文件、还可能盖掉 `.env`)。 - `websearch_selection.go`:`mergeSelectionPatch` 里按**三态**落 `maxResults` —— 缺位/null ⇒ **不改动**;正整数(含整数字符串)⇒ 采用;0/负数/小数/空串/非数/ 布尔/数组 ⇒ **400 `invalid_payload`** 且**盘上一字节不动**(绝不静默忽略)。 - `reauth` 回填该值(否则一保存就把上限清掉)。 - `.env.example`:该键的示例值与「默认 5」注释同步为 **10**(代码默认已改;⚠️ `.env` 未动)。 验收(主会话复跑 + 亲自变异):**整树 `go build ./...` 通过**;三包 vet/测试绿; 端到端「面板存 20 ⇒ 模型要 50 也只给 20、要 3 给 3」(走真 action hub → 落盘 → 热重载 → WithSearchMaxResults → 真 dispatcher,未重启后端); 我用 -overlay 把 `f.MaxResults = maxResults` 摘掉 ⇒ 两条用例变红 (「面板给的值必须真的落盘」/「回填的 maxResults = 5, want 20」)。 ⚠️ 已知取舍(如实登记):**清空输入框 = 400**(后端按「非数 ⇒ 报错」拒),因此面板上 没有「去掉上限」的手势 —— 要回到 `.env` 兜底得改文件;要支持「清空 = 去掉覆盖」 需另定哨兵语义,未自行发挥。 ⚠️ 契约面已变更(面板声明多一个 number 字段 + saveProvider 的 values 多一个可写键 + 响应多一个回显键)⇒ 主会话登记。
- 保存被包成事务:快照 → 落盘 → 按新配置重建工具 → 原子替换注册表条目(registry.ReplaceTool 窄方法,保留运行期 Policy);下一轮即生效,不要求重启 - 若新配置会让后端下次起不来(主家未配齐 fail-fast)⇒ 文件按字节回滚 + 409 config_unstartable - 声明面补 primary + fallback[](可配可生效);否决「另开 form」方案(会破坏既有 reauth 回填) - 主 provider 解析口径核查结论:没被 PLG-4 改坏(顺手修了一处日志缺陷) - 4 个变异自检(丢 Policy/不换条目/回滚 no-op/忽略 fallback)
- 新增 $DATA_DIR/config/plugins/web-search.json(0600 + temp+rename,文件名 = 插件 id) - 组合根改「先读可变源、缺失回落 .env 播种」;**不新增任何 env 键**(.env 已冻结) - 多实例:每家 provider 独立 key/baseURL/启用态 + 「测试连通」探针 - 用 PLG-1 的声明库给出该面板的 adminPanels 声明;动作名交给 PLG-2 的通用动作端点分发 (本片自己不开路由) ⚠️ 本片自认越界:改了 cmd/cyrene/wire_web_search_test.go(既有测试,为适配 DataDir); 另有 design/28 的相关注册留给主会话。