数字我 Digital Me:全量入库(大文件走 Git LFS)
nginx serve /uploads direct (audio speed) / patch-nginx (push) Failing after 1m34s

This commit is contained in:
2026-10-10 16:24:43 +08:00
commit 0593ff7e62
1893 changed files with 361723 additions and 0 deletions
+307
View File
@@ -0,0 +1,307 @@
// ta.js —— 塌。
//
// ★ 本文件的性质,照 caocao.test.js / qian.test.js 的口径:
// 【规矩写在注释里没用,写成断言才算数。】
// ⚠ 而本表是【常量表】,所以断言分两种,别混:
// ① 【体例断言】:本表自己的规矩(股别、卷次、见证数、推演不许安卷次…)—— assertTa()
// ② ★★ 【抄件断言】:本表抄的 dynasties.js 的数据,必须与真数据一致 —— assertSnapshot()
// ⇒ 它的方向是【本表过时了】,✗ 不是「引擎错了」。见 ta.js WHY_NO_IMPORT。
import test from 'node:test';
import assert from 'node:assert/strict';
import {
LING,
THE_VERB_SURVIVED,
THIRD_KIND,
THE_JOKE,
GATE_VS_CEILING,
ONLY_HAN,
ZERO_HITS,
NEW_TRAPS,
HAN_CHANNELS_SNAPSHOT,
WITNESS_NOTE,
lingOf,
zhengwenLing,
peizhuLing,
qiuxianStrata,
yearCoverage,
whatTheyChanged,
assertTa,
assertSnapshot,
} from '../src/ta.js';
import { getDynasty } from '../src/dynasties.js';
import { assertNoGameMechanics } from '../src/decisions.js';
// ══════════════════════════════════════════════════════════════════════════
// 体例
// ══════════════════════════════════════════════════════════════════════════
test('ta:体例自检 —— assertTa() 零违规', () => {
const bad = assertTa();
assert.deepEqual(bad, [], '体例违规:\n' + bad.join('\n'));
});
test('★★ ta:抄件必须与 dynasties.js 的真数据一致 —— 分叉 = 本表过时了,不是引擎错了', () => {
const bad = assertSnapshot(getDynasty('han'));
assert.deepEqual(
bad,
[],
'★ HAN_CHANNELS_SNAPSHOT 与引擎分叉了。\n' +
'⚠ 断言的方向是【本表过时了】—— 去 ta.js 把抄件更新,并【重读所有依赖那个数的话】。\n' +
bad.join('\n')
);
});
test('★ ta:抄件不是 import —— 本表必须是叶子(不 import dynasties.js / caocao.js)', async () => {
const fs = await import('node:fs');
const src = fs.readFileSync(new URL('../src/ta.js', import.meta.url), 'utf8');
// ★ 见 WHY_NO_IMPORT ③:本表也不 import caocao.js —— 两把刀必须【独立地】撞到同一个地方。
assert.equal(/^\s*import\s/m.test(src), false, '★ ta.js 必须是纯常量表,不 import 任何东西(见 WHY_NO_IMPORT)');
});
test('ta:不许有手游机制的字面 —— 数据会走到用户眼前', () => {
// ⚠ lao.js 的教训:为了禁它而提到它,扫描器照样判红 —— 禁令写注释里,数据里一个字都不许有。
const bad = assertNoGameMechanics({
LING, THE_VERB_SURVIVED, THIRD_KIND, THE_JOKE, GATE_VS_CEILING, ONLY_HAN, ZERO_HITS, NEW_TRAPS,
});
assert.deepEqual(bad, [], bad.join('\n'));
});
// ══════════════════════════════════════════════════════════════════════════
// ★★★ 老板问②:求贤令在正文还是裴注 —— 算出来,不靠注释说
// ══════════════════════════════════════════════════════════════════════════
test('★★★ ta:三道求贤令 —— 两道在正文(210/214),一道在裴注(217)', () => {
const s = qiuxianStrata();
assert.deepEqual(
s.rows.map((r) => [r.year, r.kind]),
[[210, 'zhengwen'], [214, 'zhengwen'], [217, 'peizhu']]
);
// ★ 走得最远的那一道,落在注里 —— 与 caocao.js 同形(免/復 只在注里),第二例。
assert.equal(lingOf('qiuxian-217').kind, 'peizhu');
});
test('★★ ta:正文 3 道,裴注 2 道 —— 五道令,一道不多一道不少', () => {
assert.equal(LING.length, 5);
assert.equal(zhengwenLing().length, 3);
assert.equal(peizhuLing().length, 2);
assert.equal(zhengwenLing().length + peizhuLing().length, LING.length, '★ 每一道令都必须有股别');
});
// ══════════════════════════════════════════════════════════════════════════
// ★★★ 老板要的那个数据形状 —— 两栏是空的,而空得有理由
// ══════════════════════════════════════════════════════════════════════════
test('★★★ ta:「天花板变化」全表为 0 —— 这是结论,不是漏填', () => {
const w = whatTheyChanged();
assert.equal(w.changedCeiling, 0, '★★ 五道令一道都没改天花板 —— 改天花板的那一刀是 caocao.js 的 208');
// ★★★ 这个 4 是【断言按住的】,不是手数的:我在注释里数成了 3(把「三道求贤令」
// 和「动闸的令」混成了一个集合),而【庚申令也动闸】(「軍吏……堪任郡國之選」)。
// ⇒ caocao.js 的口径第三次复现:【手数的数一律要有断言看着,否则它一定会漂。】
assert.equal(w.changedGate, 4, '★ 庚申令 / 210 / 214 / 217 四道动的是【闸】');
assert.deepEqual(w.gateIds, ['gengshen-noyear', 'qiuxian-210', 'yiwei-214', 'qiuxian-217']);
// ★ 只剩己酉令不动闸 —— 它是【军法】,不是取士。
assert.equal(lingOf('jiyou-203').gateChange, null);
// ★ 而 assertTa 里有一条断言钉着:ceilingChange 必须恒为 null。
for (const l of LING) assert.equal(l.ceilingChange, null);
});
test('★★★ ta:「起讫年」—— 五道令里,最要紧的那一道【原文没给年】', () => {
const y = yearCoverage();
assert.equal(y.total, 5);
assert.equal(y.undated, 1);
// ★ 没年的那道,正是「治平尚德行,有事賞功能」那一道。
assert.deepEqual(y.undatedIds, ['gengshen-noyear']);
const g = lingOf('gengshen-noyear');
assert.equal(g.year, null);
assert.equal(g.yearKind, 'none');
// ⚠ 不许有人「顺手」把它安成 203(裴注把它排在建安八年己酉令之侧 —— 那是裴松之的编次,不是令的日期)
assert.ok(/裴松之的編次|裴松之的编次/.test(g.yearNote), '★ 必须说明:203 是裴注的位置,不是令的日期');
});
test('★★ ta:有年的四道,全部是「年次是原文的,公元是本表换的」—— 没有一道原文直写年号', () => {
const dated = LING.filter((l) => l.year !== null);
assert.equal(dated.length, 4);
for (const l of dated) {
assert.equal(l.yearKind, 'tuiyan-from-yuanwen', `${l.id}:公元是本表换的,必须标出来`);
assert.ok(l.yearNote, `${l.id}:必须有 yearNote`);
}
});
// ══════════════════════════════════════════════════════════════════════════
// ★★★★★ 头号结论:舉没有停,它的宾语被换掉了
// ══════════════════════════════════════════════════════════════════════════
test('★★★★★ ta:THE_VERB_SURVIVED —— 同一个「舉」字,从「舉孝廉」走到「不仁不孝……其各舉所知」', () => {
const c = THE_VERB_SURVIVED.chain;
assert.equal(c.length, 4);
// ★ 每一步的原文里都必须真的有那个动词 —— 不是我在注释里说它有。
for (const s of c) assert.ok(s.quote.includes('舉'), `这一步的原文里没有「舉」:${s.quote.slice(0, 20)}`);
// ★★ 头尾两步:一个被舉为「孝廉」,一个下令舉「不仁不孝」的人。
assert.ok(c[0].quote.includes('舉孝廉爲郎'));
assert.ok(c[3].quote.includes('不仁不孝'));
assert.ok(c[3].quote.includes('其各舉所知'));
// ★★★ 而最后那一步在【注】里。
assert.equal(c[3].kind, 'peizhu');
assert.equal(c[0].kind, 'zhengwen');
});
test('★★★ ta:「初,公舉种孝廉」—— 他自己成了举主,而这一句在正文', () => {
const c = THE_VERB_SURVIVED.chain[1];
assert.equal(c.kind, 'zhengwen');
assert.ok(c.quote.includes('初,公舉种孝廉'));
assert.ok(c.quote.includes('唯其才也'), '★ 他舉的人叛了他,而他说「唯其才也」,放了他');
// ⚠ 「初」= 追叙 ⇒ 舉的年份原文没给。这一条必须说出来,否则会有人把它读成 199。
assert.ok(/追叙/.test(c.when), '★ 「初」是追叙 —— 199 是生禽种那一年,不是他舉种那一年');
});
// ══════════════════════════════════════════════════════════════════════════
// ★★★★★ 老板的红线 —— 写成断言,不写成祈祷
// ══════════════════════════════════════════════════════════════════════════
test('★★★★★ ta:红线 —— 「天花板塌了 ≠ 你就上得去」,第三档必须自己说出「新尺子上照样有零分」', () => {
const third = THIRD_KIND.kinds.find((k) => k.id === 'reclassified');
assert.ok(third, '★ 缺 reclassified 那一档 —— 那正是本刀的头号结论');
assert.ok(/零分/.test(third.theRedLine), '★ 必须说:一个不会打仗的孝廉,在新尺子上是零分');
assert.ok(/另一張卷子|另一张卷子/.test(third.theRedLine), '★ 必须说:那不是更容易的卷子,是另一张卷子');
assert.ok(/落榜/.test(third.theRedLine), '★ 必须说:他是被重新量了一遍然后落榜了 —— 不是「他上得去」');
});
test('★★★ ta:老板的二分是两档,本表是三档 —— 而第三档是引擎装不下的那一档', () => {
assert.equal(THIRD_KIND.kinds.length, 3);
assert.deepEqual(THIRD_KIND.kinds.map((k) => k.id), ['zero-score', 'no-ruler', 'reclassified']);
// ★ 前两档引擎有(no-ruler 就是 dynasties.js 的 KEMU_ABSENCE.kind);第三档它装不下。
const r = THIRD_KIND.kinds.find((k) => k.id === 'reclassified');
assert.ok(/沒有|没有/.test(r.inEngine), '★ 第三档:引擎没有它,而且是【类型】上没有位置');
});
test('★★★ ta:红线 —— 本表只有汉,一条别朝的都不许有', () => {
for (const l of LING) assert.equal(l.dynastyId, 'han', `★ ${l.id}:不许给别的六朝也编「塌」`);
assert.ok(/本刀沒查那六朝|本刀没查那六朝/.test(ONLY_HAN.why), '★ 必须说「我没查」,不许说「它没有」');
});
// ══════════════════════════════════════════════════════════════════════════
// ★★ 零命中 —— 铁律:零命中比单证更危险
// ══════════════════════════════════════════════════════════════════════════
test('★★ ta:每一条零命中都必须逐字符复核过,并且自己说出【它不说明什么】', () => {
assert.ok(ZERO_HITS.entries.length >= 4);
for (const z of ZERO_HITS.entries) {
assert.ok(z.charCheck, `「${z.word}」:没有逐字符复核`);
assert.ok(z.whatItDoesNotSay, `「${z.word}」:没有说它不说明什么 —— ★ 别反过来说本表无据`);
// ★ 「察舉」那一条尤其:它必须自己说出「舉」字在本卷里其实很多 —— 否则它读起来像「察举没了」。
if (z.word === '察舉') {
assert.ok(/舉」三本【正文各 16/.test(z.charCheck), '★ 必须给出「舉」的单字计数 —— 证明不是检索坏了');
assert.ok(/論文的話|论文的话/.test(z.whatItDoesNotSay), '★ 「察举名存实亡」是论文不是史料 —— 老板点名禁了');
}
}
});
// ══════════════════════════════════════════════════════════════════════════
// ★★ 本轮的三个新坑 —— 教训比结论值钱,所以它们也要被断言看着
// ══════════════════════════════════════════════════════════════════════════
test('★★ ta:NEW_TRAPS —— 四个坑,每个都要说清【怎么发现的】与【怎么修】', () => {
assert.equal(NEW_TRAPS.length, 4);
for (const t of NEW_TRAPS) {
assert.ok(t.what && t.why && t.fix, `坑 ${t.id}:what/why/fix 缺一不可`);
assert.ok('didItBite' in t, `坑 ${t.id}:必须说它有没有咬到人 —— ★ 「没咬到」是运气,不是方法`);
}
});
test('★★★★★ ta:第四个坑 —— 「没有哪个见证是那个可靠的见证」', () => {
const t = NEW_TRAPS.find((x) => x.id === 4);
assert.ok(t, '★ 缺第四个坑 —— 那是本刀最值钱的一条方法论');
// ★ 前五处异文错的都是 S,第六处错的是 T ⇒ 谁开始信任一本,谁就只剩两个见证。
assert.ok(/沒有哪個見證是|没有哪个见证是/.test(t.why));
assert.ok(/只剩兩個見證|只剩两个见证/.test(t.why));
// ⚠ 而它咬到了本表自己的数据(「既」→「旣」)—— 必须承认。
assert.ok(/咬到了/.test(t.didItBite), '★ 它咬到了我自己的 quote —— 不许写成「没咬到」');
});
test('★★★ ta:对照量 —— 三本的「太祖」必须是同一个数(它是切干净了的证据)', () => {
// ★ 切考證之前 S 是 70,W/T 是 62。⇒ 这个 62 就是「S 的第三股已经切掉了」的证据。
assert.ok(/正文 62 \/ 裴注 80/.test(WITNESS_NOTE.control), '★ 对照量必须写死在数据里,不是注释里');
// ⚠ 而它必须自陈它的局限:它对坑①敏感,对坑②(简体 remap)不敏感。
assert.ok(/差點把警報讀成了噪聲|差点把警报读成了噪声/.test(WITNESS_NOTE.control));
});
// ══════════════════════════════════════════════════════════════════════════
// ★★★★ 两把刀合龙:gate 与 ceiling
// ══════════════════════════════════════════════════════════════════════════
test('★★★★ ta:GATE_VS_CEILING —— caocao.js 证了 ceiling,本刀证了 gate,两个都不是常数', () => {
assert.equal(GATE_VS_CEILING.ceiling.provedBy.startsWith('caocao.js'), true);
assert.ok(GATE_VS_CEILING.gate.provedBy.includes('本刀'));
// ★ ceiling 那一条是 caocao.js 的,本表只是引它 —— 引文必须是原文。
assert.ok(GATE_VS_CEILING.ceiling.quote.includes('漢罷三公官'));
assert.ok(GATE_VS_CEILING.gate.quote.includes('上之人不求之耳'));
});
test('★★★ ta:210 年那道令,逐字驳的就是引擎 xiaolian 的 gateNote —— 而两句都对', () => {
const l = lingOf('qiuxian-210');
assert.equal(l.kind, 'zhengwen');
assert.ok(l.quote.includes('上之人不求之耳'));
assert.ok(l.quote.includes('唯才是舉'));
assert.ok(l.quote.includes('若必廉士而後可用,則齊桓其何以霸世'), '★ 老板点名的那一句');
// ★★ 而引擎的 gate 里那一维(wang = 乡论)就在抄件里 —— 它必须真的在。
const x = HAN_CHANNELS_SNAPSHOT.channels.find((c) => c.id === 'xiaolian');
assert.equal(x.gate.wang, 30, '★ 210 年那道令驳的就是这一维');
});
test('★★★ ta:庚申令 —— 「治平尚德行,有事賞功能」= 原文说【标准是时势的函数】,而它在注里', () => {
const g = lingOf('gengshen-noyear');
assert.equal(g.kind, 'peizhu', '★ 它是裴注引王沈《魏書》');
assert.ok(g.quote.includes('治平尚德行,有事賞功能'));
assert.ok(g.quote.includes('郡國之選'), '★ 它吵的正是【谁能被察举】');
// ⚠⚠ 而它必须自己拆自己:最替本刀说话的那句话,出自最偏袒曹操的那个史源。
assert.ok(/回護|回护/.test(g.finding), '★ 王沈《魏書》於曹操多回护 —— 必须说出来');
assert.ok(/他在辯解|他在辩解/.test(g.finding), '★ 这是他在替自己的做法辩护 —— 必须说出来');
});
// ══════════════════════════════════════════════════════════════════════════
// ★★★★★ 216 —— 举主与传主,在塌掉的天花板两边
// ══════════════════════════════════════════════════════════════════════════
test('★★★★★ ta:THE_JOKE —— 「孤今日可復作尉否?」「昔舉大王時,適可作尉耳。」', () => {
assert.equal(THE_JOKE.kind, 'peizhu');
assert.ok(THE_JOKE.quote.includes('孤今日可復作尉否'));
assert.ok(THE_JOKE.quote.includes('昔舉大王時,適可作尉耳'));
assert.ok(THE_JOKE.quote.includes('王大笑'));
assert.equal(THE_JOKE.witnesses.length, 3);
});
test('★★★ ta:裴松之【自己在那条注里做了两证核对】—— 而本表照抄他的判法,不自己裁', () => {
// ★ 他判了两件事,判得不一样:右丞「疑此不然」;舉北部尉「如此則爲有徵」。
assert.ok(THE_JOKE.quote.includes('疑此不然'));
assert.ok(THE_JOKE.quote.includes('如此則爲有徵'));
assert.ok(/一千六百年/.test(THE_JOKE.peiSongzhiDidOurJob), '★ 他做的正是本项目在做的事');
});
test('★★★ ta:那条注顺手证明 —— 曹操第一格的【举主】,原文给了两个人,裴松之只考了一个', () => {
assert.ok(THE_JOKE.quote.includes('梁鵠以公爲北部尉'));
assert.ok(THE_JOKE.quote.includes('司馬建公所舉'));
// ★ 而本表不替裴松之裁「梁鵠」那一说。
assert.ok(/本表【也不替他裁】/.test(THE_JOKE.twoJuzhu));
});
// ══════════════════════════════════════════════════════════════════════════
// 函数:不炸
// ══════════════════════════════════════════════════════════════════════════
test('ta:纯函数 —— 非法输入不炸,查不到返回 null(不许猜)', () => {
assert.equal(lingOf('没有这道令'), null);
assert.equal(lingOf(null), null);
assert.equal(lingOf(undefined), null);
assert.deepEqual(assertSnapshot(null), ['没拿到 dynasties.js 的汉 —— 无法验证 HAN_CHANNELS_SNAPSHOT']);
assert.ok(Array.isArray(assertSnapshot({ era: 'x', span: 'y', channels: [] })));
});
test('ta:常量表必须是冻的 —— 证词不许被人改', () => {
assert.equal(Object.isFrozen(LING), true);
assert.equal(Object.isFrozen(HAN_CHANNELS_SNAPSHOT), true);
assert.equal(Object.isFrozen(THE_VERB_SURVIVED), true);
for (const l of LING) assert.equal(Object.isFrozen(l), true, `${l.id} 没冻`);
});