dsh-math-modeling-agent 0.2.7 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -108,7 +108,7 @@ D3 方向/D4 裁决),`run-state.mjs gate` 在每次状态转移前强制校
108
108
  安装:
109
109
 
110
110
  ```bash
111
- dsh plugin --profile web add github:yohanchen1/MathModelingAgent#v0.2.7
111
+ dsh plugin --profile web add github:yohanchen1/MathModelingAgent#v0.3.1
112
112
  dsh --profile web --dump-config # 检查组合层(应看到 dsh-math-modeling-agent-skills 行)
113
113
  dsh web # 重启以加载插件
114
114
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-math-modeling-agent",
3
- "version": "0.2.7",
3
+ "version": "0.3.1",
4
4
  "description": "Evidence-driven mathematical modeling and verification skills for DeepSeek Harness",
5
5
  "type": "module",
6
6
  "files": [
@@ -22,18 +22,18 @@ Return a concise current status, supported claims, unresolved obligations, attem
22
22
  ## Default workflow
23
23
 
24
24
  1. Route the task to Fast, Standard, or High-Assurance using `references/problem-types.md`; ask D1 (task type + assurance mode).
25
- 2. Read available inputs before asking questions; apply the per-subproblem math grill in `references/math-grill.md`; show the D0 restatement and intake digest in the chat.
26
- 3. Create or validate run state with `scripts/run-state.mjs` and follow `references/state-recovery.md`. Gate EVERY transition: `run-state.mjs gate <dir> --to <status> --reason "..."`.
27
- 4. Profile inputs and build the subproblem DAG using `references/data-subproblems.md`.
28
- 5. Register assumptions (per subproblem), claims, and verification obligations from `references/claims-evidence.md`; ask D2 for the subproblem being started.
29
- 6. **Literature research (mandatory, before candidates)**: transition CLAIMS_REGISTERED → RESEARCH, search principles and methods by direction (never contest solutions), write `research/sources.jsonl`, attach a literature column to the candidate matrix, then ask D-R (user endorses/rejects/adds references) and move to CANDIDATES_READY.
30
- 7. Build a baseline and materially different candidates using `references/modeling-methodology.md`; ask D3 (candidate matrix).
31
- 8. Probe tools with `scripts/capability-probe.mjs`; create a run-local Python environment with `scripts/python-environment.mjs` only when computation is required. Announce the tool choice and fallback for the phase (references/tool-policy.md).
32
- 9. Execute, verify, critique, and revise according to `references/workflow.md` and `references/tool-policy.md`; ask D4 after each VERIFY.
25
+ 2. Literature research FIRST: transition TRIAGE RESEARCH, search principles and methods by direction (never contest solutions), write `research/sources.jsonl` + methods matrix, ask D-R (user endorses/rejects/adds).
26
+ 3. Show the D0 deep restatement six sections plus the question-dependency relation and ask D0; gate SCOPE_FROZEN (requires D0+D1+D-R).
27
+ 4. Panorama analysis + data profile (INPUT_PROFILED) with user interaction; global assumptions G-class confirmed via D2-G before CLAIMS_REGISTERED.
28
+ 5. Register claims and obligations; build the candidate matrix with literature column (CANDIDATES_READY) and ask D3.
29
+ 6. For EACH subproblem: subproblem analysis (reference the panorama, do not re-derive), S-class assumptions via D2-SPi, then ATTEMPT EXECUTE VERIFY subproblem robustness (plan→interact→run) D4 verdict REVISE or move to the next subproblem.
30
+ 7. Live documents: any discovery that shows an earlier phase was incomplete patches that phase incrementally (revision + stale), never re-running unaffected completed work.
31
+ 8. Backtracking depth follows the failure root cause (attempt/verify/direction/assumption/analysis/restatement/research), recorded in failed/issues.md.
32
+ 9. Probe tools with `scripts/capability-probe.mjs`; create a run-local Python environment with `scripts/python-environment.mjs` when computation is required; announce the tool choice and fallback.
33
33
  10. Dispatch subagents for parallel exploration, blind verification, and audits per `references/subagent-dispatch.md`.
34
- 11. Escalate evidence gaps or stalled directions through `references/research-breakthrough.md`; write wall memos and the failure ledger per `references/run-directory.md`.
35
- 12. Write only incremental attempt and terminal reports defined by `references/report-contract.md`; output the per-round runlog digest in the chat.
36
- 13. Before any terminal transition, run the cleanup gate (references/run-directory.md) and the robustness gate (a PASSED robustness-kind obligation, or scope.robustnessExempt with reason).
34
+ 11. Escalate evidence gaps through `references/research-breakthrough.md`; write wall memos and the failure ledger per `references/run-directory.md`.
35
+ 12. Write incremental attempt and terminal reports per `references/report-contract.md`; output the per-round runlog digest in the chat.
36
+ 13. Before any terminal transition: cleanup gate, ALL required robustness obligations PASS (or robustnessExempt), every subproblem DONE with dependencies satisfied, D4 for the current attempt, report.md present.
37
37
 
38
38
  ## Invariants
39
39
 
@@ -6,7 +6,9 @@ Store claim ID, exact wording, type, scope/quantifiers, assumptions, risk, verif
6
6
 
7
7
  ## Assumption record (v2)
8
8
 
9
- Store id, statement, scope (subproblem ids or global), source category, risk, sensitivity plan, validation status, affected claims, status (active/revised/superseded), and revisionHistory [{at, reason, changedTo}]. Allowed source categories are problem, data, theory, verified literature, domain instruction, and modeling simplification.
9
+ Store id, statement, scope ('global' for whole-problem assumptions, or a subproblem id list), source category, risk, sensitivity plan, validation status, affected claims, status (active/revised/superseded), and revisionHistory [{at, reason, changedTo}]. Allowed source categories are problem, data, theory, verified literature, domain instruction, and modeling simplification.
10
+
11
+ Two classes: G-class (global, confirmed once in the panorama layer via D2-G) and S-class (per subproblem, confirmed via D2-SPi). Claims carry an `assumptions` field listing the assumption ids they depend on; when an assumption is revised, dependent claims are marked stale and must carry post-revision evidence before a terminal state (validate warns otherwise).
10
12
 
11
13
  Every assumption additionally carries a `basis` field — the evidence chain behind it: `problem-quote` (exact original wording), `data` (evidence id + statistic), `literature` (source id + excerpt location), or `llm-judgment` (explicitly flagged as the LLM's own inference, challengeable in D2).
12
14
 
@@ -31,7 +31,8 @@ until the user says "可以了". Mathematical modeling is a deep activity;
31
31
  | D0 问题重述 | intake complete | the six-section deep restatement below + open clarification questions | accept + no additions |
32
32
  | D1 路由 | TRIAGE | task type (prediction/optimization/explanation/proof/…) + assurance mode (Fast/Standard/High-Assurance) | auto-routing result |
33
33
  | D-R 文献研究 | CLAIMS_REGISTERED (before CANDIDATES_READY) | the literature list by direction (AI-found); user endorses / rejects / adds their own | accept list |
34
- | D2 假设确认 | **each subproblem starts** | the assumptions for THIS subproblem, each with statement + basis chain (problem quote / data evidence / literature / LLM-judgment marker) + risk + challengeable point | accept all |
34
+ | D2-G 全局假设确认 | CLAIMS_REGISTERED (全景层) | the GLOBAL assumptions that hold for the whole problem (geometry, data, physical mechanism, numerical basis) each with basis chain + risk | accept all |
35
+ | D2 子问题假设确认 | **each subproblem starts** | the assumptions for THIS subproblem only (S-class), each with statement + basis chain + risk | accept all |
35
36
  | D2' 新假设插问 | mid-subproblem discovery that changes model structure | the new assumption + its effect | record-and-continue |
36
37
  | D3 方向选择 | CANDIDATES_READY | candidate matrix with a literature column per direction; user picks or authorizes auto | baseline |
37
38
  | D4 结果裁决 | VERIFY done | verdict + evidence digest + key intermediate results (residual/constraint-violation distributions, figure paths) + failure details | verdict-implied |
@@ -46,8 +47,12 @@ until the user says "可以了". Mathematical modeling is a deep activity;
46
47
  4. **隐含信息与歧义清单**:numbered; each entry = the LLM's reading + its
47
48
  basis + the risk if the reading is wrong. This is the heart of the
48
49
  exchange: the user corrects entries here.
49
- 5. **三问数学本质判定**:each sub-question classified (fitting/optimization/
50
+ 5. **各子问题数学本质判定**:every sub-question of the problem — however
51
+ many there are (one or ten) — is classified (fitting/optimization/
50
52
  geometric optics/simulation/…), with why.
53
+ 5.5 **问间依赖关系**:which sub-questions depend on which (models, data,
54
+ verified claims reused downstream) — the整体感 guarantee: later questions
55
+ REFERENCE earlier verified results instead of re-deriving them.
51
56
  6. **待澄清问题**:open questions inviting the user to add background,
52
57
  correct a reading, or supply their own references — never a yes/no list.
53
58
 
@@ -99,6 +104,19 @@ record to real files on disk.
99
104
  batch runs may declare `scope.contractExempt: true` — production modeling
100
105
  must never set it.
101
106
 
107
+ ## Phase pace — interactive beats between phases (v2.8)
108
+
109
+ The exposition is delivered PHASE BY PHASE, never as a firehose. The phase
110
+ sequence is: restatement → analysis → assumptions → modeling → solving →
111
+ verification → robustness → evaluation. Between every two phases there MUST
112
+ be a user interaction point: the completed phase is presented, one line asks
113
+ whether to proceed to the next phase, and the user may interrupt, ask for a
114
+ deeper explanation of any step of the just-finished phase, or contribute
115
+ knowledge. Sensitivity/robustness NEVER follows solving automatically — it
116
+ only starts after the verification phase, the user's go-ahead, and its own
117
+ D-interaction. Within a phase with ≥3 major steps, offer the user one
118
+ interruption opportunity after the first step.
119
+
102
120
  ## L2 display points (MUST show, never ask)
103
121
 
104
122
  - After every attempt round: the 8-section runlog digest (see report-contract.md).
@@ -127,6 +145,14 @@ record to real files on disk.
127
145
  and every downstream claim/obligation that depended on the old assumption is
128
146
  marked `stale: true` with the revising assumption id (see claims-evidence.md).
129
147
 
148
+ ## Live documents (every phase can be refined later)
149
+
150
+ Every phase artifact is LIVE: problem-brief.md (restatement), analysisLog,
151
+ assumptions, sources.jsonl, methods.md, attempt reports. While solving,
152
+ anything discovered that shows an earlier phase was incomplete is patched
153
+ INCREMENTALLY — revision record + downstream stale marking — without
154
+ re-running completed unaffected work. This is the "解题中不断完善" rule.
155
+
130
156
  ## Rolling documents
131
157
 
132
158
  - Problem restatement: written once at intake (problem-brief.md); only
@@ -17,6 +17,14 @@ Ask one question at a time. Read the problem and attachments first. Ask only que
17
17
  10. 假设及依据;11. 任务类型(预测/优化/解释/证明/机制/决策);12. 误差/精度要求;
18
18
  13. 验证标准;14. 最终交付物;15. 风险与失败模式;16. 不适用字段(标注原因)。
19
19
 
20
+ ## Layered questioning (v0.3)
21
+
22
+ - **全景层**:global assumptions (G-class) are grilled once — geometry, data
23
+ quality, physical mechanism, numerical basis; recorded via D2-G before
24
+ CLAIMS_REGISTERED.
25
+ - **逐问层**:per-subproblem assumptions (S-class) are grilled when the
26
+ subproblem starts (D2-SPi) — only what is specific to this sub-question.
27
+
20
28
  ## Per-subproblem grilling (v2)
21
29
 
22
30
  - Grill **one subproblem at a time**. Never demand assumptions for Q2 while Q1 is unsolved.
@@ -24,21 +24,73 @@ Do not copy full prior solutions or private reasoning.
24
24
 
25
25
  State the frozen question, final scientific status, answer/recommendation, verified claims and evidence links, conditional/unresolved claims, assumptions and sensitivity, candidate comparison, data and parameter sources, validation, failure cases, limitations, reproducibility commands, and resume conditions when non-SOLVED.
26
26
 
27
- ## Chat display the runlog digest (MUST output, v2)
27
+ 整体感 requirement (v0.3): the terminal report is ONE narrative, not a
28
+ concatenation of sub-question reports — it must show the question-dependency
29
+ chain (which model fed which), the shared global assumptions, and the
30
+ cross-sub-question consistency checks. A reader must be able to see the whole
31
+ problem as one connected argument.
32
+
33
+ ## Chat display — the runlog digest (MUST output, v2.7)
28
34
 
29
35
  The following is shown DIRECTLY IN THE CHAT, not only on disk.
30
36
 
37
+ ### Readability standard — write for a human reader, never a bullet list
38
+
39
+ Every section is an EXPOSITION, not a summary: state WHY the step is taken,
40
+ HOW the reasoning runs (derivation, mechanism, first-principles chain), WHAT
41
+ came out (numbers with units), and HOW it is checked. A reader who knows
42
+ nothing about the problem must be able to follow each section without
43
+ guessing.
44
+
45
+ Style: step-by-step numbered exposition with plain direct language (this is
46
+ a working document, not a final paper — no paper-style formal phrasing
47
+ required), but with MORE detail than a paper would carry: every parameter
48
+ choice gets its reason, every formula is followed by a plain-language
49
+ explanation of each symbol, every step ends with a transition line
50
+ ("这一步保证了…,为下一步…做好准备"), and every conclusion states what it
51
+ would take to be wrong. Minimum expectations per section:
52
+
53
+ - 问题重述:restate the question in your own words, explaining every
54
+ parameter's meaning and where it comes from in the problem text.
55
+ - 问题分析:walk the physical/geometric mechanism step by step (e.g. why the
56
+ nodes move radially, how the focal geometry pins the paraboloid), each
57
+ step justified by problem text, data, or derivation.
58
+ - 模型假设:for EVERY assumption give the reason it is justified
59
+ (original wording / data statistic / derivation / literature) and what
60
+ happens if it fails.
61
+ - 模型建立与求解:write the derivation in full (equations, variable
62
+ definitions, units, how the equation is solved, numerical method and why
63
+ it was chosen), not just the final formula.
64
+ - 验证:explain the verification LOGIC for each obligation (what would
65
+ falsify it, what tolerance is used and why), then the outcome.
66
+ - 鲁棒性:for each perturbation say what was varied, why that parameter,
67
+ and what would count as a conclusion flip.
68
+ - 评价与改进 / 参考文献:state the comparison criterion and map each
69
+ reference to the exact method it supports.
70
+
31
71
  ### Intake digest (once, at intake completion) — the six-section deep restatement
32
72
 
33
- 【题目要点摘录】problem's own numbers and constraints, quoted
34
- 【背景解读】engineering/physical mechanism
35
- 【数据语义】every attachment column by column (meaning/units/coordinate system/magnitudes)
36
- 【隐含信息与歧义清单】numbered; each = LLM reading + basis + risk (the user corrects entries here)
37
- 【三问数学本质判定】fitting/optimization/geometric optics/… + why
73
+ 【题目要点摘录】the problem's own numbers and constraints, quoted, each explained
74
+ 【背景解读】engineering/physical mechanism, walked through
75
+ 【数据语义】every attachment column by column (meaning/units/coordinate system/magnitudes), with observed statistics
76
+ 【隐含信息与歧义清单】numbered; each = LLM reading + derivation/basis + risk (the user corrects entries here)
77
+ 【各子问题数学本质判定】every sub-question (any count) classified + why
38
78
  【待澄清问题】open questions inviting user background/corrections/references
39
79
 
40
80
  After the D0 exchange, show the delta applied to problem-brief.md.
41
81
 
82
+ ### Quality self-check (MUST run before every D4 verdict)
83
+
84
+ Before presenting a D4 verdict, run the self-check on the current attempt's
85
+ report and show the result in the chat: for each of the eight digest sections,
86
+ confirm whether it answers all four questions — WHY (why this step), HOW
87
+ (how the reasoning runs), WHAT (result with units), CHECK (how it is
88
+ verified, or what would falsify it). Any section failing one of the four is
89
+ flagged as incomplete and the round is NOT presented for verdict until fixed.
90
+ The self-check is a human-facing declaration: the user decides whether the
91
+ exposition is actually sufficient. This is the quality gate; the machine
92
+ gates above only guarantee existence and structure.
93
+
42
94
  ### Per-round digest (after EVERY attempt round)
43
95
 
44
96
  ```text
