dsh-vibe-math 1.3.2 → 1.3.4

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/README.md CHANGED
@@ -49,6 +49,10 @@
49
49
  > 说明:V4 去掉 v3 的中央规划器与确定性角色(explorer/solver/verifier/planner/method-keeper),把"研究者"本身作为主体。详见 `vibe-math-v4/实现方案.md`。
50
50
  >
51
51
  > 🔧 **v1.3.2 审计修复**:修复上下文压缩按占比失效(`contextPct` 单位错配,现按百分比保存)、常驻工具按"调用者身份"路由(各常驻库归属正确)、`vibe_v4_read_progress` 真正返回文本、跨进程断点续跑重建常驻、`message(all)` 广播真正投递、`addMember` 增开无 id 碰撞。详见 `vibe-math-v4/实现方案.md` §17。
52
+ >
53
+ > 🔧 **v1.3.3 审计修复**:非全票验证把平均概率写回源卡(兑现"留库附概率")、方法型验证标 `类型: 方法`(不再误标命题)、同进程 abort→resume 重建常驻、记录命题也触发自动同步会议、唤醒信号改用 `activityTimeoutMs`、`verdictMaxRounds`/`meetingKeepEvery` 可调且展示。详见 §18。
54
+ >
55
+ > 🔧 **v1.3.4 和谐修复**:重排 `框架图-v4.png` 消除文字/箭头重叠遮挡(子标签不再溢出框、`/compact` 移入独立的"上下文"能力块、底部"产物沉淀·断点续跑"居中排布);补齐 `agent.cordis.yml` 常驻描述中的"deadlines"一词(v4 并无截止机制),并移除 `实现方案.md` 中不存在的 `vibe_v4_inject`(实际用 `vibe_v4_message`)。
52
56
 
53
57
  ---
54
58
 
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 — FOUR 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), 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), and vibe-math-v4 (FOURTH-generation: persistent self-organizing resident subagents that message & meet to decide all tasks, verify only by unanimous consensus, /compact at a context threshold, and stop only when all agree the problem is solved). Installing this bundle auto-installs all four presets into the DSH preset root.",
4
- "version": "1.3.2",
4
+ "version": "1.3.4",
5
5
  "type": "module",
6
6
  "main": "installer.js",
