dsh-vibe-math 2.8.0 → 2.8.2
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.en.md +2 -2
- package/README.md +2 -2
- package/docs/AUDIT-CHECKLIST.md +8 -3
- package/docs/maintenance-policy.md +31 -0
- package/docs/release-notes/README.en.md +2 -0
- package/docs/release-notes/README.md +2 -0
- package/docs/release-notes/RELEASE-NOTES-2.8.1.en.md +49 -0
- package/docs/release-notes/RELEASE-NOTES-2.8.1.md +49 -0
- package/docs/release-notes/RELEASE-NOTES-2.8.2.en.md +47 -0
- package/docs/release-notes/RELEASE-NOTES-2.8.2.md +47 -0
- package/docs/release-playbook.md +14 -0
- package/docs/slv-playbook.md +5 -1
- package/docs/test-timing.md +1 -1
- package/package.json +11 -3
- package/scripts/release-check.mjs +181 -0
- package/tests/e2e-v5-round2.test.mjs +14 -0
- package/tests/release-check.mutants.mjs +78 -0
- package/tests/v4-final-paper.mutants.mjs +8 -0
- package/tests/v4-final-paper.test.mjs +628 -0
- package/tests/v5-institute-fixes.mutants.mjs +23 -6
- package/vibe-math-v4/vibe-math-v4.js +30 -0
- package/vibe-math-v5/vibe-math-v5.js +27 -1
|
@@ -194,13 +194,30 @@ const FAMILIES = [
|
|
|
194
194
|
to: '只交付 paper.tex 与 paper.md。',
|
|
195
195
|
expect: /\[task-9\] the not-detected warning keeps its tail AND says where it looked/,
|
|
196
196
|
},
|
|
197
|
+
// RETIRED (task-17, 2.8.1 real-host fix): the former entry here mutated `return [forced]` into the merged
|
|
198
|
+
// `[forced].concat(order…)`, which used to let an unresolvable explicit command fall through to a real
|
|
199
|
+
// engine. After task-17 the explicit value is ALSO handed to resolveKnownTool as `explicit` (the explicit
|
|
200
|
+
// branch probes only it and returns early), so the property is now enforced at TWO independent sites and
|
|
201
|
+
// NO single-site mutation can defeat it — the merged list merely probes dead candidates. The assertion
|
|
202
|
+
// itself stays in tests/e2e-v5-round2.test.mjs (`★ [task-10/v5] …degrades`); only the falsifier is retired
|
|
203
|
+
// (and the task-17 entry below covers the hand-off single-site).
|
|
197
204
|
{
|
|
198
|
-
// task-
|
|
199
|
-
//
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
205
|
+
// task-17 (real host, 2.8.1): probing the explicit value as a BARE COMMAND NAME lets the known-root stage
|
|
206
|
+
// join it onto every TeX root, so `triedPaths` reports impossible paths. Reverting the hand-off must
|
|
207
|
+
// redden the guarding assertion by name.
|
|
208
|
+
name: 'task-17: the explicit paperLatexCommand is probed as a bare command name again (triedPaths gets fake joins)',
|
|
209
|
+
from: "explicit: forcedTex, kind: 'tex'",
|
|
210
|
+
to: "explicit: '', kind: 'tex'",
|
|
211
|
+
expect: /\[task-17\/v5\] an ABSOLUTE explicit paperLatexCommand is probed ALONE/,
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
// task-18: the DELIVERED artifacts must say why no PDF was produced (the body is composed before the
|
|
215
|
+
// compile runs, so the note is written after it). Blunting the zh note must redden that guard BY NAME.
|
|
216
|
+
// The scenario that guards this runs with lang='en', so the ENGLISH branch is the one to blunt.
|
|
217
|
+
name: 'task-18: the delivered paper stops stating that no engine was detected',
|
|
218
|
+
from: ": 'No LaTeX engine detected: tex+md only'",
|
|
219
|
+
to: ": ''",
|
|
220
|
+
expect: /\[task-18\/v5\] the DELIVERED paper.md and paper.tex/,
|
|
204
221
|
},
|
|
205
222
|
]
|
|
206
223
|
let red = 0
|
|
@@ -2998,6 +2998,34 @@ try { ok = await wakeResident(r, await heartbeatPrompt(r), 'normal') } catch(e){
|
|
|
2998
2998
|
return await writeText(norm,content)
|
|
2999
2999
|
}
|
|
3000
3000
|
function paperLog(line){ const ps=paperState; if(ps) ps.log.push(fmtTime()+'|'+String(line)) }
|
|
3001
|
+
/**
|
|
3002
|
+
* Compile-outcome note (docs/final-paper.md §D). The paper body is COMPOSED before compilation runs, so
|
|
3003
|
+
* the spec's two conditional bullets in §7 can only fire on a re-entry (where the persisted meta already
|
|
3004
|
+
* carries `compile`). State the outcome in the artifacts the user actually reads instead - ONCE and
|
|
3005
|
+
* idempotently, because finalize can be re-entered. The note text is the SAME string as those bullets, so
|
|
3006
|
+
* the bullet path and this path can never both add it.
|
|
3007
|
+
*/
|
|
3008
|
+
async function paperAnnotateCompileOutcome(ps){
|
|
3009
|
+
const res=ps.compile&&ps.compile.result
|
|
3010
|
+
if(res!=='failed'&&res!=='not-detected') return
|
|
3011
|
+
const zh=ps.cfg.language!=='en'
|
|
3012
|
+
const note=res==='failed'
|
|
3013
|
+
? (zh?'PDF 编译失败:已保留 paper.tex 与 paper.md 并上报':'PDF compilation failed: paper.tex and paper.md were kept and reported')
|
|
3014
|
+
: (zh?'本机未检测到 LaTeX 引擎:只产出 tex+md':'No LaTeX engine detected: tex+md only')
|
|
3015
|
+
if(ps.files.indexOf('paper.md')!==-1){
|
|
3016
|
+
const md=await readText(ps.dir+'/paper.md')
|
|
3017
|
+
if(typeof md==='string'&&md.indexOf(note)===-1) await paperWriteRel(ps.dir+'/paper.md',md.replace(/\s*$/,'')+'\n- '+note+'\n')
|
|
3018
|
+
}
|
|
3019
|
+
if(ps.files.indexOf('paper.tex')!==-1){
|
|
3020
|
+
const tex=await readText(ps.dir+'/paper.tex')
|
|
3021
|
+
if(typeof tex==='string'&&tex.indexOf(note)===-1){
|
|
3022
|
+
const line='% '+note
|
|
3023
|
+
await paperWriteRel(ps.dir+'/paper.tex',tex.indexOf('\\end{document}')!==-1
|
|
3024
|
+
?tex.replace('\\end{document}',line+'\n\\end{document}')
|
|
3025
|
+
:tex.replace(/\s*$/,'')+'\n'+line+'\n')
|
|
3026
|
+
}
|
|
3027
|
+
}
|
|
3028
|
+
}
|
|
3001
3029
|
function paperStepLimit(){ return Math.max(PAPER_WAIT_FLOOR_MS, recoverStallMs()*4) }
|
|
3002
3030
|
function paperCfg(ov){
|
|
3003
3031
|
const o=ov||{}
|
|
@@ -3543,6 +3571,8 @@ try { ok = await wakeResident(r, await heartbeatPrompt(r), 'normal') } catch(e){
|
|
|
3543
3571
|
if(ps.cfg.format!=='md'){ if(await paperWriteRel(ps.dir+'/paper.tex',composed.tex)) ps.files.push('paper.tex') }
|
|
3544
3572
|
ps.compile=await paperCompile(ps,composed,ev)
|
|
3545
3573
|
if(ps.compile&&ps.compile.result==='ok') ps.files.push('paper.pdf')
|
|
3574
|
+
// The body was composed just above, BEFORE this compile ran, so the outcome is stated HERE (idempotent).
|
|
3575
|
+
await paperAnnotateCompileOutcome(ps)
|
|
3546
3576
|
ps.finalizedAt=now(); ps.status='done'
|
|
3547
3577
|
await paperWriteMeta(); await paperWriteLog()
|
|
3548
3578
|
await paperWriteMeta(); await paperWriteLog()
|
|
@@ -5893,8 +5893,13 @@ export function apply(ctx) {
|
|
|
5893
5893
|
}
|
|
5894
5894
|
const engines = []
|
|
5895
5895
|
const triedPaths = [] // task-9: union of the known-root candidates probed across THIS detection
|
|
5896
|
+
// task-17: when paperLatexCommand is set, paperEngineCandidates() returns THAT VALUE ALONE. Probing it as
|
|
5897
|
+
// a bare command name made resolveKnownTool join it onto every known TeX root, so `triedPaths` reported
|
|
5898
|
+
// impossible paths like `<MiKTeX root>/Z:/no/such/xelatex.exe` (observed on a real host with 2.8.1).
|
|
5899
|
+
// Handing it over as the EXPLICIT value probes only it (docs/final-paper.md §B); '' keeps auto-detection.
|
|
5900
|
+
const forcedTex = String(params.paperLatexCommand || '').trim()
|
|
5896
5901
|
for (const name of paperEngineCandidates(lang)) {
|
|
5897
|
-
try { const _vr = await resolveKnownTool(sub, { name, explicit:
|
|
5902
|
+
try { const _vr = await resolveKnownTool(sub, { name, explicit: forcedTex, kind: 'tex' }); triedPaths.push(..._vr.tried); const exe = _vr.exe; if (exe) engines.push({ name: String(name), exe: String(exe), via: _vr.via }) } catch (e) { /* not installed */ }
|
|
5898
5903
|
}
|
|
5899
5904
|
return { engines, triedPaths, reason: engines.length ? '' : ('未检测到 LaTeX 引擎(' + paperEngineCandidates(lang).join('/') + ')') }
|
|
5900
5905
|
}
|
|
@@ -6065,6 +6070,27 @@ export function apply(ctx) {
|
|
|
6065
6070
|
// broken engine. Whatever the compile outcome, the DELIVERED tex must be the real,
|
|
6066
6071
|
// fully-generated one (the attempts are recorded in the meta either way).
|
|
6067
6072
|
if (p.format !== 'md') await writeTextRel(PAPER_FILE(id, 'paper.tex'), tex)
|
|
6073
|
+
// task-18: the body is composed BEFORE compilation runs, so the outcome cannot live in the sections.
|
|
6074
|
+
// v4 states it in the delivered artifacts since 2.8.1; v5 must too — ONCE and idempotently (finalisation
|
|
6075
|
+
// can be re-entered). The marker sentence is the same family as the warning below, and the presence
|
|
6076
|
+
// check keeps the two paths from ever duplicating it.
|
|
6077
|
+
if (compile.status === 'failed' || compile.status === 'not-detected') {
|
|
6078
|
+
const zhNote = p.lang !== 'en'
|
|
6079
|
+
const note = compile.status === 'failed'
|
|
6080
|
+
? (zhNote ? 'PDF 编译失败:已保留 paper.tex 与 paper.md 并上报' : 'PDF compilation failed: paper.tex and paper.md were kept and reported')
|
|
6081
|
+
: (zhNote ? '本机未检测到 LaTeX 引擎:只产出 tex+md' : 'No LaTeX engine detected: tex+md only')
|
|
6082
|
+
const annotate = async (rel, file, isTex) => {
|
|
6083
|
+
const cur = await readTextAbs(paperAbs(id, file))
|
|
6084
|
+
if (typeof cur !== 'string' || cur.indexOf(note) !== -1) return
|
|
6085
|
+
const line = (isTex ? '% ' : '- ') + note
|
|
6086
|
+
const next = isTex && cur.indexOf('\\end{document}') !== -1
|
|
6087
|
+
? cur.replace('\\end{document}', line + '\n\\end{document}')
|
|
6088
|
+
: cur.replace(/\s*$/, '') + '\n' + line + '\n'
|
|
6089
|
+
await writeTextRel(rel, next)
|
|
6090
|
+
}
|
|
6091
|
+
if (p.format !== 'tex') await annotate(PAPER_FILE(id, 'paper.md'), 'paper.md', false)
|
|
6092
|
+
if (p.format !== 'md') await annotate(PAPER_FILE(id, 'paper.tex'), 'paper.tex', true)
|
|
6093
|
+
}
|
|
6068
6094
|
if (await fileExistsAbs(paperAbs(id, 'paper.pdf'))) files.push('paper.pdf')
|
|
6069
6095
|
const warnings = (p.warnings || []).slice()
|
|
6070
6096
|
if (compile.status === 'failed') warnings.push('LaTeX 编译失败(已保留 paper.tex 与 paper.md,不阻塞定稿):' + (compile.attempts || []).map((a) => a.engine + '/' + a.label + ' exit=' + a.exitCode + (a.message ? '(' + a.message + ')' : '')).join(';'))
|