@@ -13,6 +13,12 @@ TRIAGE → SCOPE_FROZEN → INPUT_PROFILED → CLAIMS_REGISTERED → CANDIDATES_
13
13
  VERIFY may move to REVISE, RESEARCH, FORK, an independent audit, or one terminal status:
14
14
  SOLVED, PARTIAL, CONDITIONAL, INCONCLUSIVE, REFUTED, INFEASIBLE, UNIDENTIFIABLE, BLOCKED, CANCELLED.
15
15
 
16
+ ## Three-layer flow (v0.3)
17
+
18
+ - **全景层**(一次建立,贯穿全题):TRIAGE(读题+D1) → RESEARCH(文献+D-R,前置) → SCOPE_FROZEN(D0 七段重述,含问间依赖) → INPUT_PROFILED(全景分析+数据画像) → CLAIMS_REGISTERED(全局假设 G,D2-G) → CANDIDATES_READY(方法矩阵+D3)。
19
+ - **逐问层**(SPi 循环):子问题分析 → 子问题假设 Si(D2-SPi)→ ATTEMPT 建模求解 → VERIFY 该问验证 → 该问鲁棒性(扰动计划→交互→执行)→ D4 裁决 → REVISE(本问修订)或进入 SPi+1(reason: "SPi 完成,进入 SPi+1")。
20
+ - **收束层**:跨问联动检查(终态 gate 校验全部子问题 DONE + 依赖满足)→ 全题鲁棒性汇总 → 终态硬门 → SOLVED + final-report(整体叙事,非拼凑)。
21
+
16
22
  ## Pre-modeling literature research (v2.6, mandatory)