7
7
  "exports": {
@@ -32,7 +32,7 @@
32
32
  Data lives under {{cwd}}/VibeMath/Projects/<project>/:
33
33
  Problems/ (original problem card), Progress/<resident>/ (each resident's progress),
34
34
  Propos/<resident>/ (each resident's propositions), Methods/<resident>/ (theories/tools),
35
- Subproblems/<resident>/, Shared/ (meeting transcripts / task board / deadlines / debates),
35
+ Subproblems/<resident>/, Shared/ (meeting transcripts / task board / debates),
36
36
  Verified/ (read-only, ONLY after unanimous consensus), State/ (framework-private),
37
37
  Reliable/ (references), Notes/.
38
38
 
@@ -86,10 +86,10 @@ export function apply(ctx) {
86
86
  await writeJson('State/mailboxes.json', Object.fromEntries(mailboxes))
87
87
  await writeJson('State/taskboard.json', taskboard)
88
88
  await writeJson('State/decisions.json', decisions)
89
- await writeJson('State/session.json', {running,autoDone,phase,problemId,problemText,runId,meetings,reports,lastActivityAt,activityLog,processEpoch})
89
+ await writeJson('State/session.json', {running,autoDone,phase,problemId,problemText,runId,meetings,reports,lastActivityAt,activityLog,processEpoch,artifactCount})
90
90
  }
91
91
  async function loadAll(){
92
- const s=await readJson('State/session.json'); if(s){ running=!!s.running; autoDone=!!s.autoDone; phase=s.phase||'idle'; problemId=s.problemId||problemId; problemText=s.problemText||problemText; runId=s.runId||runId; meetings=s.meetings||[]; reports=s.reports||[]; lastActivityAt=s.lastActivityAt||now(); activityLog=s.activityLog||activityLog; persistedEpoch=s.processEpoch||'' }
92
+ const s=await readJson('State/session.json'); if(s){ running=!!s.running; autoDone=!!s.autoDone; phase=s.phase||'idle'; problemId=s.problemId||problemId; problemText=s.problemText||problemText; runId=s.runId||runId; meetings=s.meetings||[]; reports=s.reports||[]; lastActivityAt=s.lastActivityAt||now(); activityLog=s.activityLog||activityLog; persistedEpoch=s.processEpoch||''; artifactCount=s.artifactCount||0 }
93
93
  const rm=await readJson('State/residents.json'); if(rm&&typeof rm==='object') residents=new Map(Object.entries(rm))
94
94
  const mb=await readJson('State/mailboxes.json'); if(mb&&typeof mb==='object') mailboxes=new Map(Object.entries(mb))
95
95
  const tb=await readJson('State/taskboard.json'); if(Array.isArray(tb)) taskboard=tb
@@ -149,7 +149,7 @@ export function apply(ctx) {
149
149
  let residentSeq = 0
150
150
  function newResident(dir){ const rId='r-'+(++residentSeq); return {rId,childId:'',direction:dir||'',status:'brainstorm',rounds:0,roundsSinceCompact:0,lastActiveAt:now(),insight:'',contextPct:0,contextSeed:'',needCompact:false} }
151
151
  async function spawnResident(r){
152
- const started=await subagents.startContinuable({provider:pickProvider(),label:r.rId,request:{prompt:[textBlock(brainstormPrompt(r))],parent:rootAgent,agentOptions:{}},signal:makeSignal(60000)})
152
+ const started=await subagents.startContinuable({provider:pickProvider(),label:r.rId,request:{prompt:[textBlock(brainstormPrompt(r))],parent:rootAgent,agentOptions:{}},signal:makeSignal(params.activityTimeoutMs||60000)})
153
153
  r.childId=started.childId; r.status='brainstorm'; r.lastActiveAt=now()
154
154
  childOwner.set(started.childId,sessionId); busy.add(r.rId); wakeKind.set(r.rId,'normal'); currentResident=r.rId
155
155
  residents.set(r.rId,r); await saveAll(); logActivity('spawn',r.rId+' ('+(r.direction||'brainstorm')+')')
@@ -168,14 +168,14 @@ export function apply(ctx) {
168
168
  'Set "contextPct": 15 (your post-compact usage) and "compacted": true in the reply so the framework records the condensed seed.]\n\n' + promptText
169
169
  r.needCompact = true
170
170
  }
171
- try { await subagents.followup(rootAgent,r.childId,[textBlock(prompt)],{source:{kind:'user'},signal:makeSignal(60000)}); return true }
171
+ try { await subagents.followup(rootAgent,r.childId,[textBlock(prompt)],{source:{kind:'user'},signal:makeSignal(params.activityTimeoutMs||60000)}); return true }
172
172
  catch(e){ console.error('vibe-v4 wake '+r.rId+' failed: '+String((e&&e.message)||e)); busy.delete(r.rId); return false }
173
173
  }
174
174
  function byChild(childId){ for(const [,r] of residents){ if(r.childId===childId) return r } return undefined }
175
175
 
176
176
  // ---- artifact writers (resident-facing) ----
177
177
  async function publishProgress(rId,content){ const rel='Progress/'+rId+'/progress.md'; const prev=(await readText(rel))||''; await writeText(rel, prev+'\n### '+fmtTime()+'|'+rId+'\n'+String(content||'')+'\n'); return {ok:true} }
178
- async function recordProposition(rId,o){ const id=o.id||('p-'+shortId()); const lines=['# 命题|'+(o.title||id),'- 标题: '+(o.title||id),'- ID: '+id,'- 类型: 命题','- 状态: 未定论','- 概率: '+cl(o.prob!=null?o.prob:0.5),'- 价值程度: '+cl(o.value!=null?o.value:0.5),'- 动机用途计划: '+(o.motivation||''),'- 依赖: []','','## 陈述',String(o.statement||''),'','## 证明尝试','','## 证伪尝试','']; await writeText('Propos/'+rId+'/'+id+'.md',lines.join('\n')); logActivity('record',rId+' 命题 '+id); return {ok:true,id,file:'Propos/'+rId+'/'+id+'.md'} }
178
+ async function recordProposition(rId,o){ const id=o.id||('p-'+shortId()); const lines=['# 命题|'+(o.title||id),'- 标题: '+(o.title||id),'- ID: '+id,'- 类型: 命题','- 状态: 未定论','- 概率: '+cl(o.prob!=null?o.prob:0.5),'- 价值程度: '+cl(o.value!=null?o.value:0.5),'- 动机用途计划: '+(o.motivation||''),'- 依赖: []','','## 陈述',String(o.statement||''),'','## 证明尝试','','## 证伪尝试','']; await writeText('Propos/'+rId+'/'+id+'.md',lines.join('\n')); logActivity('record',rId+' 命题 '+id); bumpArtifacts(); return {ok:true,id,file:'Propos/'+rId+'/'+id+'.md'} }
179
179
  async function recordMethod(rId,o){ const id=o.id||('m-'+shortId()); const lines=['# 方法|'+(o.title||id),'- 标题: '+(o.title||id),'- ID: '+id,'- 类型: '+(o.type||'方法'),'- 状态: 经验','- 可信断言: []','- 价值程度: '+cl(o.value!=null?o.value:0.5),'- 动机用途计划: '+(o.motivation||''),'','## 核心内容',String(o.content||''),'','## 定义与记号',String(o.notation||''),'','## 应用记录','## 改进历史','']; await writeText('Methods/'+rId+'/'+id+'.md',lines.join('\n')); logActivity('record',rId+' 方法 '+id); bumpArtifacts(); return {ok:true,id,file:'Methods/'+rId+'/'+id+'.md'} }
180
180
  async function recordSubproblem(rId,o){ const id=o.id||('s-'+shortId()); const lines=['# 子问题|'+(o.title||id),'- 标题: '+(o.title||id),'- ID: '+id,'- 状态: 求解中','- 价值程度: '+cl(o.value!=null?o.value:0.5),'- 动机用途计划: '+(o.motivation||''),'- 依赖: []','','## 陈述',String(o.statement||''),'','## 进度','']; await writeText('Subproblems/'+rId+'/'+id+'.md',lines.join('\n')); logActivity('record',rId+' 子问题 '+id); bumpArtifacts(); return {ok:true,id,file:'Subproblems/'+rId+'/'+id+'.md'} }
181
181
  // auto-sync meeting: every meetingKeepEvery new artifacts, convene a general coordination meeting
@@ -272,7 +272,7 @@ export function apply(ctx) {
272
272
  if(allTrue||allFalse){ await closeVerify(vs,allTrue); return }
273
273
  if(vs.round+1<params.verdictMaxRounds){ vs.stage='debate'; vs.round+=1; vs.asked=[]; logActivity('verify',vs.targetId+' round '+vs.round+' → debate'); await saveAll(); await scheduleNext(); return }
274
274
  const avg=vals.length? vals.reduce((a,x)=>a+(x.verdict==='TRUE'?x.confidence:x.verdict==='FALSE'?1-x.confidence:0.5),0)/vals.length : 0.5
275
- await writeDebateDoc(vs,false,avg); logActivity('verify',vs.targetId+' NOT unanimous → kept unverified (avg '+avg.toFixed(2)+')')
275
+ await writeDebateDoc(vs,false,avg); await rewriteSourceProb(vs.targetId, avg); logActivity('verify',vs.targetId+' NOT unanimous → kept unverified (avg '+avg.toFixed(2)+')')
276
276
  verifyState=null; wakeKind.clear(); await saveAll(); await scheduleNext()
277
277
  }
278
278
  async function closeVerify(vs,isTrue){
@@ -289,12 +289,13 @@ export function apply(ctx) {
289
289
  await writeText('Shared/debates/'+vs.targetId+'.md', lines.join('\n'))
290
290
  }
291
291
  async function writeVerifiedCard(vs,isTrue){
292
- const dir= vs.targetType==='subproblem'?'问题':'命题'
293
- const text='# 已验证|'+vs.targetId+'\n- ID: '+vs.targetId+'\n- 类型: '+(vs.targetType==='subproblem'?'问题':'命题')+'\n- 结论: '+(isTrue?'真':'假')+'\n- 概率: '+(isTrue?1:0)+'\n- 来源: 全体常驻一致\n## 陈述\n参见来源卡。\n'
292
+ const isSub=vs.targetType==='subproblem'
293
+ const dir= isSub?'问题':'命题'
294
+ const type= isSub?'问题': vs.targetType==='method'?'方法':'命题'
295
+ const text='# 已验证|'+vs.targetId+'\n- ID: '+vs.targetId+'\n- 类型: '+type+'\n- 结论: '+(isTrue?'真':'假')+'\n- 概率: '+(isTrue?1:0)+'\n- 来源: 全体常驻一致\n## 陈述\n参见来源卡。\n'
294
296
  await writeText('Verified/'+dir+'/'+vs.targetId+'.md', text)
295
297
  }
296
- async function rewriteSource(target,isTrue){
297
- // find & update the source card status/prob; best effort across per-resident libs
298
+ async function findSourceRel(target){
298
299
  let rel=null
299
300
  for(const [rid] of residents){
300
301
  for(const base of ['Propos','Methods','Subproblems']){
@@ -302,7 +303,19 @@ export function apply(ctx) {
302
303
  }
303
304
  if(rel) break
304
305
  }
305
- if(!rel) rel='Propos/'+target+'.md'
306
+ return rel || ('Propos/'+target+'.md')
307
+ }
308
+ // non-unanimous verification: keep the object in its library but write back the
309
+ // average probability (design §8: "留库附概率"), so the card reflects the consensus estimate.
310
+ async function rewriteSourceProb(target,prob){
311
+ const rel=await findSourceRel(target)
312
+ let text=(await readText(rel))||''
313
+ text=text.replace(/(^|\n)- 概率:.*/m,'$1- 概率: '+Number(prob).toFixed(2))
314
+ await writeText(rel,text)
315
+ }
316
+ async function rewriteSource(target,isTrue){
317
+ // find & update the source card status/prob; best effort across per-resident libs
318
+ const rel=await findSourceRel(target)
306
319
  let text=(await readText(rel))||''
307
320
  text=text.replace(/(^|\n)- 状态:.*/m,'$1'+(isTrue?'- 状态: 已验证·真':'- 状态: 已验证·假'))
308
321
  .replace(/(^|\n)- 概率:.*/m,'$1'+(isTrue?'- 概率: 1':'- 概率: 0'))
@@ -385,14 +398,14 @@ export function apply(ctx) {
385
398
  if(residentCount) params.residentCount=Number(residentCount)||4
386
399
  running=true; autoDone=false; phase='brainstorm'
387
400
  await writeText('Problems/'+problemId+'.md','# 问题|'+problemId+'\n- ID: '+problemId+'\n- 类型: 问题\n- 状态: 求解中\n- 优先级: 1\n- 依赖: []\n\n## 陈述\n'+problemText+'\n')
388
- residents=new Map(); mailboxes=new Map(); taskboard=[]; decisions=[]; meetings=[]; reports=[]; verifyState=null; meetingState=null; pendingVerify=null; residentSeq=0
401
+ residents=new Map(); mailboxes=new Map(); taskboard=[]; decisions=[]; meetings=[]; reports=[]; verifyState=null; meetingState=null; pendingVerify=null; residentSeq=0; artifactCount=0
389
402
  const dirs=Array.isArray(seedDirections)?seedDirections.slice(0,params.residentCount):[]
390
403
  for(let i=0;i<params.residentCount;i++){ const r=newResident(dirs[i]||''); await spawnResident(r) }
391
404
  await saveAll(); return {ok:true,message:'v4 started: '+params.residentCount+' resident(s) brainstorming',project:currentProject}
392
405
  }
393
406
  async function resume(){
394
407
  currentProject=await readCurrentProject(); await ensureDirs(); await loadAll()
395
- if(phase==='idle' && !running) return {ok:false,message:'nothing to resume'}
408
+ if(phase==='idle' && !running && residents.size===0) return {ok:false,message:'nothing to resume'}
396
409
  // If the persisted State came from a DIFFERENT process (crash/restart), the saved
397
410
  // childIds are stale; clear them so residents re-spawn (their libraries persist on
398
411
  // disk and re-seed the resumed run). Same-process pause→resume keeps continuable ids.
@@ -405,7 +418,7 @@ export function apply(ctx) {
405
418
  }
406
419
  function status(){ return { ok:true, running, phase, autoDone, project:currentProject, residentCount:residents.size,
407
420
  residents:listResidents(), busy:[...busy], taskboard:taskboard.length,
408
- params:['residentCount','compactAfterRounds','compactThreshold','maxParallel','activityTimeoutMs'].map(k=>k+'='+params[k]).join(', ') } }
421
+ params:['residentCount','compactAfterRounds','compactThreshold','maxParallel','activityTimeoutMs','meetingKeepEvery','verdictMaxRounds'].map(k=>k+'='+params[k]).join(', ') } }
409
422
  function report(){ return { ok:true, running, phase, autoDone, project:currentProject, problem:problemText,
410
423
  residents:listResidents(), taskboard:taskboard.filter(t=>t.status!=='done'),
411
424
  verify: verifyState?{target:verifyState.targetId,stage:verifyState.stage}:null, meetings:meetings.length,
@@ -413,7 +426,7 @@ export function apply(ctx) {
413
426
  async function addMember(direction){ const r=newResident(direction||''); await spawnResident(r); return {ok:true,id:r.rId,direction:r.direction} }
414
427
  async function removeMember(id){ const r=residents.get(id); if(!r) return {ok:false}; if(r.childId){ try{ subagents.interrupt(r.childId,{kind:'ancestor',agent:rootAgent}) }catch(e){} } residents.delete(id); busy.delete(id); mailboxes.delete(id); await saveAll(); return {ok:true} }
415
428
  function setParams(upd){ for(const k of Object.keys(upd||{})){ if(k in params) params[k]=upd[k] } return {ok:true} }
416
- function initAbort(){ running=false; phase='idle'; autoDone=false; for(const [,r] of residents){ if(r.childId){ try{ subagents.interrupt(r.childId,{kind:'ancestor',agent:rootAgent}) }catch(e){} } }; return {ok:true,message:'aborted'} }
429
+ async function initAbort(){ running=false; phase='idle'; autoDone=false; for(const [,r] of residents){ if(r.childId){ try{ subagents.interrupt(r.childId,{kind:'ancestor',agent:rootAgent}) }catch(e){} } r.childId=''; r.lastActiveAt=0; r.roundsSinceCompact=0 } await saveAll(); return {ok:true,message:'aborted'} }
417
430
  function setPause(){ running=false; return {ok:true,message:'paused'} }
418
431
 
419
432
  return {
@@ -454,7 +467,7 @@ export function apply(ctx) {
454
467
  registerTool('vibe_v4_list_members','List residents.',objParams({}),(s)=>({ok:true,residents:s.listResidents()}))
455
468
  registerTool('vibe_v4_add_member','Add a resident.',objParams({direction:{type:'string'}}),(s,a)=>s.addMember(a.direction))
456
469
  registerTool('vibe_v4_remove_member','Close a resident.',objParams({id:{type:'string'}},['id']),(s,a)=>s.removeMember(a.id))
457
- registerTool('vibe_v4_set','Set V4 parameters.',objParams({residentCount:{type:'integer'},compactAfterRounds:{type:'integer'},compactThreshold:{type:'integer'},meetingKeepEvery:{type:'integer'},maxParallel:{type:'integer'},activityTimeoutMs:{type:'integer'}}),(s,a)=>{ s.setParams(a); return {ok:true} })
470
+ registerTool('vibe_v4_set','Set V4 parameters.',objParams({residentCount:{type:'integer'},compactAfterRounds:{type:'integer'},compactThreshold:{type:'integer'},meetingKeepEvery:{type:'integer'},maxParallel:{type:'integer'},activityTimeoutMs:{type:'integer'},verdictMaxRounds:{type:'integer'}}),(s,a)=>{ s.setParams(a); return {ok:true} })
458
471
  // resident-facing tools: route to the CALLING resident (exec.agent.id === childId);
459
472
  // fall back to the last-woken resident when called by the host/assistant.
460
473
  registerTool('vibe_v4_send_message','(resident) Send a message to another resident.',objParams({to:{type:'string'},content:{type:'string'}},['to','content']),(s,a,x)=>s.postMessage(s.residentIdOf(x),a.to,a.content))
@@ -484,7 +497,7 @@ export function apply(ctx) {
484
497
  if(cmd==='start') r=await s.start({problem:rest.join(' ')})
485
498
  else if(cmd==='resume') r=await s.resume()
486
499
  else if(cmd==='pause') r=s.setPause()
487
- else if(cmd==='abort') r=s.initAbort()
500
+ else if(cmd==='abort') r=await s.initAbort()
488
501
  else if(cmd==='status') r=s.status()
489
502
  else if(cmd==='report') r=s.report()
490
503
  else if(cmd==='meeting') r=await s.startMeeting(rest.join(' '))
@@ -124,7 +124,7 @@ VibeMath/Projects/<project>/
124
124
  | **共识验证** | `vibe_v4_propose_verify(targetId)` → 常驻们同意后开辩论 | 独立初评→公开辩论→**全票真/假才入 Verified/**;否则留库附概率 |
125
125
  | **停止条件** | 会议中全体常驻对"原问题已解决"投票,**全部同意** → 停止唤醒 | 人工 `abort` 始终可用 |
126
126
  | **上下文/compact** | 监测每常驻上下文占比 ≥ 阈值 → 触发 DSH `/compact` | `compactThreshold` 默认 66,可调 |
127
- | **人工/助手干预** | `vibe_v4_message(all, content)` / `vibe_v4_meeting` / `vibe_v4_add_member` / `remove_member` / `vibe_v4_inject`;`/v4` slash | 不改变"之后由它们自组织" |
127
+ | **人工/助手干预** | `vibe_v4_message(all, content)` / `vibe_v4_meeting` / `vibe_v4_add_member` / `remove_member` / `vibe_v4_message(to, content)`;`/v4` slash | 不改变"之后由它们自组织" |
128
128
 
129
129
  ---
130
130
 
@@ -272,7 +272,7 @@ VibeMath/Projects/<project>/
272
272
  - **P3 会议 + 任务板 + 决策**:`meeting`/taskboard/decisions;分工涌现。
273
273
  - **P4 产物沉淀**:`record_proposition/method/subproblem` + `publish_progress`(价值/动机/概率必填校验)。
274
274
  - **P5 共识验证**:`propose_verify` + 独立初评 + 公开辩论 + 全票闭环;`Verified/` 生成与来源库回写。
275
- - **P6 停止 + 干预 + 恢复**:stop-vote(全票 solved)/abort/pause/resume;`add_member/remove_member/inject/message(all)`。
275
+ - **P6 停止 + 干预 + 恢复**:stop-vote(全票 solved)/abort/pause/resume;`add_member/remove_member/message(all)`。
276
276
  - **P7 测试**:`selfdrive-v4.mjs`(常驻互相留言/开会/沉淀/共识验证/compact/停止 的全流程记录器);E2E 断言:消息送达、会议记录、仅全票入 Verified、未全票留库附概率、compact 触发、仅全体一致才 stop、resume。
277
277
 
278
278
  ---
@@ -356,3 +356,22 @@ VibeMath/Projects/<project>/
356
356
 
357
357
  > **未改(保留为已知边界)**:`maxParallel`/`activityTimeoutMs` 目前为声明参数但未实际限流/门控(保持"常驻持续推进"的收敛行为,未做心跳超时门控以免阻塞自组织推进);`claim_write`/`release_write` 仍为占位(常驻专属目录内天然无写冲突,共享文件由框架独占写);真实 DSH `/compact` API 仍为 TODO(等效层已修复单位错配后可正常触发)。
358
358
 
359
+ ---
360
+
361
+ ## 18. 第二轮深度审计修复(v1.3.3,自驱动 21/21 + 独立修复测试 8/8)
362
+
363
+ 针对 v1.3.2 之后仍存在的缺陷做第二轮审计并修复:
364
+
365
+ | 缺陷 | 说明 | 修复 |
366
+ |---|---|---|
367
+ | **非全票验证不写回平均概率** | `finalizeVerify` 算出 `avg` 只写进辩论录,源卡 `- 概率:` 保持原值,未兑现设计 §8"留库附概率"。 | 新增 `findSourceRel`/`rewriteSourceProb`,非全票时把 `avg` 写回源卡 `- 概率:`(状态仍 `未定论`)。 |
368
+ | **方法型验证被误标为"命题"** | `writeVerifiedCard` 把方法目标写进 `Verified/命题/` 且类型=命题。 | 按 `targetType` 标 `类型: 方法`(子问题→问题,方法→方法,其余→命题),来源卡标 `已验证·真/假`。 |
369
+ | **同进程 abort→resume 不重建常驻** | `processEpoch` 进程级,同进程 abort(interrupt 杀掉常驻)后 resume 判非跨进程→保留死 childId;且 `phase=idle && !running` 时 resume 直接拒绝。 | `initAbort` 清空 `childId` 并 `saveAll()`;`resume` 守卫改为 `phase==='idle' && !running && residents.size===0`(有常驻即可 重建)。 |
370
+ | **自动同步会议计数不一致** | `bumpArtifacts` 只在记录方法/子问题时调用,`recordProposition`/`publish_progress` 不计;`artifactCount` 未持久化。 | `recordProposition` 也 `bumpArtifacts()`;`artifactCount` 持久化到 `session.json` 并在 `start()` 时清零。 |
371
+ | **唤醒信号硬编码 60s** | `spawnResident`/`wakeResident` 用 `makeSignal(60000)`,比 `activityTimeoutMs`(120s) 短。 | 改用 `makeSignal(params.activityTimeoutMs||60000)`。 |
372
+ | **`verdictMaxRounds`/`meetingKeepEvery` 不可调/不展示** | `verdictMaxRounds` 不在 `vibe_v4_set` schema;两者都不在 `status()` 参数串。 | `vibe_v4_set` 增加 `verdictMaxRounds`;`status()` 参数串补 `meetingKeepEvery` 与 `verdictMaxRounds`。 |
373
+
374
+ > 测试:`selfdrive-v4.mjs` 21/21(新增 B1 参数可见性断言);新增 `e2e-v4-fixes.test.mjs` 8/8(每项修复用独立 mock 宿主驱动:A1 非全票写回、A2 方法标 `类型: 方法`、A3 同进程 abort→resume 重建、B2 记录命题自动开会)。
375
+
376
+ > **已知边界(未改)**:`maxParallel`/`activityTimeoutMs` 仍未实际限流/心跳门控(避免破坏"持续推进→收敛");`claim_write`/`release_write` 仍未落地为真锁;真实 DSH `/compact` API 为 TODO。
377
+