dsh-math-modeling-agent 0.1.1 → 0.2.0
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 +5 -4
- package/package.json +2 -3
- package/skills/math-modeling-agent/SKILL.md +16 -11
- package/skills/math-modeling-agent/references/claims-evidence.md +10 -2
- package/skills/math-modeling-agent/references/interaction-protocol.md +64 -0
- package/skills/math-modeling-agent/references/math-grill.md +16 -10
- package/skills/math-modeling-agent/references/report-contract.md +38 -2
- package/skills/math-modeling-agent/references/run-directory.md +62 -0
- package/skills/math-modeling-agent/references/subagent-dispatch.md +47 -0
- package/skills/math-modeling-agent/references/tool-policy.md +18 -0
- package/skills/math-modeling-agent/references/workflow.md +31 -0
- package/skills/math-modeling-agent/schemas/ledger.schema.json +25 -4
- package/skills/math-modeling-agent/scripts/python-environment.mjs +13 -5
- package/skills/math-modeling-agent/scripts/run-state.mjs +93 -10
- package/docs/assets/mathmodelingagent-overview.svg +0 -92
- package/docs/superpowers/plans/2026-08-22-math-modeling-dsh-plugin.md +0 -3303
- package/docs/superpowers/specs/2026-08-22-math-modeling-dsh-plugin-design.md +0 -889
|
@@ -4,7 +4,9 @@ import { dirname, join, resolve } from 'node:path'
|
|
|
4
4
|
import { fileURLToPath } from 'node:url'
|
|
5
5
|
import { isDeepStrictEqual } from 'node:util'
|
|
6
6
|
|
|
7
|
-
export const SCHEMA_VERSION =
|
|
7
|
+
export const SCHEMA_VERSION = 2
|
|
8
|
+
export const SUPPORTED_SCHEMA_VERSIONS = [1, 2]
|
|
9
|
+
export const INTERACTION_DECISIONS = Object.freeze({ D1: 'routing', D2: 'assumptions', D3: 'direction', D4: 'verdict' })
|
|
8
10
|
export const MODE_DEFAULTS = Object.freeze({
|
|
9
11
|
fast: Object.freeze({ attempts: 2, researchQueries: 0, computeSeconds: 60 }),
|
|
10
12
|
standard: Object.freeze({ attempts: 12, researchQueries: 12, computeSeconds: 1800 }),
|
|
@@ -282,7 +284,7 @@ function deeplyEqual(left, right) { return isDeepStrictEqual(left, right) }
|
|
|
282
284
|
function validateRunShape(run, diagnostics, prefix = 'run') {
|
|
283
285
|
addDiagnostic(diagnostics, exactKeys(run, RUN_KEYS), `${prefix} must contain exactly the contracted fields`)
|
|
284
286
|
if (!isObject(run)) return
|
|
285
|
-
addDiagnostic(diagnostics, run.schemaVersion
|
|
287
|
+
addDiagnostic(diagnostics, SUPPORTED_SCHEMA_VERSIONS.includes(run.schemaVersion), `${prefix}.schemaVersion must be one of ${SUPPORTED_SCHEMA_VERSIONS.join(',')}`)
|
|
286
288
|
addDiagnostic(diagnostics, typeof run.taskId === 'string' && TASK_ID.test(run.taskId), `${prefix}.taskId must be kebab-case`)
|
|
287
289
|
addDiagnostic(diagnostics, Object.hasOwn(MODE_DEFAULTS, run.mode), `${prefix}.mode is invalid`)
|
|
288
290
|
addDiagnostic(diagnostics, STATUSES.includes(run.status), `${prefix}.status is invalid`)
|
|
@@ -297,7 +299,7 @@ function validateRunShape(run, diagnostics, prefix = 'run') {
|
|
|
297
299
|
function validateLedgerShape(ledger, diagnostics) {
|
|
298
300
|
addDiagnostic(diagnostics, exactKeys(ledger, LEDGER_KEYS), 'ledger must contain exactly the contracted fields')
|
|
299
301
|
if (!isObject(ledger)) return
|
|
300
|
-
addDiagnostic(diagnostics, ledger.schemaVersion
|
|
302
|
+
addDiagnostic(diagnostics, SUPPORTED_SCHEMA_VERSIONS.includes(ledger.schemaVersion), `ledger.schemaVersion must be one of ${SUPPORTED_SCHEMA_VERSIONS.join(',')}`)
|
|
301
303
|
addDiagnostic(diagnostics, typeof ledger.taskId === 'string' && TASK_ID.test(ledger.taskId), 'ledger.taskId must be kebab-case')
|
|
302
304
|
addDiagnostic(diagnostics, isObject(ledger.scope), 'ledger.scope must be an object')
|
|
303
305
|
for (const key of ['assumptions', 'claims', 'obligations', 'subproblems', 'candidates', 'issues']) addDiagnostic(diagnostics, Array.isArray(ledger[key]), `ledger.${key} must be an array`)
|
|
@@ -312,7 +314,7 @@ function validateEventShape(event, diagnostics, index) {
|
|
|
312
314
|
? ['schemaVersion', 'sequence', 'type', 'taskId', 'timestamp', 'snapshot']
|
|
313
315
|
: ['schemaVersion', 'sequence', 'type', 'taskId', 'from', 'to', 'reason', 'evidenceIds', 'issueIds', 'timestamp', 'snapshot']
|
|
314
316
|
addDiagnostic(diagnostics, exactKeys(event, expectedKeys), `${prefix} must contain exactly the contracted fields`)
|
|
315
|
-
addDiagnostic(diagnostics, event.schemaVersion
|
|
317
|
+
addDiagnostic(diagnostics, SUPPORTED_SCHEMA_VERSIONS.includes(event.schemaVersion), `${prefix}.schemaVersion must be one of ${SUPPORTED_SCHEMA_VERSIONS.join(',')}`)
|
|
316
318
|
addDiagnostic(diagnostics, event.sequence === index, `${prefix}.sequence must equal ${index}`)
|
|
317
319
|
addDiagnostic(diagnostics, typeof event.taskId === 'string' && TASK_ID.test(event.taskId), `${prefix}.taskId must be kebab-case`)
|
|
318
320
|
addDiagnostic(diagnostics, event.type === expectedType, `${prefix}.type must be ${expectedType}`)
|
|
@@ -377,8 +379,8 @@ export async function initRun(root, { taskId, mode = 'standard', budget, budgets
|
|
|
377
379
|
bestCandidateId: null, budget: effectiveBudget, createdAt: timestamp, updatedAt: timestamp,
|
|
378
380
|
}
|
|
379
381
|
const ledger = {
|
|
380
|
-
schemaVersion: SCHEMA_VERSION, taskId, scope: { independentAuditPassed: false
|
|
381
|
-
obligations: [], subproblems: [], candidates: [], issues: [],
|
|
382
|
+
schemaVersion: SCHEMA_VERSION, taskId, scope: { independentAuditPassed: false, interactions: [], decisionStack: [], robustnessExempt: false },
|
|
383
|
+
assumptions: [], claims: [], obligations: [], subproblems: [], candidates: [], issues: [],
|
|
382
384
|
}
|
|
383
385
|
const event = { schemaVersion: SCHEMA_VERSION, sequence: 0, type: 'RUN_INITIALIZED', taskId, timestamp, snapshot: clone(run) }
|
|
384
386
|
const p = paths(root)
|
|
@@ -409,6 +411,51 @@ export async function initRun(root, { taskId, mode = 'standard', budget, budgets
|
|
|
409
411
|
}, runtime)
|
|
410
412
|
}
|
|
411
413
|
|
|
414
|
+
/** The v2 interaction contract: decision records, decision-stack backtracking, and the robustness gate. */
|
|
415
|
+
function hasDecisionRecord(scope, decisionPoint) {
|
|
416
|
+
return Array.isArray(scope?.interactions) && scope.interactions.some(entry =>
|
|
417
|
+
entry && (entry.decisionPoint === decisionPoint || entry.auto === true))
|
|
418
|
+
}
|
|
419
|
+
function decisionStackIds(scope) {
|
|
420
|
+
return Array.isArray(scope?.decisionStack)
|
|
421
|
+
? scope.decisionStack.filter(entry => entry && typeof entry.id === 'string').map(entry => entry.id)
|
|
422
|
+
: []
|
|
423
|
+
}
|
|
424
|
+
function interactionContractViolations(run, ledger, to, reason) {
|
|
425
|
+
if (ledger.schemaVersion !== SCHEMA_VERSION) return []
|
|
426
|
+
const scope = ledger.scope ?? {}
|
|
427
|
+
const violations = []
|
|
428
|
+
const transitional = to !== undefined
|
|
429
|
+
const earlyStop = ['BLOCKED', 'CANCELLED'].includes(run.status)
|
|
430
|
+
if ((transitional && to === 'SCOPE_FROZEN') || (!transitional && run.status !== 'TRIAGE' && !earlyStop)) {
|
|
431
|
+
if (!hasDecisionRecord(scope, 'D1')) violations.push('D1 routing interaction record missing (ledger.scope.interactions)')
|
|
432
|
+
}
|
|
433
|
+
const enteringAttemptFromCandidates = transitional ? (to === 'ATTEMPT' && run.status === 'CANDIDATES_READY') : (run.currentAttempt >= 1)
|
|
434
|
+
if (enteringAttemptFromCandidates && !hasDecisionRecord(scope, 'D3')) violations.push('D3 direction-selection interaction record missing')
|
|
435
|
+
const enteringTerminalFromVerify = transitional
|
|
436
|
+
? (FINAL_STATES.includes(to) && run.status === 'VERIFY')
|
|
437
|
+
: (FINAL_STATES.includes(run.status) && !['BLOCKED', 'CANCELLED'].includes(run.status))
|
|
438
|
+
if (enteringTerminalFromVerify && !hasDecisionRecord(scope, 'D4')) violations.push('D4 verdict interaction record missing')
|
|
439
|
+
if (transitional && (to === 'REVISE' || to === 'RESEARCH')) {
|
|
440
|
+
const ids = decisionStackIds(scope)
|
|
441
|
+
if (ids.length > 0 && !ids.some(id => typeof reason === 'string' && reason.includes(id))) {
|
|
442
|
+
violations.push(`REVISE/RESEARCH reason must reference a decisionStack entry id (available: ${ids.join(', ')})`)
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
const terminalAndExempt = transitional
|
|
446
|
+
? (FINAL_STATES.includes(to) && !['BLOCKED', 'CANCELLED'].includes(to))
|
|
447
|
+
: (FINAL_STATES.includes(run.status) && !['BLOCKED', 'CANCELLED'].includes(run.status))
|
|
448
|
+
if (terminalAndExempt && scope.robustnessExempt !== true) {
|
|
449
|
+
const passed = Array.isArray(ledger.obligations) && ledger.obligations.some(o => o && o.kind === 'robustness' && o.status === 'PASS')
|
|
450
|
+
if (!passed) violations.push('terminal state requires a PASSED robustness-kind obligation (or scope.robustnessExempt: true)')
|
|
451
|
+
}
|
|
452
|
+
return violations
|
|
453
|
+
}
|
|
454
|
+
function enforceInteractionContract(run, ledger, to, reason) {
|
|
455
|
+
const violations = interactionContractViolations(run, ledger, to, reason)
|
|
456
|
+
if (violations.length > 0) throw new Error(`interaction contract violated: ${violations.join('; ')}`)
|
|
457
|
+
}
|
|
458
|
+
|
|
412
459
|
function enforceSolvedGate(run, ledger) {
|
|
413
460
|
if (ledger.claims.length < 1) throw new Error('SOLVED requires at least one claim')
|
|
414
461
|
if (ledger.obligations.some(item => item.required !== false && item.status !== 'PASS')) throw new Error('required obligations remain open')
|
|
@@ -473,12 +520,13 @@ async function readEvents(path) {
|
|
|
473
520
|
|
|
474
521
|
async function validateRunUnlocked(root, expectedTaskId) {
|
|
475
522
|
const diagnostics = []
|
|
523
|
+
const warnings = []
|
|
476
524
|
let run, ledger, events
|
|
477
525
|
const p = paths(root)
|
|
478
526
|
try { run = await readJson(p.run) } catch (error) { diagnostics.push(`run unreadable: ${error.message}`) }
|
|
479
527
|
try { ledger = await readJson(p.ledger) } catch (error) { diagnostics.push(`ledger unreadable: ${error.message}`) }
|
|
480
528
|
try { events = await readEvents(p.events) } catch (error) { diagnostics.push(error.message) }
|
|
481
|
-
if (!run || !ledger || !events) return { valid: false, diagnostics }
|
|
529
|
+
if (!run || !ledger || !events) return { valid: false, diagnostics, warnings }
|
|
482
530
|
validateRunShape(run, diagnostics)
|
|
483
531
|
validateLedgerShape(ledger, diagnostics)
|
|
484
532
|
validateJournal(events, diagnostics)
|
|
@@ -486,13 +534,34 @@ async function validateRunUnlocked(root, expectedTaskId) {
|
|
|
486
534
|
const last = events.at(-1)
|
|
487
535
|
if (last && run.eventSequence !== last.sequence) diagnostics.push('state/journal sequence mismatch')
|
|
488
536
|
if (last && JSON.stringify(run) !== JSON.stringify(last.snapshot)) diagnostics.push('state/journal snapshot mismatch')
|
|
489
|
-
|
|
537
|
+
if (ledger.schemaVersion !== SCHEMA_VERSION) warnings.push(`legacy run (ledger schema v${ledger.schemaVersion}): interaction contract not enforced`)
|
|
538
|
+
else {
|
|
539
|
+
for (const violation of interactionContractViolations(run, ledger, undefined, undefined)) {
|
|
540
|
+
warnings.push(`interaction contract: ${violation}`)
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
return { valid: diagnostics.length === 0, diagnostics, warnings, run: clone(run) }
|
|
490
544
|
}
|
|
491
545
|
|
|
492
546
|
export async function validateRun(root, expectedTaskId, runtime = {}) {
|
|
493
547
|
return withLock(root, MUTATION_LOCK_FILE, 'validate', () => validateRunUnlocked(root, expectedTaskId), runtime)
|
|
494
548
|
}
|
|
495
549
|
|
|
550
|
+
/**
|
|
551
|
+
* Check the v2 interaction contract for a PROSPECTIVE transition without
|
|
552
|
+
* mutating anything. The agent workflow must call this before `transition`;
|
|
553
|
+
* the CLI `transition` command enforces it internally unless --skip-gate.
|
|
554
|
+
*/
|
|
555
|
+
export async function gateTransition(root, { to, reason }, runtime = {}) {
|
|
556
|
+
return withLock(root, MUTATION_LOCK_FILE, 'gate', async () => {
|
|
557
|
+
const p = paths(root)
|
|
558
|
+
const run = await readJson(p.run)
|
|
559
|
+
const ledger = await readJson(p.ledger)
|
|
560
|
+
const violations = interactionContractViolations(run, ledger, to, reason)
|
|
561
|
+
return { allowed: violations.length === 0, violations, status: run.status }
|
|
562
|
+
}, runtime)
|
|
563
|
+
}
|
|
564
|
+
|
|
496
565
|
export async function recoverRun(root, runtime = {}) {
|
|
497
566
|
return withLock(root, MUTATION_LOCK_FILE, 'recover', async () => {
|
|
498
567
|
const p = paths(root)
|
|
@@ -533,7 +602,21 @@ async function cli(argv) {
|
|
|
533
602
|
const root = resolve(rootArg)
|
|
534
603
|
const options = parseOptions(rest)
|
|
535
604
|
if (command === 'init') return initRun(root, { taskId: options['task-id'], mode: options.mode ?? 'standard' })
|
|
536
|
-
if (command === '
|
|
605
|
+
if (command === 'gate') {
|
|
606
|
+
const result = await gateTransition(root, { to: options.to, reason: options.reason })
|
|
607
|
+
if (!result.allowed) process.exitCode = 1
|
|
608
|
+
return result
|
|
609
|
+
}
|
|
610
|
+
if (command === 'transition') {
|
|
611
|
+
if (options['skip-gate'] !== 'true') {
|
|
612
|
+
const gate = await gateTransition(root, { to: options.to, reason: options.reason })
|
|
613
|
+
if (!gate.allowed) {
|
|
614
|
+
process.exitCode = 1
|
|
615
|
+
return { error: `interaction contract violated: ${gate.violations.join('; ')}`, gate }
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
return transitionRun(root, { to: options.to, reason: options.reason, evidenceIds: options.evidence ? options.evidence.split(',').filter(Boolean) : [], issueIds: options.issues ? options.issues.split(',').filter(Boolean) : [], patch: options.candidate ? { bestCandidateId: options.candidate } : undefined })
|
|
619
|
+
}
|
|
537
620
|
if (command === 'validate') {
|
|
538
621
|
const result = await validateRun(root, options['task-id'])
|
|
539
622
|
if (!result.valid) process.exitCode = 1
|
|
@@ -541,7 +624,7 @@ async function cli(argv) {
|
|
|
541
624
|
}
|
|
542
625
|
if (command === 'recover') return recoverRun(root)
|
|
543
626
|
if (command === 'status') return statusRun(root)
|
|
544
|
-
throw new Error('usage: run-state.mjs <init|transition|validate|recover|status> <run-directory> [options]')
|
|
627
|
+
throw new Error('usage: run-state.mjs <init|transition|gate|validate|recover|status> <run-directory> [options]')
|
|
545
628
|
}
|
|
546
629
|
const invoked = process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)
|
|
547
630
|
if (invoked) cli(process.argv.slice(2)).then(result => process.stdout.write(`${JSON.stringify(result)}\n`)).catch(error => { process.stdout.write(`${JSON.stringify({ error: error.message })}\n`); process.exitCode = 1 })
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="1066" height="620" viewBox="0 0 1066 620" role="img" aria-label="MathModelingAgent 工作流总览">
|
|
2
|
-
<defs>
|
|
3
|
-
<filter id="shadow" x="-4%" y="-4%" width="108%" height="112%">
|
|
4
|
-
<feDropShadow dx="0" dy="2" stdDeviation="4" flood-color="#0f172a" flood-opacity="0.06"/>
|
|
5
|
-
</filter>
|
|
6
|
-
</defs>
|
|
7
|
-
<rect width="1066" height="620" rx="16" fill="#f8fafc"/>
|
|
8
|
-
<text x="40" y="52" font-family="system-ui,-apple-system,'Segoe UI',sans-serif" font-size="30" font-weight="700" fill="#0f172a">MathModelingAgent</text>
|
|
9
|
-
<text x="40" y="84" font-family="system-ui,-apple-system,'Segoe UI',sans-serif" font-size="15" fill="#475569">从题目到可复现结论 · 模型负责提出,证据负责裁决</text>
|
|
10
|
-
<g font-family="system-ui,-apple-system,'Segoe UI',sans-serif" font-size="11" fill="#334155">
|
|
11
|
-
<rect x="774" y="26" width="116" height="28" rx="14" fill="#ffffff" stroke="#e2e8f0"/>
|
|
12
|
-
<text x="832" y="44" text-anchor="middle" fill="#475569">不绑定具体状态名</text>
|
|
13
|
-
<rect x="900" y="26" width="122" height="28" rx="14" fill="#ffffff" stroke="#e2e8f0"/>
|
|
14
|
-
<text x="961" y="44" text-anchor="middle" fill="#475569">不绑定文件结构</text>
|
|
15
|
-
<rect x="1032" y="26" width="104" height="28" rx="14" fill="#ffffff" stroke="#e2e8f0"/>
|
|
16
|
-
<text x="1084" y="44" text-anchor="middle" fill="#475569">工具可替换</text>
|
|
17
|
-
</g>
|
|
18
|
-
<g font-family="system-ui,-apple-system,'Segoe UI',sans-serif">
|
|
19
|
-
<rect x="40" y="112" width="184" height="250" rx="14" fill="#ffffff" stroke="#e2e8f0" filter="url(#shadow)"/>
|
|
20
|
-
<rect x="40" y="112" width="184" height="8" rx="4" fill="#34d399"/>
|
|
21
|
-
<text x="56" y="156" font-size="17" font-weight="700" fill="#0f172a">题目与数据</text>
|
|
22
|
-
<text x="56" y="178" font-size="11" font-weight="700" letter-spacing="1" fill="#34d399">INPUT</text>
|
|
23
|
-
<circle cx="62" cy="208" r="4" fill="#34d399"/><text x="74" y="212" font-size="13" fill="#475569">题目 / PDF</text>
|
|
24
|
-
<circle cx="62" cy="240" r="4" fill="#34d399"/><text x="74" y="244" font-size="13" fill="#475569">数据与附件</text>
|
|
25
|
-
<circle cx="62" cy="272" r="4" fill="#34d399"/><text x="74" y="276" font-size="13" fill="#475569">目标与约束</text>
|
|
26
|
-
|
|
27
|
-
<rect x="246" y="112" width="184" height="250" rx="14" fill="#ffffff" stroke="#e2e8f0" filter="url(#shadow)"/>
|
|
28
|
-
<rect x="246" y="112" width="184" height="8" rx="4" fill="#60a5fa"/>
|
|
29
|
-
<text x="262" y="156" font-size="17" font-weight="700" fill="#0f172a">理解与拆解</text>
|
|
30
|
-
<text x="262" y="178" font-size="11" font-weight="700" letter-spacing="1" fill="#60a5fa">UNDERSTAND</text>
|
|
31
|
-
<circle cx="268" cy="208" r="4" fill="#60a5fa"/><text x="280" y="212" font-size="13" fill="#475569">明确问题边界</text>
|
|
32
|
-
<circle cx="268" cy="240" r="4" fill="#60a5fa"/><text x="280" y="244" font-size="13" fill="#475569">拆成可验证子问题</text>
|
|
33
|
-
<circle cx="268" cy="272" r="4" fill="#60a5fa"/><text x="280" y="276" font-size="13" fill="#475569">登记关键假设</text>
|
|
34
|
-
|
|
35
|
-
<rect x="452" y="112" width="184" height="250" rx="14" fill="#ffffff" stroke="#e2e8f0" filter="url(#shadow)"/>
|
|
36
|
-
<rect x="452" y="112" width="184" height="8" rx="4" fill="#a78bfa"/>
|
|
37
|
-
<text x="468" y="156" font-size="17" font-weight="700" fill="#0f172a">建模与执行</text>
|
|
38
|
-
<text x="468" y="178" font-size="11" font-weight="700" letter-spacing="1" fill="#a78bfa">EXPLORE</text>
|
|
39
|
-
<circle cx="474" cy="208" r="4" fill="#a78bfa"/><text x="486" y="212" font-size="13" fill="#475569">提出候选模型</text>
|
|
40
|
-
<circle cx="474" cy="240" r="4" fill="#a78bfa"/><text x="486" y="244" font-size="13" fill="#475569">计算 / 搜索 / 推导</text>
|
|
41
|
-
<circle cx="474" cy="272" r="4" fill="#a78bfa"/><text x="486" y="276" font-size="13" fill="#475569">保留过程产物</text>
|
|
42
|
-
|
|
43
|
-
<rect x="658" y="112" width="184" height="250" rx="14" fill="#ffffff" stroke="#e2e8f0" filter="url(#shadow)"/>
|
|
44
|
-
<rect x="658" y="112" width="184" height="8" rx="4" fill="#fb923c"/>
|
|
45
|
-
<text x="674" y="156" font-size="17" font-weight="700" fill="#0f172a">证据验证</text>
|
|
46
|
-
<text x="674" y="178" font-size="11" font-weight="700" letter-spacing="1" fill="#fb923c">VERIFY</text>
|
|
47
|
-
<circle cx="680" cy="208" r="4" fill="#fb923c"/><text x="692" y="212" font-size="13" fill="#475569">主张 ↔ 验证义务</text>
|
|
48
|
-
<circle cx="680" cy="240" r="4" fill="#fb923c"/><text x="692" y="244" font-size="13" fill="#475569">独立重算 / 反例攻击</text>
|
|
49
|
-
<circle cx="680" cy="272" r="4" fill="#fb923c"/><text x="692" y="276" font-size="13" fill="#475569">证据不足就回去修正</text>
|
|
50
|
-
|
|
51
|
-
<rect x="864" y="112" width="184" height="250" rx="14" fill="#ffffff" stroke="#e2e8f0" filter="url(#shadow)"/>
|
|
52
|
-
<rect x="864" y="112" width="184" height="8" rx="4" fill="#f472b6"/>
|
|
53
|
-
<text x="880" y="156" font-size="17" font-weight="700" fill="#0f172a">结果交付</text>
|
|
54
|
-
<text x="880" y="178" font-size="11" font-weight="700" letter-spacing="1" fill="#f472b6">DELIVER</text>
|
|
55
|
-
<circle cx="886" cy="208" r="4" fill="#f472b6"/><text x="898" y="212" font-size="13" fill="#475569">结论 + 局限</text>
|
|
56
|
-
<circle cx="886" cy="240" r="4" fill="#f472b6"/><text x="898" y="244" font-size="13" fill="#475569">代码 / 数据 / 复现材料</text>
|
|
57
|
-
<circle cx="886" cy="272" r="4" fill="#f472b6"/><text x="898" y="276" font-size="13" fill="#475569">审计 / 终态报告</text>
|
|
58
|
-
</g>
|
|
59
|
-
<g stroke="#94a3b8" stroke-width="2.5" fill="none" marker-end="url(#arrow)">
|
|
60
|
-
<line x1="224" y1="237" x2="242" y2="237"/>
|
|
61
|
-
<line x1="430" y1="237" x2="448" y2="237"/>
|
|
62
|
-
<line x1="636" y1="237" x2="654" y2="237"/>
|
|
63
|
-
<line x1="842" y1="237" x2="860" y2="237"/>
|
|
64
|
-
</g>
|
|
65
|
-
<defs><marker id="arrow" markerWidth="8" markerHeight="8" refX="6" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8 Z" fill="#94a3b8"/></marker></defs>
|
|
66
|
-
<path d="M750 362 C 750 430, 700 452, 544 452 L 544 366" fill="none" stroke="#6366f1" stroke-width="2" stroke-dasharray="6 5" marker-end="url(#arrowLoop)"/>
|
|
67
|
-
<defs><marker id="arrowLoop" markerWidth="8" markerHeight="8" refX="6" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8 Z" fill="#6366f1"/></marker></defs>
|
|
68
|
-
<rect x="404" y="430" width="284" height="28" rx="14" fill="#eef2ff" stroke="#c7d2fe"/>
|
|
69
|
-
<text x="546" y="448" text-anchor="middle" font-family="system-ui,-apple-system,'Segoe UI',sans-serif" font-size="13" fill="#4338ca">证据不够 → 修正,而不是硬通过</text>
|
|
70
|
-
|
|
71
|
-
<rect x="40" y="478" width="1008" height="118" rx="14" fill="#ffffff" stroke="#e2e8f0"/>
|
|
72
|
-
<text x="64" y="512" font-family="system-ui,-apple-system,'Segoe UI',sans-serif" font-size="15" font-weight="700" fill="#0f172a">可插拔能力</text>
|
|
73
|
-
<text x="64" y="534" font-family="system-ui,-apple-system,'Segoe UI',sans-serif" font-size="12" fill="#64748b">实现可以换,语义不变:任何工具都只是为"建模"或"验证"提供证据。</text>
|
|
74
|
-
<g font-family="system-ui,-apple-system,'Segoe UI',sans-serif" text-anchor="middle">
|
|
75
|
-
<rect x="64" y="552" width="180" height="26" rx="8" fill="#f1f5f9"/>
|
|
76
|
-
<text x="154" y="569" font-size="12" font-weight="600" fill="#0f172a">数值计算</text>
|
|
77
|
-
<text x="154" y="582" font-size="10" fill="#64748b">Python / 其他运行时</text>
|
|
78
|
-
<rect x="258" y="552" width="180" height="26" rx="8" fill="#f1f5f9"/>
|
|
79
|
-
<text x="348" y="569" font-size="12" font-weight="600" fill="#0f172a">形式验证</text>
|
|
80
|
-
<text x="348" y="582" font-size="10" fill="#64748b">Lean / theorem prover</text>
|
|
81
|
-
<rect x="452" y="552" width="180" height="26" rx="8" fill="#f1f5f9"/>
|
|
82
|
-
<text x="542" y="569" font-size="12" font-weight="600" fill="#0f172a">符号计算</text>
|
|
83
|
-
<text x="542" y="582" font-size="10" fill="#64748b">Wolfram / CAS</text>
|
|
84
|
-
<rect x="646" y="552" width="180" height="26" rx="8" fill="#f1f5f9"/>
|
|
85
|
-
<text x="736" y="569" font-size="12" font-weight="600" fill="#0f172a">研究检索</text>
|
|
86
|
-
<text x="736" y="582" font-size="10" fill="#64748b">文献 / Web</text>
|
|
87
|
-
<rect x="840" y="552" width="180" height="26" rx="8" fill="#f1f5f9"/>
|
|
88
|
-
<text x="930" y="569" font-size="12" font-weight="600" fill="#0f172a">终审</text>
|
|
89
|
-
<text x="930" y="582" font-size="10" fill="#64748b">MCM / ICM audit</text>
|
|
90
|
-
</g>
|
|
91
|
-
<text x="1032" y="612" text-anchor="end" font-family="system-ui,-apple-system,'Segoe UI',sans-serif" font-size="10" fill="#94a3b8">README overview</text>
|
|
92
|
-
</svg>
|