17
23
 
18
24
  CLAIMS_REGISTERED → RESEARCH → CANDIDATES_READY. Before any candidate is
@@ -37,6 +43,22 @@ scope.robustnessExempt is set with a reason.
37
43
 
38
44
  - REVISE/RESEARCH reasons MUST reference a decisionStack entry id; the round
39
45
  returns to that decision point and tries the next option.
46
+ - Backtracking depth is chosen by the failure root cause (recorded in
47
+ failed/issues.md):
48
+
49
+ | Root cause | Depth | Action |
50
+ |---|---|---|
51
+ | IMPLEMENTATION / TOOL | attempt | fix and rerun, no backtrack |
52
+ | VALIDATION | verify | add obligations, re-verify |
53
+ | MODEL (wrong direction) | direction | switch candidate (wall memo) |
54
+ | assumption falsified | assumption | revise assumption (revisionHistory) → dependent claims stale → re-verify |
55
+ | analysis gap | analysis | update analysisLog → re-derive downstream |
56
+ | restatement misunderstanding | restatement | revise problem-brief (re-freeze) |
57
+ | RESEARCH_GAP | research | add sources, switch method |
58
+
59
+ Backtracking to assumption/analysis/restatement depth is a CONTENT revision
60
+ plus downstream re-verification — the state machine does not rewind; the
61
+ loop is expressed as REVISE cycles and event records.
40
62
  - FORK records its fork point; each failed branch writes a wall memo
