dsh-vibe-math 1.2.1 → 1.2.3

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-vibe-math",
3
3
  "description": "Multi-agent mathematical problem-solving & verification frameworks for DeepSeek Harness — THREE agent presets in one install: vibe-math-v1 (classic pipeline), vibe-math-v2 (probability-driven: qs.json + Propos knowledge base + explorer→solver→review/debate verdict), and vibe-math-v3 (THIRD-generation, recommended: paper-style Markdown knowledge base with Problems/Progress/Propos/Methods/Verified + planner-agent scheduling that decides the next N actions + universal theory/method invention library + agents write their own Markdown directly via a per-file write lock). Installing this bundle auto-installs all three presets into the DSH preset root.",
4
- "version": "1.2.1",
4
+ "version": "1.2.3",
5
5
  "type": "module",
6
6
  "main": "installer.js",
7
7
  "exports": {
@@ -405,6 +405,7 @@ export function apply(ctx) {
405
405
  function composeProblemMd(p) {
406
406
  const lines = []
407
407
  lines.push('# 问题|' + (p.标题 || p.id))
408
+ lines.push(anchorLine('标题', p.标题 || p.id))
408
409
  lines.push(anchorLine('ID', p.id))
409
410
  lines.push(anchorLine('类型', '问题'))
410
411
  lines.push(anchorLine('状态', p.状态 || '求解中'))
@@ -447,6 +448,7 @@ export function apply(ctx) {
447
448
  function composePropositionMd(p) {
448
449
  const lines = []
449
450
  lines.push('# 命题|' + (p.标题 || p.id))
451
+ lines.push(anchorLine('标题', p.标题 || p.id))
450
452
  lines.push(anchorLine('ID', p.id))
451
453
  lines.push(anchorLine('类型', '命题'))
452
454
  lines.push(anchorLine('状态', p.状态 || '未定论'))
@@ -498,6 +500,7 @@ export function apply(ctx) {
498
500
  function composeMethodMd(m) {
499
501
  const lines = []
500
502
  lines.push('# 方法|' + (m.标题 || m.id))
503
+ lines.push(anchorLine('标题', m.标题 || m.id))
501
504
  lines.push(anchorLine('ID', m.id))
502
505
  lines.push(anchorLine('类型', m.类型 || '方法'))
503
506
  lines.push(anchorLine('状态', m.状态 || '经验'))
@@ -890,32 +893,74 @@ export function apply(ctx) {
890
893
 
891
894
  // ================= prompts =================
892
895
  function personaText(key) { return params[key] ? (String(params[key]) + '\n\n') : '' }
893
- function defaultKnowledgeContext() {
894
- return 'KNOWLEDGE BASE & DATA MODEL (definition contract you MUST follow):\n' +
895
- '\n1) TRUST LAYERS — the single most important rule:\n' +
896
+ function kcTrustLayers() {
897
+ return '\n1) TRUST LAYERS the single most important rule:\n' +
896
898
  '- Verified/ 中的内容 = 绝对可信(已被验证器判定为真/假并生成只读副本):可直接引用。\n' +
897
899
  '- Propos/ 中 状态: 已验证·真/假 的命题 = 可信(以 Verified/ 副本为准)。\n' +
898
900
  '- 其余一切(未定论命题、Progress/ 研究日志、Methods/ 中未验证断言、Notes/)= 经验性记录/参考,绝不能当作已成立事实引用。\n' +
899
- '- 概率语义:1 = 绝对正确(可当已知事实);0 = 绝对错误;0 与 1 之间 = 未定论/待验证。\n' +
900
- '\n2) OBJECT MODELS(md 卡片,软规范:头部锚点行 + 正文自由叙述):\n' +
901
- '- 问题卡 Problems/<id>.md:{ ID, 类型:问题, 状态:原始|求解中|等待依赖|已解决|死路, 优先级, 依赖:[], 被依赖:[], 来源:原始|后生, 计划(由调度器按规划代理的计划自动更新:一句话说明下一轮安排), ## 陈述(完整问题陈述,每个记号/对象都要完整定义), ## 来源与动机(后生问题:产生流程/动机/如何回填主线), ## 解法候选(### 解法 N|标题|概率X|状态Y + 叙述式完整解法)}。\n' +
902
- '- 命题卡 Propos/<分类>/<id>.md:{ ID, 类型:命题, 状态:未定论|已验证·真|已验证·假, 概率, 优先级, 依赖:[], ## 陈述(完整), ## 证明尝试(### 证明 N|…), ## 证伪尝试(### 证伪 N|…)}。\n' +
903
- '- 方法卡 Methods/<id>.md:{ ID, 类型:方法, 状态:经验|应用验证|含已验证断言, 可信断言:[](只允许已进 Verified/ ID), 上级体系/子方法/相关, 适用场景, ## 核心内容, ## 定义与记号, ## 应用记录, ## 改进历史 }。\n' +
904
- '- 收口规则:某个解法/证明/证伪 概率=1 问题已解决 / 命题已验证(状态/概率锚点由调度器改写)。\n' +
905
- '\n3) FOLDERS:Problems/ 问题清单;Progress/ 研究日志(每问题一个 md,按方向按轮续写);Propos/ 命题库;Methods/ 理论发明库;Verified/ 绝对可信(只读);Reliable/ 可信参考文献(只读);Notes/ 自由笔记;Logs/ 审计;State/ 调度器私有——不要读也不要改。\n' +
906
- '\n4) METHOD LIBRARY RULES:开工前先查 Methods/(含全局 VibeMath/Methods/),有可复用方法/体系则引用其 ID;用后必须在 methods_used 上报(含效果与改进建议);本轮新发明/经验性总结必须在 new_inventions 上报(类型:理论体系|框架|工具|方法|思想|范式|技巧)——若与某张已有方法卡同类,在内容描述里注明"可并入 m-xxx"以便 Method Keeper 合并而非重复建卡。**重要区分**:methods_used 只能填**已存在方法卡的 ID**(形如 m-abc12345,来自 AVAILABLE METHODS 列表);你自己刚想出的新方法/新技巧不属于 methods_used,请如实填入 new_inventions(由 Method Keeper 蒸馏建卡);千万不要把方法名/标题文字当 id 填进 methods_used。\n' +
907
- '\n5) OUTPUT REQUIREMENTS:完整性、不断章取义——任何输出的问题/命题/结论都要给出完整陈述并补全所依赖的对象/环境/背景定义;引用必须给出处(文件路径 + ID + 锚点/节),事实只引 Verified/;若结论依赖临时假设 p,必须显式写「若 <p 完整陈述> 成立,则:…」;只输出规定的 JSON(```json 围栏内),JSON 之外不写任何内容。' +
908
- '\n\n6) WRITE-INTO-MD WORKFLOW(推荐,代理自组织直接写 md):框架允许你**直接写 Markdown**,把研究内容落到对应路径的 md 文件里,而不是全部塞进 JSON。做法(你拥有 file 工具):\n' +
909
- '- 每个角色有明确的"归属文件":求解器写该方向的完整叙述到 `Progress/<问题id>/<方向id>.md`;新引理写一张完整命题卡到 `Propos/<分类>/<p-id>.md`(含锚点 `# 命题|标题`、`- ID/状态/概率/优先级` 与 `## 陈述`/`## 证明尝试`);方法整理代理写 `Methods/<m-id>.md`(锚点 `- ID/类型/状态/可信断言/适用场景` + `## 核心内容`/`## 应用记录`/`## 改进历史`)。\n' +
910
- '- **并发写安全**:写任何文件前先 `vibe_math_claim_write({target:"<相对项目根的路径>"})` 申请写锁(同一文件同一时刻只允许一个代理写;若返回 busy 请稍后重试),写完 `vibe_math_release_write({target})`。不同方向是不同文件,天然不冲突。\n' +
911
- '- 写完内容后,用 `vibe_math_sync_meta({meta:{kind:"solver|directions|methods", ...}})` 上报**轻量元数据**(方向状态/存活率/引理 id 及**证明 proof**/方法卡 id/新发明清单),让调度器更新索引与调度——内容留在 md,只有调度元数据与**待验证的证明**才进机读接口(证明是验证必需,必须随 lemmas 上报,否则验证器无法核验)。\n' +
912
- '- 若你所在环境无法写文件(工具不可用/被拒),再回退到"把内容放进下面的 JSON 字段"由调度器落盘。两种方式二选一即可,不要重复。'
913
- }
914
- function knowledgeContextText() { const k = params.knowledgeContext ? String(params.knowledgeContext) : defaultKnowledgeContext(); return k ? ('\n' + k + '\n') : '' }
901
+ '- 概率语义:1 = 绝对正确(可当已知事实);0 = 绝对错误;0 与 1 之间 = 未定论/待验证。\n'
902
+ }
903
+ function kcObjectModels() {
904
+ return '\n2) OBJECT MODELS(md 卡片,软规范:头部锚点行 + 正文自由叙述):\n' +
905
+ '- 问题卡 Problems/<id>.md:{ 标题, ID, 类型:问题, 状态:原始|求解中|等待依赖|已解决|死路, 优先级, 依赖:[], 被依赖:[], 来源:原始|后生, 计划(由调度器按规划代理的计划自动更新:一句话说明下一轮安排), ## 陈述(完整问题陈述,每个记号/对象都要完整定义), ## 来源与动机(后生问题:产生流程/动机/如何回填主线), ## 解法候选(### 解法 N|标题|概率X|状态Y + 叙述式完整解法)}。\n' +
906
+ '- 命题卡 Propos/<分类>/<id>.md:{ 标题, ID, 类型:命题, 状态:未定论|已验证·真|已验证·假, 概率, 优先级, 依赖:[], ## 陈述(完整), ## 证明尝试(### 证明 N|…|概率X|状态Y), ## 证伪尝试(### 证伪 N|…|概率X|状态Y)}。\n' +
907
+ '- 证明/证伪尝试语义:`## 证明尝试`=为证实而写的论证;`## 证伪尝试`=专门反驳/反例的论证。**失败的"找反例未果"/sanity check 是支持性证据,不属于证伪尝试**;不要写入 `## 证伪尝试`(否则系统会当作待验证的反驳去验证)。对仍未完成的证明/证伪,明确标注缺口而非伪装完成。\n' +
908
+ '- 方法卡 Methods/<id>.md:{ 标题, ID, 类型:方法, 状态:经验|应用验证|含已验证断言, 可信断言:[](只允许已进 Verified/ ID), 上级体系/子方法/相关, 适用场景, ## 核心内容, ## 定义与记号, ## 应用记录, ## 改进历史 }。\n' +
909
+ '- 收口规则:某个解法/证明/证伪 概率=1 问题已解决 / 命题已验证(状态/概率锚点由调度器改写)。\n'
910
+ }
911
+ function kcFolders() {
912
+ return '\n3) FOLDERS:Problems/ 问题清单;Progress/ 研究日志(每问题一个聚合索引 <qid>.md + 每方向一个文件 <qid>/<dirId>.md,按方向按轮续写);Propos/ 命题库;Methods/ 理论发明库;Verified/ 绝对可信(只读);Reliable/ 可信参考文献(只读);Notes/ 自由笔记;Logs/ 审计;State/ 调度器私有——不要读也不要改。\n'
913
+ }
914
+ function kcMethodLibrary() {
915
+ return '\n4) METHOD LIBRARY RULES:开工前先查 Methods/(含全局 VibeMath/Methods/),有可复用方法/体系则引用其 ID;用后必须在 methods_used 上报(含效果与改进建议);本轮新发明/经验性总结必须在 new_inventions 上报(类型:理论体系|框架|工具|方法|思想|范式|技巧)——若与某张已有方法卡同类,在内容描述里注明"可并入 m-xxx"以便 Method Keeper 合并而非重复建卡。**重要区分**:methods_used 只能填**已存在方法卡的 ID**(形如 m-abc12345,来自 AVAILABLE METHODS 列表);你自己刚想出的新方法/新技巧不属于 methods_used,请如实填入 new_inventions(由 Method Keeper 蒸馏建卡);千万不要把方法名/标题文字当 id 填进 methods_used。\n'
916
+ }
917
+ function kcOutputQuality() {
918
+ return '\n5) OUTPUT QUALITY RULES:完整性、不断章取义——任何输出的问题/命题/结论都要给出完整陈述并补全所依赖的对象/环境/背景定义;引用必须给出处(文件路径 + ID + 锚点/节),事实只引 Verified/;若结论依赖临时假设 p,必须显式写「若 <p 完整陈述> 成立,则:…」。你的机器回复是一个 JSON 对象(```json 围栏内),JSON 之外不要再输出其他文本——任何要写进 md 的内容都通过文件工具写入,不要当作聊天气泡输出。'
919
+ }
920
+ // 写文件共用规则(仅供真正写 md 的代理:solver / method-keeper):写锁、上报、回退;不含具体归属文件(按角色注入)
921
+ function kcWriteRules() {
922
+ return '\nWRITE-INTO-MD WORKFLOW(优先推荐):把研究内容直接写进你的归属 Markdown 文件,而不是塞进回复 JSON。\n' +
923
+ '- **并发写安全**:写任何文件前先 `vibe_math_claim_write({target:"<相对项目根的路径>"})` 申请写锁(同一文件同一时刻只允许一个代理写;返回 busy 请稍后重试),写完 `vibe_math_release_write({target})`。不同方向是不同文件,天然不冲突。\n' +
924
+ '- **写完必须上报**:用 `vibe_math_sync_meta({meta:{kind:"solver|methods", ...}})` 上报轻量元数据(方向状态/存活率/引理 id+证明/方法卡 id/新发明/解法),让调度器更新索引与调度——内容留在 md,只有调度元数据与**待验证的证明**才进机读接口。\n' +
925
+ '- **分类一致性**:你写引理卡到 `Propos/<分类>/`,sync_meta 里该引理的 `分类` 字段必须严格等于那个目录名(否则调度器会按别处去查,找不到你写的卡)。\n' +
926
+ '- 若你的环境无法真正写文件(文件工具不可用/被拒),回退:把要写的内容放进回复 JSON 的 `__writes` 数组(`[{"path":"<目标>","content":"<全文>"}]`)并同样配 `meta`,由调度器落盘。两种方式二选一,不要重复。\n'
927
+ }
928
+ // 求解器专属:方向叙述 + 引理命题卡(带有完整证明)
929
+ function kcSolverFiles() {
930
+ return '你的归属文件:\n' +
931
+ '- 求解器:把该方向的完整叙述(本轮进展/子路线/可行性信号/教训/完整解法文本)写进 `Progress/<问题id>/<方向id>.md`;聚合索引 `Progress/<问题id>.md` 由调度器维护,不要动它。\n' +
932
+ '- 新引理:写一张完整命题卡到 `Propos/<分类>/<p-id>.md`,含锚点 `- 标题:`、`- ID/类型/状态/概率/优先级` 与 `## 陈述`;证明写进 `### 证明 1|标题|概率X|状态Y` 段落(完整证明文本是验证必需,否则验证器只能验裸命题)。\n'
933
+ }
934
+ // 方法整理代理专属:方法卡
935
+ function kcKeeperFiles() {
936
+ return '你的归属文件:\n' +
937
+ '- 方法整理代理:写 `Methods/<m-id>.md`,含 `- 标题/ID/类型/状态/可信断言/适用场景` 与 `## 核心内容`/`## 应用记录`/`## 改进历史`。\n'
938
+ }
939
+ // 基础核心(所有代理都需:可信层级 / 对象模型 / 文件夹 / 输出质量)
940
+ function coreKnowledgeContext() {
941
+ return 'KNOWLEDGE BASE & DATA MODEL (definition contract you MUST follow):\n' +
942
+ kcTrustLayers() + kcObjectModels() + kcFolders() + kcOutputQuality()
943
+ }
944
+ // 研究类角色(explorer / solver / method-keeper):再加方法库规则;explorer 不写文件所以不带写-文件段
945
+ function researchKnowledgeContext() { return coreKnowledgeContext() + kcMethodLibrary() }
946
+ // 验证器:只返回 Result/Reason,不需要写文件与方法上报规则
947
+ function verifierKnowledgeContext() { return coreKnowledgeContext() }
948
+ function solverKnowledgeContext() { return researchKnowledgeContext() + kcWriteRules() + kcSolverFiles() }
949
+ function methodKeeperKnowledgeContext() { return researchKnowledgeContext() + kcWriteRules() + kcKeeperFiles() }
950
+ function knowledgeContextText(role) {
951
+ const k = params.knowledgeContext ? String(params.knowledgeContext)
952
+ : (role === 'verifier' ? verifierKnowledgeContext()
953
+ : role === 'explorer' ? researchKnowledgeContext()
954
+ : role === 'method-keeper' ? methodKeeperKnowledgeContext()
955
+ : solverKnowledgeContext())
956
+ return k ? ('\n' + k + '\n') : ''
957
+ }
915
958
  function capabilitiesText(role) {
916
- const maxCalls = role === 'solver' ? params.solverMaxToolCalls : params.verifierMaxToolCalls
917
- const netOn = role === 'solver' ? params.solverAllowNetwork : params.verifierAllowNetwork
918
- const scrOn = role === 'solver' ? params.solverAllowScripts : params.verifierAllowScripts
959
+ // explorer 是研究/探索角色,工具预算与 solver 一致(网络/脚本/调用次数),仅 verifier 使用 verifier 配置
960
+ const isSolver = role === 'solver' || role === 'explorer'
961
+ const maxCalls = isSolver ? params.solverMaxToolCalls : params.verifierMaxToolCalls
962
+ const netOn = isSolver ? params.solverAllowNetwork : params.verifierAllowNetwork
963
+ const scrOn = isSolver ? params.solverAllowScripts : params.verifierAllowScripts
919
964
  const toolParts = []
920
965
  if (netOn !== false) toolParts.push('web search / literature lookup')
921
966
  if (scrOn !== false) toolParts.push('symbolic/numeric computation (running scripts)')
@@ -925,9 +970,15 @@ export function apply(ctx) {
925
970
  ? ('- You may use external tools (' + toolParts.join(', ') + ') to assist; ' + ((maxCalls && Number(maxCalls) > 0) ? ('call such external tools AT MOST ' + maxCalls + ' times this round.\n') : 'no per-round limit by default.\n'))
926
971
  : '- External tools: none enabled for you this round.\n'
927
972
  t += '- You may READ any file under Verified/ as a known, trusted dependency.\n'
928
- t += '- You should BASE your reasoning on Propos/ (propositions with proofs/refutations and probabilities), Methods/ (reusable theories/tools), Reliable/ (trusted references), and Verified/.\n'
929
- t += '- You must NOT write files directly: return structured JSON only the scheduler is the single writer (it composes the Markdown knowledge base from your report).\n'
930
- t += '\nHOW TO READ EXISTING KNOWLEDGE: these are Markdown files. COARSE SCAN first: use read/grep on the anchor header lines (- ID/- 状态/- 概率/- 优先级/- 依赖) to locate relevant objects — do NOT load full prose yet. FINE READ after: read the full card for 陈述/证明/证伪/解法/核心内容 sections.\n'
973
+ t += (role === 'verifier'
974
+ ? '- You should BASE your verification on Verified/ and on Propos/ objects already marked 已验证·真/假; verify the TARGET against the rigorous standard, not against Methods/ or unproven claims.\n'
975
+ : '- You should BASE your reasoning on Propos/ (propositions with proofs/refutations and probabilities), Methods/ (reusable theories/tools), Reliable/ (trusted references), and Verified/.\n')
976
+ t += (role === 'verifier'
977
+ ? '- You ONLY return Result/Reason JSON — you do not write files and you do not use the WRITE-INTO-MD workflow.\n'
978
+ : (role === 'explorer'
979
+ ? '- Your output is the direction set (structural metadata): report it via the metadata form (meta.kind=directions); the scheduler writes it into the research log. You do NOT write per-direction files.\n'
980
+ : '- Write your research content directly into your assigned Markdown file (see WRITE-INTO-MD WORKFLOW) and return ONLY lightweight scheduling metadata; if your file tools are unavailable, fall back to the __writes + meta JSON described in the OUTPUT CONTRACT.\n'))
981
+ t += '\nHOW TO READ EXISTING KNOWLEDGE: these are Markdown files. COARSE SCAN first: use read/grep on the anchor header lines (- 标题/- ID/- 状态/- 概率/- 优先级/- 依赖) to locate relevant objects — do NOT load full prose yet. FINE READ after: read the full card for 陈述/证明/证伪/解法/核心内容 sections.\n'
931
982
  return t
932
983
  }
933
984
  function methodsIndexText() {
@@ -940,14 +991,14 @@ export function apply(ctx) {
940
991
  }
941
992
  function explorerPrompt(q) {
942
993
  return personaText('explorerPersona') + 'You are a research mathematician orchestrating strategy for one problem.\n\nPROBLEM (id: ' + q.id + '): ' + q.陈述 + '\n' +
943
- knowledgeContextText() +
994
+ knowledgeContextText('explorer') +
944
995
  methodsIndexText() +
945
- capabilitiesText('solver') +
946
- '\nDo a first-stage METACOGNITIVE BRAINSTORM: decompose constraints, test boundary/extreme cases, map to similar known problems. First check the AVAILABLE METHODS list — if a listed method/system applies, plan to use it (you will reference its id in methods_used). ' +
996
+ capabilitiesText('explorer') +
997
+ '\nDo a first-stage METACOGNITIVE BRAINSTORM: decompose constraints, test boundary/extreme cases, map to similar known problems. First check the AVAILABLE METHODS list — if a listed method/system underlies a direction you will propose, reference its id in methods_used (this is a REFERENCE, not an application — you are only suggesting the direction builds on it, not claiming you used it). ' +
947
998
  'Then propose 3-6 DIVERSE, mutually distinct solution directions (e.g. analytic method, constructive proof, contradiction, numeric approximation + limit passage, categorical abstraction, ...). ' +
948
999
  'Record each direction with its core assumption and an initial feasibility estimate. Every direction must be self-contained: title / method / core_assumption written completely, defining every object they mention — no 断章取义.\n\n' +
949
- 'feasibility ∈ [0,1]. Respond with ONLY a single JSON object wrapped in a ```json code fence no prose:\n' +
950
- '{"directions":[{"id":"d1","title":"...","method":"...","core_assumption":"...","feasibility":0.5}],"methods_used":[{"id":"m-...","效果":"...","建议":"..."}],"new_inventions":[{"类型":"方法|工具|...","标题":"...","内容描述":"...","是否已入库":false}]}'
1000
+ 'feasibility ∈ [0,1] = your estimate of the probability this direction leads to a full solution. Respond with ONLY a single JSON object in a ```json code fence (no prose outside it). Register the directions as metadata; the scheduler writes them into the research log:\n' +
1001
+ '{"meta":{"kind":"directions","qid":"<qid>","directions":[{"id":"d1","title":"...","method":"...","core_assumption":"...","feasibility":0.5}],"methods_used":[{"id":"m-...","效果":"<为何该方向借鉴它>","建议":"..."}],"new_inventions":[{"类型":"方法|工具|...","标题":"...","内容描述":"...","是否已入库":false}]}}'
951
1002
  }
952
1003
  function rederivePrompt(q, prog) {
953
1004
  const prior = prog.map(function (d) {
@@ -955,13 +1006,13 @@ export function apply(ctx) {
955
1006
  (d.routes && d.routes.length ? ' | routes: ' + d.routes.map(function (r) { return r.title + '[' + (r.feasibility_signal || '') + ']' }).join('; ') : '')
956
1007
  }).join('\n')
957
1008
  return personaText('explorerPersona') + 'You are a research mathematician re-deriving strategy for a problem whose prior directions stalled or failed.\n\nPROBLEM (id: ' + q.id + '): ' + q.陈述 + '\n\nPRIOR DIRECTIONS (with blockers):\n' + prior + '\n' +
958
- knowledgeContextText() +
1009
+ knowledgeContextText('explorer') +
959
1010
  methodsIndexText() +
960
- capabilitiesText('solver') +
1011
+ capabilitiesText('explorer') +
961
1012
  '\nQuantitatively analyze the historical progress, blocker causes, and feasibility decay of each prior direction. Discard directions already proven dead ends (unless a new tool/idea changes that). ' +
962
1013
  'Then deeply DERIVE 1-3 BRAND-NEW directions never tried before, each with a one-line motivation. Return the UNION of high-potential leftover directions and the brand-new directions (drop dead ends).\n\n' +
963
- 'Respond with ONLY a single JSON object wrapped in a ```json code fence no prose:\n' +
964
- '{"directions":[{"id":"d1","title":"...","method":"...","core_assumption":"...","feasibility":0.5,"motivation":"..."}],"methods_used":[...],"new_inventions":[...]}'
1014
+ 'feasibility ∈ [0,1]. Respond with ONLY a single JSON object in a ```json code fence (no prose outside it). Register the directions as metadata; the scheduler writes them into the research log:\n' +
1015
+ '{"meta":{"kind":"directions","qid":"<qid>","directions":[{"id":"d1","title":"...","method":"...","core_assumption":"...","feasibility":0.5}],"methods_used":[{"id":"m-...","效果":"...","建议":"..."}],"new_inventions":[{"类型":"方法|工具|...","标题":"...","内容描述":"...","是否已入库":false}]}}'
965
1016
  }
966
1017
  function directionSummary(d) {
967
1018
  return 'id ' + d.id + '「' + d.title + '」method=' + d.method + ' | round=' + d.round + ' status=' + d.status +
@@ -985,7 +1036,7 @@ export function apply(ctx) {
985
1036
  let head = personaText('solverPersona') + 'You are a dedicated solver agent working ONE solution direction of a math problem (agent_self_iteration).\n\n'
986
1037
  head += 'PROBLEM (id: ' + q.id + '): ' + q.陈述 + '\nDIRECTION: ' + dir.title + ' (method: ' + dir.method + '; core assumption: ' + dir.core_assumption + ')\nROUND: ' + round + ' of ' + params.solverMaxRounds + '\n'
987
1038
  if (round > 1 || (progressText && progressText.length)) head += '\nYOUR PRIOR PROGRESS / OTHER DIRECTIONS:\n' + progressText + '\n'
988
- head += knowledgeContextText()
1039
+ head += knowledgeContextText('solver')
989
1040
  head += methodsIndexText()
990
1041
  head += capabilitiesText('solver')
991
1042
  head += '\nStart from the last recorded node of direction ' + dir.id + ' (inherit progress, or branch a sub-route under it). Consult AVAILABLE METHODS first — reuse a listed method/system when it fits (report it in methods_used). Each round you MUST produce, even if incomplete:\n' +
@@ -995,10 +1046,15 @@ export function apply(ctx) {
995
1046
  '- an updated survival probability for this direction;\n' +
996
1047
  '- ANY new theory/tool/method/idea you invented or summarized this round in new_inventions (类型:理论体系|框架|工具|方法|思想|范式|技巧) — the Method Keeper will distill it into the theory library.'
997
1048
  head += '\nIf you encounter an EXTREMELY complex auxiliary conjecture/sub-problem q_sub: list it in "sub_questions" as a PROBLEM-class object with its COMPLETE statement (every object/definition/notation fully defined — 不断章取义), together with p_{q-tmp}: a PROPOSITION-class TEMPORARY ASSUMPTION answering q_sub. TEMPORARILY ASSUME p_{q-tmp} holds and continue the main line — every later proposition/conclusion depending on it MUST be stated as "若 <p_{q-tmp} 的完整陈述> 成立,则:..." (complete definitions).\n'
998
- head += '\nIMPORTANT — PROBABILITY RULES FOR NEW RESULTS: any 概率 / solution_probability / survival_probability you output for NEW results must be strictly BETWEEN 0 and 1 (they await independent verifier confirmation). NEVER mark your own fresh lemma or solution as 1 or 0 — that is the verifiers\' job. Only facts already recorded in Verified/ count as certain.\n'
999
- head += '\nIf you obtain a COMPLETE solution: adversarially self-check (construct counterexamples, test boundary conditions) BEFORE declaring success; put the full solution text in "solution".\n'
1000
- head += '\nRespond with ONLY a single JSON object wrapped in a ```json code fenceno prose:\n' +
1001
- '{"status":"continue|success|dead-end","solution":"complete solution text, or null","solution_probability":0.85,"lemmas":[{"title":"...","statement":"...","proof":"...","细类型":{"分析":{}},"布尔估计":0.6,"价值/关键性":0.5,"优先级":1}],"routes":[{"title":"...","progress":"...","feasibility_signal":"...","blocker":"..."}],"lessons":["..."],"survival_probability":0.5,"dead_end_reason":"... or null","sub_questions":[{"q_sub_title":"...","q_sub_statement":"完整问题陈述(含所有对象/定义)","assumption_title":"p_{q-tmp} 标题","assumption_statement":"完整假设陈述(含所有定义)"}],"methods_used":[{"id":"<已有方法卡的ID,形如 m-abc12345,必须是 AVAILABLE METHODS 中出现的 id>","效果":"...","建议":"..."}],"new_inventions":[{"类型":"...","标题":"...","内容描述":"...","是否已入库":false}]}\n' +
1049
+ head += '\nIMPORTANT — PROBABILITY RULES FOR NEW RESULTS: any 概率 / prob / solution_prob / survival you output for NEW results must be strictly BETWEEN 0 and 1 (they await independent verifier confirmation). NEVER mark your own fresh lemma or solution as 1 or 0 — that is the verifiers\' job. Only facts already recorded in Verified/ count as certain.\n'
1050
+ head += '\nIf you obtain a COMPLETE solution: adversarially self-check (construct counterexamples, test boundary conditions) BEFORE declaring success; write the full solution prose into your direction Progress file and put the solution into the `solution_text` field of the meta.\n'
1051
+ head += '\nSTATUS SEMANTICS report the truth, do not hedge: `success` = you produced a complete, self-consistent solution; `dead-end` = the direction is MATHEMATICALLY dead (a decisive blocker / a core sub-assumption refuted / a step proven impossible); `continue` = still viable and you made real progress this round. Do NOT use `dead-end` merely because you ran out of time capping rounds is the controller\'s decision (solverMaxRounds), not yours; if you progressed but didn\'t finish, report `continue` with the new survival.\n'
1052
+ head += '\nLEMMA RULES: every lemma you register MUST carry a complete proof in `lemmas[].proof` (and in the card\'s `## 证明尝试`). If a claim is only partly argued, do NOT register it as a finished lemma — either prove it fully or record it as an explicit gap/conjecture stating the missing step, so the verifier knows exactly what is (and is not) being claimed. Incomplete "lemmas" waste verification and can mislead.\n'
1053
+ head += '\nOUTPUT CONTRACT — pick ONE channel. Write content into Markdown; only lightweight scheduling metadata (and verification-required proofs) cross the machine reply.\n' +
1054
+ 'CHANNEL A (recommended, you can write files): write the full round narrative into `Progress/' + q.id + '/' + dir.id + '.md` and each new lemma card into `Propos/<分类>/<id>.md`, then reply ONLY this metadata object:\n' +
1055
+ '{"meta":{"kind":"solver","qid":"' + q.id + '","dirId":"' + dir.id + '","round":' + round + ',"survival":0.5,"status":"continue|success|dead-end","dead_end_reason":"... or null","lemmas":[{"id":"p-...","title":"...","statement":"...","proof":"<完整证明文本,供验证器核验>","prob":0.6,"分类":"<引理卡目录名,必须与你要写入的 Propos/<分类>/ 目录严格一致>","优先级":1}],"methods_used":[{"id":"m-...","效果":"...","建议":"..."}],"new_inventions":[{"类型":"...","标题":"...","内容描述":"...","是否已入库":false}],"solution_prob":0.85,"solution_text":"<完整解法文本,或 null>","sub_questions":[{"q_sub_title":"...","q_sub_statement":"完整问题陈述(含所有对象/定义)","assumption_title":"p_{q-tmp} 标题","assumption_statement":"完整假设陈述(含所有定义)"}]}}\n' +
1056
+ 'CHANNEL B (your file tools are unavailable): put the content you would have written into __writes and carry the same meta:\n' +
1057
+ '{"__writes":[{"path":"Progress/' + q.id + '/' + dir.id + '.md","content":"<完整本轮叙述>"}],"meta":{"kind":"solver","qid":"' + q.id + '","dirId":"' + dir.id + '",...同上 meta 字段...}}\n' +
1002
1058
  '区分规则:methods_used 只能填**已存在的方法卡 ID**(m-…,来自 AVAILABLE METHODS 列表)——引用你自己刚想出的新方法/新技巧不属于 methods_used,请如实填入 new_inventions(它会由 Method Keeper 蒸馏建卡);不要把方法名/标题当 id 填进 methods_used。'
1003
1059
  return head
1004
1060
  }
@@ -1009,22 +1065,25 @@ export function apply(ctx) {
1009
1065
  }
1010
1066
  function verifierReviewPrompt(r) {
1011
1067
  return personaText('verifierPersona') + 'You are a STRICT peer reviewer verifying one mathematical object. Check it multiple times.\n\nTARGET (r: ' + r.kind + '):\n' + verifierTargetText(r) + '\n' +
1012
- knowledgeContextText() +
1068
+ knowledgeContextText('verifier') +
1013
1069
  capabilitiesText('verifier') +
1014
1070
  '\nResult ∈ [0,1] = your probability that the TARGET is CORRECT: 1 ONLY when you are fully certain (for a bare proposition: Reason must be a complete proof; for a proof/refutation/solution: you verified every step and Reason confirms the whole chain); 0 ONLY when you are certain it is wrong (Reason must be a rigorous complete refutation / pinpoint the fatal flaw); otherwise a value strictly between 0 and 1.\n' +
1071
+ '\nCalibration: 0.5 means "genuinely undecided — there is a real unresolved gap"; it is NOT a safe hedge, so do not default to 0.5. Give the number your honest confidence from the evidence actually supports.\n' +
1072
+ '\n**Reason is MANDATORY and MUST be non-empty**: name the exact step you verified, or the potential counterexample / fatal flaw, or (for 0.5) the precise gap that blocks a decision. A Result with an empty Reason is non-contributory and will be ignored; never return {"Result":0.5} with no justification.\n' +
1015
1073
  '\nCitations: facts may only be cited from Verified/ (or Propos/ 状态: 已验证·真/假). Never cite an unverified or refuted object as a fact — if you need a sub-claim of a refuted card, re-derive it yourself.\n' +
1016
- '\nIndependently output your initial review. Respond with ONLY a single JSON object wrapped in a ```json code fence no prose:\n' +
1017
- '{"Result":0.5,"Reason":"detailed logic chain, potential counterexample, or supporting evidence"}'
1074
+ '\nIndependently output your initial review ONLY a single JSON object in a ```json code fence, no prose outside it:\n' +
1075
+ '{"Result":0.5,"Reason":"<MANDATORY, non-empty: your detailed logic chain / potential counterexample / supporting evidence>"}'
1018
1076
  }
1019
1077
  function verifierDebatePrompt(r, transcript) {
1020
1078
  return personaText('verifierPersona') + 'You are one reviewer in a DEBATE ("交流群") about this object.\n\nTARGET:\n' + verifierTargetText(r) + '\n' +
1021
- knowledgeContextText() +
1079
+ knowledgeContextText('verifier') +
1022
1080
  capabilitiesText('verifier') +
1023
1081
  '\nFULL DEBATE HISTORY SO FAR (每轮所有评审轮流发言的记录):\n' + transcript + '\n' +
1024
1082
  '\nRespond to the others (agree / rebut / add new evidence, referencing earlier rounds if needed). If you changed your Result because of them, state the reason explicitly. ' +
1025
- 'Remember: formal/notation-level flaws in an otherwise correct proof should lower confidence only slightly — a mathematically correct argument is not "uncertain" because of typos; near-consensus is not a deadlock.\n' +
1026
- 'Respond with ONLY a single JSON object wrapped in a ```json code fence no prose:\n' +
1027
- '{"Result":0.5,"Reason":"updated logic chain / counterexample / proof / refutation","changed":"brief reason if you changed your Result, else null"}'
1083
+ 'Remember: formal/notation-level flaws in an otherwise correct proof should lower confidence only slightly — a mathematically correct argument is not "uncertain" because of typos; near-consensus is not a deadlock. Undue swing to 0.5 is discouraged: a bare review merits 0.5 ONLY if there is a genuine undecidable gap, never as a hedge.\n' +
1084
+ '\nReason is MANDATORY and MUST be non-empty; an empty-Reason result (esp. a bare 0.5) is ignored as non-contributory, so always justify your number.\n' +
1085
+ '\nReply with ONLY a single JSON object in a ```json code fence, no prose outside it:\n' +
1086
+ '{"Result":0.5,"Reason":"<MANDATORY, non-empty: updated logic chain / counterexample / proof / refutation>","changed":"brief reason if you changed your Result, else null"}'
1028
1087
  }
1029
1088
  function plannerPrompt(brief) {
1030
1089
  return personaText('plannerPersona') + 'You are the SCHEDULING PLANNER of a multi-agent mathematical research system. Your job: autonomously choose the OPTIMAL schedule — you may lay out the NEXT ' + params.planningHorizon + ' agent-task calls in one plan (they will be executed in order, beyond-capacity ones queued for later ticks).\n\n' +
@@ -1037,17 +1096,20 @@ export function apply(ctx) {
1037
1096
  '- {"action":"interrupt","childId":"<childId>","reason":"..."} — stop a running child (direction dead, superseded...).\n' +
1038
1097
  '- {"action":"promote","target":"<pId>","reason":"..."} — high-value unresolved proposition → judge problem.\n' +
1039
1098
  '- {"action":"wait","target":"<id>","reason":"..."} — advisory: wait for a dependency.\n' +
1040
- '\nHARD RULES: never re-schedule verified objects; problems with 依赖未就绪 (依赖就绪=false) should wait unless you explicitly accept a temporary assumption; respect capacity (brief.free_slots); PREFER problems whose dependencies are ready and whose directions have the highest survival; DO NOT forget verification — unresolved solutions/proofs/refutations (verify_candidates) will never be checked unless you schedule a verifier; schedule at most ' + params.planningHorizon + ' actions.\n' +
1099
+ '\nHARD RULES: never re-schedule verified objects; problems with 依赖未就绪 (依赖就绪=false) should wait unless you explicitly accept a temporary assumption; respect capacity (brief.free_slots); PREFER problems whose dependencies are ready and whose directions have the highest survival; DO NOT forget verification — unresolved solutions/proofs/refutations (verify_candidates) will never be checked unless you schedule a verifier; DO NOT assume a direction is already being worked just because it is shown "active" in a problem — check brief.problems[].running_solver_dirs and brief.active_agents: schedule a solver for a direction ONLY if that direction is NOT in running_solver_dirs (an "active" direction absent from running_solver_dirs is WAITING to be dispatched, not being worked); schedule at most ' + params.planningHorizon + ' actions.\n' +
1041
1100
  'Respond with ONLY a single JSON object wrapped in a ```json code fence — no prose:\n' +
1042
1101
  '{"summary":"one-line plan rationale","plan":[{"action":"...","role":"...","target":"...","direction":"...","childId":"...","reason":"..."}]}'
1043
1102
  }
1044
1103
  function methodKeeperPrompt(digest) {
1045
1104
  return personaText('methodKeeperPersona') + 'You are the METHOD KEEPER of a mathematical research system. Your job: distill reusable THEORIES, FRAMEWORKS, TOOLS, METHODS, IDEAS (including experiential ones) invented during solving into the theory library, so future work can apply and extend them — like inventing group theory while solving an equation, or functional analysis while studying variational problems.\n\n' +
1046
- knowledgeContextText() +
1105
+ knowledgeContextText('method-keeper') +
1047
1106
  '\nRECENT WORK DIGEST:\n' + digest + '\n\n' +
1048
1107
  'For each pending invention decide: create a NEW method card, or fold it into an EXISTING method (as an improvement). Only list 可信断言 for claims already verified (ids from Verified/) — everything else stays 经验 (experiential). You may propose 上级体系/子方法 links to organize methods into systems.\n' +
1049
- 'Respond with ONLY a single JSON object wrapped in a ```json code fence no prose:\n' +
1050
- '{"new_methods":[{"标题":"...","类型":"理论体系|框架|工具|方法|思想|范式|技巧","核心内容":"...","定义与记号":"...","适用场景":"...","上级体系":[],"子方法":[],"可信断言":[],"来源":"从哪些工作提炼"}],"improvements":[{"id":"m-...","改进内容":"...","原因":"..."}]}'
1108
+ 'OUTPUT CONTRACT pick ONE channel. Write method cards into Markdown; only the created IDs, which cards were used, and improvements cross the machine reply.\n' +
1109
+ 'CHANNEL A (recommended, you can write files): write each method card into `Methods/<m-id>.md` (`# 方法|标题` + `- 标题/ID/类型/状态/可信断言/适用场景` + `## 核心内容`/`## 应用记录`/`## 改进历史`), then reply ONLY this metadata:\n' +
1110
+ '{"meta":{"kind":"methods","used":[{"id":"m-...","效果":"...","建议":"..."}],"created":["m-xxx"],"improvements":[{"id":"m-...","改进内容":"...","原因":"..."}]}}\n' +
1111
+ 'CHANNEL B (your file tools are unavailable): put the method-card content into __writes and carry the same meta:\n' +
1112
+ '{"__writes":[{"path":"Methods/<m-id>.md","content":"<# 方法|标题 + 锚点 + ## 核心内容... 完整卡面>"}],"meta":{"kind":"methods","used":[...],"created":["m-xxx"],"improvements":[...]}}'
1051
1113
  }
1052
1114
 
1053
1115
  // ================= decisions (manual/auto) =================
@@ -1323,6 +1385,7 @@ export function apply(ctx) {
1323
1385
  id: q.id, 状态: q.状态, 优先级: q.优先级, 依赖: q.依赖, 依赖就绪: problemDepReady(q),
1324
1386
  方向数: dirs.length,
1325
1387
  活跃方向: dirs.filter(function (d) { return d.status === 'active' }).map(function (d) { return d.id }),
1388
+ running_solver_dirs: dirs.filter(function (d) { return d.status === 'active' && Object.keys(agentRegistry).some(function (cid) { const m = agentRegistry[cid]; return m && m.qid === q.id && m.direction === d.id && m.role === 'solver' }) }).map(function (d) { return d.id }),
1326
1389
  最高存活率: dirs.length ? Math.max.apply(null, dirs.map(function (d) { return Number(d.survival) || 0 })) : null,
1327
1390
  解法数: (q.solutions || []).length,
1328
1391
  })
@@ -1437,6 +1500,7 @@ export function apply(ctx) {
1437
1500
  if (!q || q.状态 === '已解决' || q.优先级 === 'never' || q.状态 === '等待依赖') continue
1438
1501
  const dir = (getDirState(q.id) || []).find(function (d) { return d.id === String(a.direction || '') })
1439
1502
  if (!dir || dir.status !== 'active') continue
1503
+ if ((dir.round || 0) >= (Number(params.solverMaxRounds) || 3)) continue // 已到轮次上限,不再调度(由后续 re-derive/stall 处理)
1440
1504
  const running = Object.keys(agentRegistry).some(function (cid) { const m = agentRegistry[cid]; return m && m.qid === q.id && m.direction === dir.id && m.role === 'solver' })
1441
1505
  if (running) continue
1442
1506
  } else if (role === 'verifier') {
@@ -1504,9 +1568,10 @@ export function apply(ctx) {
1504
1568
  if (!q) return
1505
1569
  const dir = (getDirState(q.id) || []).find(function (d) { return d.id === a.direction })
1506
1570
  if (!dir) return
1507
- const progressText = buildSolverContext(getDirState(q.id), dir, 1, params.directionsPerSolver)
1508
- await spawnChild('solver:' + q.id + ':' + dir.id, solverPrompt(q, dir, 1, progressText), { role: 'solver', qid: q.id, direction: dir.id, round: 1, description: q.陈述 })
1509
- logActivity('solver', 'problem ' + q.id + ' direction ' + dir.id + ' solver spawned (plan)')
1571
+ const nextRound = (dir.round || 0) + 1 // 续轮上限由 syncMeta 的 solver 分支约束(见下)
1572
+ const progressText = buildSolverContext(getDirState(q.id), dir, nextRound, params.directionsPerSolver)
1573
+ await spawnChild('solver:' + q.id + ':' + dir.id, solverPrompt(q, dir, nextRound, progressText), { role: 'solver', qid: q.id, direction: dir.id, round: nextRound, description: q.陈述 })
1574
+ logActivity('solver', 'problem ' + q.id + ' direction ' + dir.id + ' solver spawned (plan, round ' + nextRound + ')')
1510
1575
  } else if (a.role === 'verifier') {
1511
1576
  const cands = await buildVerifyCandidates()
1512
1577
  const c = cands.find(function (x) { return x.rId === a.target })
@@ -1591,9 +1656,11 @@ export function apply(ctx) {
1591
1656
  if (dir.status === 'success' || dir.status === 'dead-end') continue
1592
1657
  const running = Object.keys(agentRegistry).some(function (cid) { const m = agentRegistry[cid]; return m && m.qid === q.id && m.direction === dir.id && m.role === 'solver' })
1593
1658
  if (running) continue
1594
- const progressText = buildSolverContext(dirs, dir, 1, params.directionsPerSolver)
1595
- const promptText = solverPrompt(q, dir, 1, progressText)
1596
- const r = await maybeGate('spawn', 'solver for problem ' + q.id + ' direction ' + dir.id, { label: 'solver:' + q.id + ':' + dir.id, promptText: promptText, meta: { role: 'solver', qid: q.id, direction: dir.id, round: 1, description: q.陈述 } }, async function (d) { await spawnChild(d.label, d.promptText, d.meta); return { spawned: true } })
1659
+ if ((dir.round || 0) >= (Number(params.solverMaxRounds) || 3)) { dir.status = 'dead-end'; dir.dead_end_reason = dir.dead_end_reason || '迭代轮限到达(solverMaxRounds=' + params.solverMaxRounds + ')'; await saveDirState(); await writeJournal(q.id); logActivity('solver', 'problem ' + q.id + ' direction ' + dir.id + ' dead-end (round cap reached in heuristic)'); continue }
1660
+ const nextRound = (dir.round || 0) + 1
1661
+ const progressText = buildSolverContext(dirs, dir, nextRound, params.directionsPerSolver)
1662
+ const promptText = solverPrompt(q, dir, nextRound, progressText)
1663
+ const r = await maybeGate('spawn', 'solver for problem ' + q.id + ' direction ' + dir.id, { label: 'solver:' + q.id + ':' + dir.id, promptText: promptText, meta: { role: 'solver', qid: q.id, direction: dir.id, round: nextRound, description: q.陈述 } }, async function (d) { await spawnChild(d.label, d.promptText, d.meta); return { spawned: true } })
1597
1664
  if (r && r.gated) return
1598
1665
  }
1599
1666
  }
@@ -1781,10 +1848,14 @@ export function apply(ctx) {
1781
1848
  continue
1782
1849
  }
1783
1850
  if (methods.has(mu.id)) {
1784
- m.applications = m.applications || []
1785
- m.applications.push({ at: fmtTime(), 问题: ctx.qid || '', 方向: ctx.dirId || '', text: (mu.效果 || '') + (mu.建议 ? ';建议:' + mu.建议 : '') })
1786
- await saveMethod(m, false)
1787
- logActivity('method', 'application record appended to ' + mu.id + (ctx.qid ? ' (问题 ' + ctx.qid + (ctx.dirId ? ' 方向 ' + ctx.dirId : '') + ')' : ''))
1851
+ // 只记录"有实际问题/方向上下文"的引用;method-keeper 纯整理时的引用(qid/dirId 皆空)不当作应用,
1852
+ // 避免把"整理时引用该方法"误记为"实际应用",从而污染应用计数并触发错误的全局晋升。
1853
+ if (ctx.dirId || ctx.qid) {
1854
+ m.applications = m.applications || []
1855
+ m.applications.push({ at: fmtTime(), 问题: ctx.qid || '', 方向: ctx.dirId || '', text: (mu.效果 || '') + (mu.建议 ? ';建议:' + mu.建议 : '') })
1856
+ await saveMethod(m, false)
1857
+ logActivity('method', 'application record appended to ' + mu.id + ' (问题 ' + ctx.qid + ' 方向 ' + ctx.dirId + ')')
1858
+ }
1788
1859
  }
1789
1860
  }
1790
1861
  }
@@ -1840,7 +1911,8 @@ export function apply(ctx) {
1840
1911
  if (parsed && ((Array.isArray(parsed.__writes) && parsed.__writes.length) || (parsed.meta && parsed.meta.kind === 'methods'))) {
1841
1912
  await applyAgentWrites(parsed.__writes)
1842
1913
  if (parsed.meta && parsed.meta.kind === 'methods') await syncMeta(parsed.meta, { id: childId })
1843
- if (parsed.meta && Array.isArray(parsed.meta.created) && parsed.meta.created.length > 0) methodLog.pendingInventions = []
1914
+ // 消费已沉淀的发明:新建方法卡或对已有方法的改进都视为已处理本轮 pending(与旧 JSON 路径一致,防 improvements-only 反复触发)
1915
+ if (parsed.meta && ((Array.isArray(parsed.meta.created) && parsed.meta.created.length > 0) || (Array.isArray(parsed.meta.improvements) && parsed.meta.improvements.length > 0))) methodLog.pendingInventions = []
1844
1916
  await saveAll(); return
1845
1917
  }
1846
1918
  if (!parsed) { logActivity('method', 'method keeper returned nothing usable'); await saveAll(); return }
@@ -2200,7 +2272,10 @@ export function apply(ctx) {
2200
2272
  }
2201
2273
  async function checkTermination() {
2202
2274
  const unsolved = allProblems().filter(function (q) { return !(q.状态 === '已解决' || q.优先级 === 'never') })
2203
- if (unsolved.length === 0 && Object.keys(agentRegistry).length === 0 && Object.keys(tasks).length === 0 && planQueue.length === 0) {
2275
+ // 终止前必须无遗留验证对象 / 待沉淀发明:否则会在命题/解法仍未验证、发明尚未蒸馏时提前停机,
2276
+ // 导致"仅剩验证候选 / 仅剩发明"的工作永远不会被执行(验证/方法库是独立于问题求解的收尾工作)。
2277
+ const leftoverVerify = (await buildVerifyCandidates()).length > 0 || methodLog.pendingInventions.length > 0
2278
+ if (unsolved.length === 0 && !leftoverVerify && Object.keys(agentRegistry).length === 0 && Object.keys(tasks).length === 0 && planQueue.length === 0) {
2204
2279
  scheduler.running = false
2205
2280
  await releaseProjectLock()
2206
2281
  logActivity('stop', 'all active problems solved (never-priority excluded) and no active agents/tasks/plans — scheduler stopped (strict termination)')
@@ -2317,7 +2392,7 @@ export function apply(ctx) {
2317
2392
  if (oldDirs && oldDirs.length > 0) await archiveDirections(qid, oldDirs)
2318
2393
  const list = (meta.directions || []).map(function (d) {
2319
2394
  const old = (getDirState(qid) || []).find(function (x) { return x.id === d.id })
2320
- return { id: d.id || ('d_' + shortId()), title: d.title || '', method: d.method || old?.method || '', core_assumption: d.core_assumption || old?.core_assumption || '', feasibility: clamp01(d.feasibility != null ? d.feasibility : (old ? old.survival : 0.5)), status: 'active', round: old ? old.round : 0, survival: clamp01(d.survival != null ? d.survival : (old ? old.survival : 0.5)), routes: old?.routes || [], lessons: old?.lessons || [], blockers: old?.blockers || [], lemmas: old?.lemmas || [], journal: old?.journal || [], dead_end_reason: '' }
2395
+ return { id: d.id || ('d_' + shortId()), title: d.title || '', method: d.method || old?.method || '', core_assumption: d.core_assumption || old?.core_assumption || '', feasibility: clamp01(d.feasibility != null ? d.feasibility : (old ? old.survival : 0.5)), status: 'active', round: old ? old.round : 0, survival: clamp01(d.survival != null ? d.survival : (d.feasibility != null ? d.feasibility : (old ? old.survival : 0.5))), routes: old?.routes || [], lessons: old?.lessons || [], blockers: old?.blockers || [], lemmas: old?.lemmas || [], journal: old?.journal || [], dead_end_reason: '' }
2321
2396
  })
2322
2397
  dirState.set(qid, list)
2323
2398
  await saveDirState(); await writeJournal(qid)
@@ -2337,6 +2412,11 @@ export function apply(ctx) {
2337
2412
  if (meta.status) dir.status = String(meta.status)
2338
2413
  if (meta.dead_end_reason) dir.dead_end_reason = String(meta.dead_end_reason)
2339
2414
  if (meta.round) dir.round = Number(meta.round)
2415
+ // 轮次上限:达到 solverMaxRounds 且仍未成功/死路 → 强制死路(新协议路径没有 followup 自迭代,必须靠此收口,与旧路径一致)
2416
+ if ((dir.round || 0) >= (Number(params.solverMaxRounds) || 3) && dir.status !== 'success' && dir.status !== 'dead-end') {
2417
+ dir.status = 'dead-end'
2418
+ dir.dead_end_reason = dir.dead_end_reason || ('迭代轮限到达(solverMaxRounds=' + params.solverMaxRounds + ')')
2419
+ }
2340
2420
  // 引理注册(id 由代理在命题卡里自定)
2341
2421
  if (Array.isArray(meta.lemmas)) {
2342
2422
  for (const l of meta.lemmas) {
@@ -2360,6 +2440,14 @@ export function apply(ctx) {
2360
2440
  const p = clamp01(meta.solution_prob)
2361
2441
  q.solutions.push({ title: '解法 ' + (q.solutions.length + 1), prob: p >= 1 ? 0.99 : (p <= 0 ? 0.01 : p), status: '未定论', text: String(meta.solution_text).slice(0, 2000) })
2362
2442
  }
2443
+ // 子问题/临时假设(与旧 JSON 路径一致):注册 q_sub 问题 + 判断问题 + p-tmp 假设
2444
+ if (Array.isArray(meta.sub_questions)) {
2445
+ for (const sq of meta.sub_questions) {
2446
+ if (!sq || !sq.q_sub_statement) continue
2447
+ const rec = await addSubQuestion(qid, dirId, sq)
2448
+ if (rec) { dir.sub_questions = dir.sub_questions || []; dir.sub_questions.push(rec) }
2449
+ }
2450
+ }
2363
2451
  await saveProblem(q); await saveDirState(); await writeJournal(qid)
2364
2452
  await consumeMethodFeedback(meta, { qid: qid, dirId: dirId })
2365
2453
  logActivity('solver', qid + '/' + dirId + ' meta sync (status=' + (meta.status || '') + ', survival=' + dir.survival + ')')
@@ -2377,6 +2465,18 @@ export function apply(ctx) {
2377
2465
  if ((await readText('Methods/' + mid + '.md')) === undefined) await saveMethod(mm, false)
2378
2466
  }
2379
2467
  }
2468
+ // 改进:把内容写进已有方法卡的 ## 改进历史(与旧 JSON 路径一致)
2469
+ if (Array.isArray(meta.improvements)) {
2470
+ for (const imp of meta.improvements) {
2471
+ if (!imp || !imp.id) continue
2472
+ const m = methods.get(imp.id)
2473
+ if (!m) { logActivity('method', 'improvement referenced unknown method ' + imp.id); continue }
2474
+ m.improvements = m.improvements || []
2475
+ m.improvements.push({ v: m.improvements.length + 1, 原因: imp.原因 || '', text: imp.改进内容 || '' })
2476
+ await saveMethod(m, false)
2477
+ logActivity('method', 'method ' + imp.id + ' improved (v' + m.improvements.length + ')')
2478
+ }
2479
+ }
2380
2480
  await saveAll()
2381
2481
  return { ok: true }
2382
2482
  }
@@ -2472,7 +2572,7 @@ export function apply(ctx) {
2472
2572
  registerTool('vibe_math_lock_status', 'Show the project lock occupancy.', objParams({}), 'vibe_math_lock_status')
2473
2573
  registerTool('vibe_math_claim_write', 'Acquire the write lock for one target file (relative to the project root). Call before writing a Markdown file directly.', objParams({ target: { type: 'string' } }, ['target']), 'vibe_math_claim_write')
2474
2574
  registerTool('vibe_math_release_write', 'Release the write lock for one target file (relative to the project root).', objParams({ target: { type: 'string' } }, ['target']), 'vibe_math_release_write')
2475
- registerTool('vibe_math_sync_meta', 'Report lightweight scheduling metadata after writing content to Markdown files.', objParams({ meta: { type: 'object' } }, ['meta']), 'vibe_math_sync_meta')
2575
+ registerTool('vibe_math_sync_meta', 'After you write content into Markdown files, report ONLY lightweight scheduling metadata to keep the scheduler state in sync (content stays in the md files). meta.kind must be one of:\n- "directions": {qid, directions:[{id,title,method,core_assumption,feasibility}], methods_used:[{id,效果,建议}], new_inventions:[{类型,标题,内容描述,是否已入库}]}\n- "solver": {qid, dirId, round, survival, status:"continue|success|dead-end", dead_end_reason, lemmas:[{id,title,statement,proof,prob,分类,优先级}], methods_used, new_inventions, solution_prob, solution_text, sub_questions:[{q_sub_title,q_sub_statement,assumption_title,assumption_statement}]}\n- "methods": {used:[{id,效果,建议}], created:[ids], improvements:[{id,改进内容,原因}]}', objParams({ meta: { type: 'object' } }, ['meta']), 'vibe_math_sync_meta')
2476
2576
 
2477
2577
  // /vibe slash command (registered once; routed per session)
2478
2578
  ctx.effect(() => commands.register({
@@ -24,7 +24,7 @@ V3 是在 v1(经典流水线)、v2(概率驱动 + JSON 数据层)基础
24
24
 
25
25
  | 维度 | v1(经典流水线) | v2(概率驱动) | v3(论文式自组织) |
26
26
  |---|---|---|---|
27
- | 知识媒介 | JSON(csv/字段) | JSON(qs.json / Propos / Verified 卡) | **md(调度器是唯一物理写者;子代理的叙述以 JSON 报告交调度器落盘为论文式 md——代理"写论文",调度器"执笔")** |
27
+ | 知识媒介 | JSON(csv/字段) | JSON(qs.json / Propos / Verified 卡) | **md(子代理直接写各自归属的 md:方向日志、引理命题卡、方法卡——写前用 `vibe_math_claim_write` 申请写锁;`vibe_math_sync_meta` 只上报轻量调度元数据;调度器负责聚合索引与锚点回写)** |
28
28
  | 书写风格 | 结构化字段 | 结构化字段 | **论文/研究报告式叙述,自组织** |
29
29
  | 问题来源记录 | 无 | progress 字段零散记录 | **问题卡"来源与动机"章节 + 依赖图(后生问题自带产生原因与计划)** |
30
30
  | 方法论沉淀 | 无 | 无 | **Methods/ 通用理论发明库 + Method Keeper 沉淀循环** |
@@ -81,13 +81,13 @@ flowchart TB
81
81
  SCHED -->|"④"| VERI
82
82
  SCHED -->|"④ 定期安排"| KEEP
83
83
  SCHED -->|"④ 可选"| REP
84
- EXPL -->|"方向集(JSON 摘要)"| SCHED
85
- SOLV -->|"轮次结果 + 新发明上报(JSON 摘要)"| SCHED
84
+ EXPL -->|"方向集(meta.directions)"| SCHED
85
+ SOLV -->|"轻元数据 + 新发明(meta)"| SCHED
86
86
  VERI -->|"审查/辩论结果(JSON)"| SCHED
87
- SCHED -->|"⑤ 代理续写/追加 md(写所有权内)"| PROG
87
+ SCHED -->|"⑤ 聚合索引 + 锚点回写"| PROG
88
88
  SCHED -->|"⑤ 注册后生问题/命题"| PROB
89
89
  SCHED -->|"⑤"| PROP
90
- KEEP -->|"⑥ 沉淀/完善方法"| METH
90
+ KEEP -->|"⑥ 沉淀/完善方法(写 Methods/*.md)"| METH
91
91
  SCHED -->|"⑦ 裁决回写锚点 + 生成"| VERF
92
92
  SCHED -->|"⑧ 记录计划与结果"| LOGS
93
93
  SCHED <-->|"读写状态(唯一写者)"| STATE
@@ -99,7 +99,7 @@ flowchart TB
99
99
  - **②** 若有可推进工作(待解问题方向、**可验证对象**、待沉淀发明)且并发有空闲 → 构建**规划简报**,调用**规划代理**一次(仅有验证候选而无待解问题时同样触发,保证命题/解法不会因"无问题可解"而永不验证);
100
100
  - **③** 规划代理返回**计划**(最多 `planningHorizon` 步动作的 JSON);manual 模式下先挂**计划审批门**;审批/入队后调度器把每步动作的 reason 回写到问题卡的「计划」锚点(`- 计划: 一句话说明下一轮安排`);
101
101
  - **④** 调度器逐条**校验**(硬约束)并**执行**:派发/续轮/中断 Explorer、Solver、Verifier,定期安排 Method Keeper 与汇报代理;超出当前并发的动作进入待执行队列跨 tick 消费;
102
- - **⑤** 子代理以 JSON 返回结构化摘要(方向集、轮次结果、审查报告、新发明上报),**调度器是唯一物理写者**——在**写所有权**内把子代理的叙述原文/摘要落盘为对应 md(研究日志续写、问题卡/命题卡更新、方法卡应用记录追加);子代理的"论文式书写"体现在其 JSON 报告中的叙述文本原样进入 md,而非子代理直接碰文件;
102
+ - **⑤** 子代理把研究内容**直接写进各自归属的 md**(求解器→`Progress/<问题id>/<方向id>.md`、引理→`Propos/<分类>/<p-id>.md`、新方法→`Methods/<m-id>.md`),写前用 `vibe_math_claim_write` 申请写锁、写后用 `vibe_math_release_write` 释放(同一文件同一时刻只允许一个代理写,避免并发冲突),再用 `vibe_math_sync_meta` 上报**轻量调度元数据**(方向状态/存活率/引理 id+证明/方法卡 id/新发明/解法;若所在环境无法真正写文件则回退为回复 JSON `__writes` 数组由调度器兜底落盘);调度器据此更新索引与调度,并负责聚合索引 `Progress/<问题id>.md`、状态/概率锚点回写与 `Verified/` 副本生成。
103
103
  - **⑥** Method Keeper 提炼近期工作,沉淀新方法条目、合并碎片、完善体系结构;
104
104
  - **⑦** 验证裁决由调度器回写锚点(状态/概率)、生成 `Verified/` 副本、更新索引;
105
105
  - **⑧** 计划 + 逐条执行结果写入 `Logs/Plans/`,作为下一次规划的"上次计划经验"(规划学习闭环)。