dsh-math-modeling-agent 0.2.0 → 0.2.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
|
@@ -81,14 +81,20 @@ NOT_CHECKED → DERIVED → EXECUTED → VERIFIED → INDEPENDENTLY_VERIFIED →
|
|
|
81
81
|
默认运行目录 `math-modeling-runs/<task-id>/`:
|
|
82
82
|
|
|
83
83
|
```text
|
|
84
|
-
run.json
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
84
|
+
run.json ledger.json events.jsonl # 原子状态 + 契约(interactions/decisionStack)+ 日志
|
|
85
|
+
problem-brief.md inputs.json # 冻结的问题重述 + 输入清单
|
|
86
|
+
attempts/<n>/ # 每轮:report.md + code/ + data/ + plots/ + _drafts/
|
|
87
|
+
failed/directions/<id>/ # 方向级失败:wall memo + 代码 + 放弃理由
|
|
88
|
+
failed/code-drafts/<attempt>-<name>/ # 实现级失败:bug 版/超时版/弃用版(不删除)
|
|
89
|
+
failed/issues.md # 失败账本(DATA/TOOL/IMPLEMENTATION/MODEL/VALIDATION/EVIDENCE/RESEARCH_GAP)
|
|
90
|
+
research/ walls/ # 文献检索 / 突破备忘录
|
|
89
91
|
reproducibility.json final-report.md
|
|
90
92
|
```
|
|
91
93
|
|
|
94
|
+
v0.2.0 起:每个会改变模型结构的决策在对话中交互确认并写入 ledger(D0 重述/D1 路由/
|
|
95
|
+
D2 子问题假设/D3 方向/D4 裁决),`run-state.mjs gate` 在每次状态转移前强制校验;
|
|
96
|
+
终态前强制鲁棒性敏感度分析;失败尝试按类归档到独立目录。
|
|
97
|
+
|
|
92
98
|
崩溃恢复:跨进程互斥锁 + stale 锁与 reclaim guard 回收 + Windows 共享冲突重试;已完成且输入未变的工作不重跑;任何失败都保留最佳候选与原始产物。
|
|
93
99
|
|
|
94
100
|
## MCM / ICM 终审(audit Skill)
|
|
@@ -100,7 +106,7 @@ reproducibility.json final-report.md
|
|
|
100
106
|
安装:
|
|
101
107
|
|
|
102
108
|
```bash
|
|
103
|
-
dsh plugin --profile web add github:yohanchen1/MathModelingAgent#v0.2.
|
|
109
|
+
dsh plugin --profile web add github:yohanchen1/MathModelingAgent#v0.2.1
|
|
104
110
|
dsh --profile web --dump-config # 检查组合层
|
|
105
111
|
dsh web # 然后重启
|
|
106
112
|
```
|
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Minimal Run Example
|
|
1
|
+
# Minimal Run Example (v2)
|
|
2
2
|
|
|
3
3
|
Problem: solve 2x + 3 = 11.
|
|
4
4
|
|
|
@@ -11,4 +11,10 @@ Evidence E-002: substitution 2(4) + 3 = 11.
|
|
|
11
11
|
|
|
12
12
|
Status: SOLVED.
|
|
13
13
|
|
|
14
|
+
v2 interaction records (ledger.scope.interactions): D0 restatement (auto), D1 routing
|
|
15
|
+
(algebra / Fast), D2 assumptions (none beyond the equation), D3 direction
|
|
16
|
+
(direct inverse), D4 verdict (user accepted). scope.robustnessExempt = true —
|
|
17
|
+
a pure algebraic equation has no perturbable parameters, so the robustness
|
|
18
|
+
gate is exempted with that reason. decisionStack holds the D3 entry.
|
|
19
|
+
|
|
14
20
|
No Python, Lean, Wolfram, research directory, or empty artifact directory is required.
|
|
@@ -7,7 +7,9 @@ Resume sequence:
|
|
|
7
7
|
1. run `node scripts/run-state.mjs validate <run-root>`;
|
|
8
8
|
2. run `node scripts/run-state.mjs recover <run-root>`;
|
|
9
9
|
3. confirm C-003 remains the best candidate;
|
|
10
|
-
4.
|
|
11
|
-
5.
|
|
10
|
+
4. run `node scripts/run-state.mjs gate <run-root> --to REVISE --reason "回退到 D3-SP1:..."` (the reason must reference the decisionStack entry);
|
|
11
|
+
5. rerun only the incomplete verifier;
|
|
12
|
+
6. append a new evidence-linked transition (the CLI enforces the gate).
|
|
12
13
|
|
|
13
|
-
Previously hash-stable Python outputs are not recomputed.
|
|
14
|
+
Previously hash-stable Python outputs are not recomputed. Interaction records,
|
|
15
|
+
the decision stack, and the failure ledger survive the crash unchanged.
|
|
@@ -421,17 +421,27 @@ function decisionStackIds(scope) {
|
|
|
421
421
|
? scope.decisionStack.filter(entry => entry && typeof entry.id === 'string').map(entry => entry.id)
|
|
422
422
|
: []
|
|
423
423
|
}
|
|
424
|
-
|
|
424
|
+
/** D2 records carry the subproblem id(s) they cover: {decisionPoint:'D2', scope:['SP1'], ...} */
|
|
425
|
+
function hasSubproblemD2(scope, subproblem) {
|
|
426
|
+
if (!subproblem || !Array.isArray(scope?.interactions)) return true
|
|
427
|
+
return scope.interactions.some(entry => entry && entry.decisionPoint === 'D2' &&
|
|
428
|
+
(Array.isArray(entry.scope) ? entry.scope.includes(subproblem) : entry.subproblem === subproblem))
|
|
429
|
+
}
|
|
430
|
+
function interactionContractViolations(run, ledger, to, reason, subproblem) {
|
|
425
431
|
if (ledger.schemaVersion !== SCHEMA_VERSION) return []
|
|
426
432
|
const scope = ledger.scope ?? {}
|
|
427
433
|
const violations = []
|
|
428
434
|
const transitional = to !== undefined
|
|
429
435
|
const earlyStop = ['BLOCKED', 'CANCELLED'].includes(run.status)
|
|
430
436
|
if ((transitional && to === 'SCOPE_FROZEN') || (!transitional && run.status !== 'TRIAGE' && !earlyStop)) {
|
|
437
|
+
if (!hasDecisionRecord(scope, 'D0')) violations.push('D0 restatement interaction record missing (ledger.scope.interactions)')
|
|
431
438
|
if (!hasDecisionRecord(scope, 'D1')) violations.push('D1 routing interaction record missing (ledger.scope.interactions)')
|
|
432
439
|
}
|
|
433
440
|
const enteringAttemptFromCandidates = transitional ? (to === 'ATTEMPT' && run.status === 'CANDIDATES_READY') : (run.currentAttempt >= 1)
|
|
434
|
-
if (enteringAttemptFromCandidates
|
|
441
|
+
if (enteringAttemptFromCandidates) {
|
|
442
|
+
if (!hasDecisionRecord(scope, 'D3')) violations.push('D3 direction-selection interaction record missing')
|
|
443
|
+
if (transitional && !hasSubproblemD2(scope, subproblem)) violations.push(`D2 assumption interaction record missing for subproblem ${subproblem} (run the gate with --subproblem <id>)`)
|
|
444
|
+
}
|
|
435
445
|
const enteringTerminalFromVerify = transitional
|
|
436
446
|
? (FINAL_STATES.includes(to) && run.status === 'VERIFY')
|
|
437
447
|
: (FINAL_STATES.includes(run.status) && !['BLOCKED', 'CANCELLED'].includes(run.status))
|
|
@@ -539,6 +549,17 @@ async function validateRunUnlocked(root, expectedTaskId) {
|
|
|
539
549
|
for (const violation of interactionContractViolations(run, ledger, undefined, undefined)) {
|
|
540
550
|
warnings.push(`interaction contract: ${violation}`)
|
|
541
551
|
}
|
|
552
|
+
if (run.currentAttempt >= 1) {
|
|
553
|
+
const reportPath = join(root, 'attempts', String(run.currentAttempt), 'report.md')
|
|
554
|
+
try {
|
|
555
|
+
const reportText = await readFile(reportPath, 'utf8')
|
|
556
|
+
for (const section of ['问题重述', '问题分析', '模型假设', '模型建立与求解', '验证', '鲁棒性', '评价与改进', '参考文献']) {
|
|
557
|
+
if (!reportText.includes(section)) warnings.push(`attempt ${run.currentAttempt} report.md missing runlog digest section: ${section}`)
|
|
558
|
+
}
|
|
559
|
+
} catch {
|
|
560
|
+
warnings.push(`attempt ${run.currentAttempt} report.md unreadable (runlog digest required)`)
|
|
561
|
+
}
|
|
562
|
+
}
|
|
542
563
|
}
|
|
543
564
|
return { valid: diagnostics.length === 0, diagnostics, warnings, run: clone(run) }
|
|
544
565
|
}
|
|
@@ -552,12 +573,12 @@ export async function validateRun(root, expectedTaskId, runtime = {}) {
|
|
|
552
573
|
* mutating anything. The agent workflow must call this before `transition`;
|
|
553
574
|
* the CLI `transition` command enforces it internally unless --skip-gate.
|
|
554
575
|
*/
|
|
555
|
-
export async function gateTransition(root, { to, reason }, runtime = {}) {
|
|
576
|
+
export async function gateTransition(root, { to, reason, subproblem }, runtime = {}) {
|
|
556
577
|
return withLock(root, MUTATION_LOCK_FILE, 'gate', async () => {
|
|
557
578
|
const p = paths(root)
|
|
558
579
|
const run = await readJson(p.run)
|
|
559
580
|
const ledger = await readJson(p.ledger)
|
|
560
|
-
const violations = interactionContractViolations(run, ledger, to, reason)
|
|
581
|
+
const violations = interactionContractViolations(run, ledger, to, reason, subproblem)
|
|
561
582
|
return { allowed: violations.length === 0, violations, status: run.status }
|
|
562
583
|
}, runtime)
|
|
563
584
|
}
|
|
@@ -603,13 +624,13 @@ async function cli(argv) {
|
|
|
603
624
|
const options = parseOptions(rest)
|
|
604
625
|
if (command === 'init') return initRun(root, { taskId: options['task-id'], mode: options.mode ?? 'standard' })
|
|
605
626
|
if (command === 'gate') {
|
|
606
|
-
const result = await gateTransition(root, { to: options.to, reason: options.reason })
|
|
627
|
+
const result = await gateTransition(root, { to: options.to, reason: options.reason, subproblem: options.subproblem })
|
|
607
628
|
if (!result.allowed) process.exitCode = 1
|
|
608
629
|
return result
|
|
609
630
|
}
|
|
610
631
|
if (command === 'transition') {
|
|
611
632
|
if (options['skip-gate'] !== 'true') {
|
|
612
|
-
const gate = await gateTransition(root, { to: options.to, reason: options.reason })
|
|
633
|
+
const gate = await gateTransition(root, { to: options.to, reason: options.reason, subproblem: options.subproblem })
|
|
613
634
|
if (!gate.allowed) {
|
|
614
635
|
process.exitCode = 1
|
|
615
636
|
return { error: `interaction contract violated: ${gate.violations.join('; ')}`, gate }
|