41
63
  (run-directory.md) before the next branch starts. A wrong direction always
42
64
  returns to the fork point, never mid-branch.
@@ -18,7 +18,8 @@ const FINAL_STATES = ['SOLVED', 'PARTIAL', 'CONDITIONAL', 'INCONCLUSIVE', 'REFUT
18
18
  const STATUSES = [...NON_FINAL_STATES, ...FINAL_STATES]
19
19
  const STOP_TRANSITIONS = ['BLOCKED', 'CANCELLED']
20
20
  const TRANSITIONS = Object.freeze({
21
- TRIAGE: ['SCOPE_FROZEN', ...STOP_TRANSITIONS],
21
+ TRIAGE: ['RESEARCH', 'SCOPE_FROZEN', ...STOP_TRANSITIONS],
22
+ RESEARCH: ['SCOPE_FROZEN', 'CANDIDATES_READY', ...STOP_TRANSITIONS],
22
23
  SCOPE_FROZEN: ['INPUT_PROFILED', ...STOP_TRANSITIONS],
23
24
  INPUT_PROFILED: ['CLAIMS_REGISTERED', ...STOP_TRANSITIONS],
24
25
  CLAIMS_REGISTERED: ['RESEARCH', 'CANDIDATES_READY', ...STOP_TRANSITIONS],
@@ -27,7 +28,6 @@ const TRANSITIONS = Object.freeze({
27
28
  EXECUTE: ['VERIFY', ...STOP_TRANSITIONS],
28
29
  VERIFY: ['REVISE', 'RESEARCH', 'FORK', ...FINAL_STATES],
29
30
  REVISE: ['ATTEMPT', ...STOP_TRANSITIONS],
30
- RESEARCH: ['CANDIDATES_READY', ...STOP_TRANSITIONS],
31
31
  FORK: ['ATTEMPT', ...STOP_TRANSITIONS],
32
32
  })
33
33
  const TASK_ID = /^[a-z0-9]+(?:-[a-z0-9]+)*$/
@@ -440,13 +440,19 @@ function interactionContractViolations(run, ledger, to, reason, subproblem) {
440
440
  const violations = []
441
441
  const transitional = to !== undefined
442
442
  const earlyStop = ['BLOCKED', 'CANCELLED'].includes(run.status)
443
- if ((transitional && to === 'SCOPE_FROZEN') || (!transitional && run.status !== 'TRIAGE' && !earlyStop)) {
443
+ const statusOrder = ['TRIAGE', 'RESEARCH', 'SCOPE_FROZEN', 'INPUT_PROFILED', 'CLAIMS_REGISTERED', 'CANDIDATES_READY', 'ATTEMPT', 'EXECUTE', 'VERIFY', 'REVISE', 'FORK']
444
+ const reached = (target) => transitional
445
+ ? to === target
446
+ : (statusOrder.indexOf(target) <= statusOrder.indexOf(run.status) && !earlyStop)
447
+ if (reached('SCOPE_FROZEN')) {
444
448
  if (!hasDecisionRecord(scope, 'D0')) violations.push('D0 restatement interaction record missing (ledger.scope.interactions)')
445
449
  if (!hasDecisionRecord(scope, 'D1')) violations.push('D1 routing interaction record missing (ledger.scope.interactions)')
450
+ if (!hasDecisionRecord(scope, 'D-R', { allowAuto: false })) violations.push('D-R literature-research interaction record missing (literature survey precedes restatement; auto-authorization is not accepted)')
446
451
  }
452
+ if (reached('CLAIMS_REGISTERED') && !hasDecisionRecord(scope, 'D2-G')) violations.push('D2-G global-assumptions interaction record missing (global assumptions must be confirmed before claims registration)')
453
+ const candidatesGate = reached('CANDIDATES_READY')
454
+ if (candidatesGate && !hasDecisionRecord(scope, 'D-R', { allowAuto: false })) violations.push('D-R literature-research interaction record missing (backstop check)')
447
455
  const afterCandidates = ['CANDIDATES_READY', 'ATTEMPT', 'EXECUTE', 'VERIFY', 'REVISE', 'FORK', ...FINAL_STATES]
448
- const candidatesGate = transitional ? (to === 'CANDIDATES_READY') : (afterCandidates.includes(run.status) && !earlyStop)
449
- if (candidatesGate && !hasDecisionRecord(scope, 'D-R', { allowAuto: false })) violations.push('D-R literature-research interaction record missing (pre-modeling literature survey is mandatory; auto-authorization is not accepted)')
450
456
  const enteringAttemptFromCandidates = transitional ? (to === 'ATTEMPT' && run.status === 'CANDIDATES_READY') : (run.currentAttempt >= 1)
451
457
  if (enteringAttemptFromCandidates) {
452
458
  if (transitional && !subproblem) violations.push('ATTEMPT from CANDIDATES_READY requires --subproblem <id>')
@@ -468,8 +474,19 @@ function interactionContractViolations(run, ledger, to, reason, subproblem) {
468
474
  : (FINAL_STATES.includes(run.status) && !['BLOCKED', 'CANCELLED'].includes(run.status))
469
475
  if (terminalAndExempt) {
470
476
  if (scope.robustnessExempt !== true) {
471
- const passed = Array.isArray(ledger.obligations) && ledger.obligations.some(o => o && o.kind === 'robustness' && o.status === 'PASS')
472
- if (!passed) violations.push('terminal state requires a PASSED robustness-kind obligation (or scope.robustnessExempt: true)')
477
+ const robos = Array.isArray(ledger.obligations) ? ledger.obligations.filter(o => o && o.kind === 'robustness' && o.required !== false) : []
478
+ const allPassed = robos.length > 0 && robos.every(o => o.status === 'PASS')
479
+ if (!allPassed) violations.push(`terminal state requires ALL ${robos.length} required robustness obligations to be PASS (or scope.robustnessExempt: true)`)
480
+ }
481
+ const subs = Array.isArray(ledger.subproblems) ? ledger.subproblems : []
482
+ if (subs.length > 0) {
483
+ const done = (sp) => sp.status === 'DONE' || sp.status === 'CLOSED'
484
+ const unfinished = subs.filter(sp => !done(sp))
485
+ if (unfinished.length > 0) violations.push(`terminal state requires every subproblem DONE (open: ${unfinished.map(sp => sp.id).join(', ')})`)
486
+ const depGaps = subs.filter(sp => done(sp) && Array.isArray(sp.dependencies) && sp.dependencies.some(dep => {
487
+ const d = subs.find(x => x.id === dep); return d === undefined || !done(d)
488
+ }))
489
+ if (depGaps.length > 0) violations.push(`subproblem dependencies incomplete: ${depGaps.map(sp => sp.id).join(', ')}`)
473
490
  }
474
491
  if (scope.cleanupPassed !== true) violations.push('terminal state requires scope.cleanupPassed: true (run the cleanup checklist in run-directory.md)')
475
492
  }
@@ -574,6 +591,16 @@ async function validateRunUnlocked(root, expectedTaskId) {
574
591
  warnings.push(`attempt ${run.currentAttempt} report.md unreadable (runlog digest required)`)
575
592
  }
576
593
  }
594
+ for (const assumption of Array.isArray(ledger.assumptions) ? ledger.assumptions : []) {
595
+ const revised = assumption && (assumption.status === 'revised' || (Array.isArray(assumption.revisionHistory) && assumption.revisionHistory.length > 0))
596
+ if (!revised) continue
597
+ for (const claim of Array.isArray(ledger.claims) ? ledger.claims : []) {
598
+ const depends = claim && Array.isArray(claim.assumptions) && claim.assumptions.includes(assumption.id)
599
+ if (depends && claim.status === 'VERIFIED' && (!Array.isArray(claim.evidenceIds) || claim.evidenceIds.length === 0)) {
600
+ warnings.push(`claim ${claim.id} depends on revised assumption ${assumption.id} but has no post-revision evidence`)
601
+ }
602
+ }
603
+ }
577
604
  }
578
605
  return { valid: diagnostics.length === 0, diagnostics, warnings, run: clone(run) }
579
606
  }