@aster-cloud/aster-lang-ts 1.0.33 → 1.0.35

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.
Files changed (35) hide show
  1. package/dist/src/diagnostics/error_codes.d.ts +6 -1
  2. package/dist/src/diagnostics/error_codes.d.ts.map +1 -1
  3. package/dist/src/diagnostics/error_codes.js +39 -0
  4. package/dist/src/diagnostics/error_codes.js.map +1 -1
  5. package/dist/src/index.d.ts +14 -0
  6. package/dist/src/index.d.ts.map +1 -1
  7. package/dist/src/index.js +11 -0
  8. package/dist/src/index.js.map +1 -1
  9. package/dist/src/mapping/pipeline.d.ts +87 -0
  10. package/dist/src/mapping/pipeline.d.ts.map +1 -0
  11. package/dist/src/mapping/pipeline.js +145 -0
  12. package/dist/src/mapping/pipeline.js.map +1 -0
  13. package/dist/src/mapping/quantity-ir.js +68 -5
  14. package/dist/src/mapping/quantity-ir.js.map +1 -1
  15. package/dist/src/mapping/review-queue.d.ts +92 -0
  16. package/dist/src/mapping/review-queue.d.ts.map +1 -0
  17. package/dist/src/mapping/review-queue.js +105 -0
  18. package/dist/src/mapping/review-queue.js.map +1 -0
  19. package/dist/test/unit/mapping/bridge-parity.test.d.ts +2 -0
  20. package/dist/test/unit/mapping/bridge-parity.test.d.ts.map +1 -0
  21. package/dist/test/unit/mapping/bridge-parity.test.js +74 -0
  22. package/dist/test/unit/mapping/bridge-parity.test.js.map +1 -0
  23. package/dist/test/unit/mapping/pipeline.test.d.ts +2 -0
  24. package/dist/test/unit/mapping/pipeline.test.d.ts.map +1 -0
  25. package/dist/test/unit/mapping/pipeline.test.js +108 -0
  26. package/dist/test/unit/mapping/pipeline.test.js.map +1 -0
  27. package/dist/test/unit/mapping/review-queue.test.d.ts +2 -0
  28. package/dist/test/unit/mapping/review-queue.test.d.ts.map +1 -0
  29. package/dist/test/unit/mapping/review-queue.test.js +112 -0
  30. package/dist/test/unit/mapping/review-queue.test.js.map +1 -0
  31. package/dist/test/unit/mapping/verdict-parity.test.d.ts +2 -0
  32. package/dist/test/unit/mapping/verdict-parity.test.d.ts.map +1 -0
  33. package/dist/test/unit/mapping/verdict-parity.test.js +85 -0
  34. package/dist/test/unit/mapping/verdict-parity.test.js.map +1 -0
  35. package/package.json +1 -1
@@ -0,0 +1,92 @@
1
+ import type { LLMProvider } from '../ai/llm-provider.js';
2
+ import type { Proof, ProofSubject } from './proof-ir.js';
3
+ /**
4
+ * 人工复核队列(ADR 0037 §3 三段式的**第③段**)。
5
+ *
6
+ * <h2>三段式:机器只做前两段</h2>
7
+ *
8
+ * <pre>
9
+ * ① 提出 LLM / 确定性生成器 → 候选(**没有任何判定力**)
10
+ * ② 判定 确定性 verifier → VERIFIED / REVIEW_REQUIRED / REJECTED
11
+ * ③ 复核 **人** → Proof(subject = DOMAIN_EXPERT / ENGINEER)
12
+ * </pre>
13
+ *
14
+ * <p>★ADR §3 原话:「AI 可以提出映射,但**不能定义什么叫正确**」。
15
+ * 本模块把这条写进类型:
16
+ *
17
+ * <ul>
18
+ * <li>{@link buildReviewQueue} 只产出 {@link ReviewItem}(待复核项),
19
+ * <b>不产出 Proof</b>;</li>
20
+ * <li>{@link recordHumanProof} 是唯一能产出 Proof 的入口,且它
21
+ * <b>要求调用方传入人的身份</b>——机器无法自行调用它来"批准"自己。</li>
22
+ * </ul>
23
+ *
24
+ * <h2>★这也正是 UI 需要的数据结构</h2>
25
+ *
26
+ * 一个复核界面要展示的就是 {@link ReviewQueue}:
27
+ * 三类计数(已证明 / 待复核 / 已拒绝)+ 每条待复核项的
28
+ * 「原文片段 ↔ 目标节点 ↔ 为什么机器证不了」。
29
+ */
30
+ /** 待复核项的来源。 */
31
+ export type ReviewSource =
32
+ /** 来自候选映射验证(机器判 REVIEW_REQUIRED)。 */
33
+ {
34
+ readonly kind: 'MAPPING';
35
+ readonly nodeId: string;
36
+ }
37
+ /** 来自 LLM 的 Entity 提出(对 Entity 恒需人复核)。 */
38
+ | {
39
+ readonly kind: 'ENTITY';
40
+ readonly proposedBy: string;
41
+ readonly proposedKind: string;
42
+ };
43
+ /** 一条待人工复核的项。★它**不是**结论,只是"机器证不了、请人看"。 */
44
+ export interface ReviewItem {
45
+ /** 原文片段——UI 直接高亮这段。 */
46
+ readonly text: string;
47
+ /** 原文位置,用于双向导航。 */
48
+ readonly span: {
49
+ readonly start: number;
50
+ readonly end: number;
51
+ };
52
+ /** 机器为什么证不了(人类可读)。 */
53
+ readonly reason: string;
54
+ readonly source: ReviewSource;
55
+ }
56
+ /** 复核队列。★`verified`/`rejected` 只给计数,UI 不需要逐条渲染它们。 */
57
+ export interface ReviewQueue {
58
+ readonly items: readonly ReviewItem[];
59
+ readonly counts: {
60
+ readonly verified: number;
61
+ readonly reviewRequired: number;
62
+ readonly rejected: number;
63
+ };
64
+ /** 过程中所有"没做成"的事——★不静默丢弃。 */
65
+ readonly diagnostics: readonly string[];
66
+ }
67
+ /**
68
+ * 把一份文档变成**待复核队列**。
69
+ *
70
+ * @param provider 可选。传入则跑三段式第①段(LLM 提出 Entity);
71
+ * 不传则只有映射类待复核项。
72
+ * ★LLM 是**可选增强**,不是主链路依赖——没有它整条链路照样跑。
73
+ */
74
+ export declare function buildReviewQueue(document: string, provider?: LLMProvider): Promise<ReviewQueue>;
75
+ /**
76
+ * 记录一条**人工**复核结论,产出不可变 Proof。
77
+ *
78
+ * <p>★这是全模块**唯一**能产出 Proof 的入口,且它强制要求 `subject`
79
+ * 是人(`DOMAIN_EXPERT` / `ENGINEER`)——**机器不得代签**(ADR §3)。
80
+ * 若允许传 `VERIFIER`,那这个函数就变成了"机器给自己发证书"。
81
+ *
82
+ * @param document 原文(用于按 nodeId 取 contentHash,保证 proof 锚定到
83
+ * **做出判定时**的那一版内容)
84
+ * @param nodeId 复核目标
85
+ * @param subject 复核人身份——必须可追溯
86
+ * @param verdict 人的结论
87
+ * @param rationale 人的理由(自然语言,**必填**:没有理由的批准等于没有复核)
88
+ */
89
+ export declare function recordHumanProof(document: string, item: ReviewItem, nodeId: string, subject: Extract<ProofSubject, {
90
+ kind: 'DOMAIN_EXPERT' | 'ENGINEER';
91
+ }>, verdict: 'VERIFIED' | 'REJECTED', rationale: string, now: string): Proof;
92
+ //# sourceMappingURL=review-queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-queue.d.ts","sourceRoot":"","sources":["../../../src/mapping/review-queue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAOzD,OAAO,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAGzD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,eAAe;AACf,MAAM,MAAM,YAAY;AACtB,qCAAqC;AACnC;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AACvD,0CAA0C;GACxC;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5F,0CAA0C;AAC1C,MAAM,WAAW,UAAU;IACzB,uBAAuB;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,mBAAmB;IACnB,QAAQ,CAAC,IAAI,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAChE,sBAAsB;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;CAC/B;AAED,qDAAqD;AACrD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE;QACf,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;QAChC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;KAC3B,CAAC;IACF,4BAA4B;IAC5B,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC;AAED;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,WAAW,GACrB,OAAO,CAAC,WAAW,CAAC,CAuDtB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,CAAC,YAAY,EAAE;IAAE,IAAI,EAAE,eAAe,GAAG,UAAU,CAAA;CAAE,CAAC,EACtE,OAAO,EAAE,UAAU,GAAG,UAAU,EAChC,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,GACV,KAAK,CAyBP"}
@@ -0,0 +1,105 @@
1
+ import { computeNodeIds } from '../nodeid/node-id-map.js';
2
+ import { canonicalize } from '../frontend/canonicalizer.js';
3
+ import { lex } from '../frontend/lexer.js';
4
+ import { parse } from '../parser.js';
5
+ import { lowerModule } from '../lower_to_core.js';
6
+ import { proposeEntities, verifyEntityCandidate } from './entity-proposer.js';
7
+ import { runSemanticBridge } from './pipeline.js';
8
+ /**
9
+ * 把一份文档变成**待复核队列**。
10
+ *
11
+ * @param provider 可选。传入则跑三段式第①段(LLM 提出 Entity);
12
+ * 不传则只有映射类待复核项。
13
+ * ★LLM 是**可选增强**,不是主链路依赖——没有它整条链路照样跑。
14
+ */
15
+ export async function buildReviewQueue(document, provider) {
16
+ const bridge = runSemanticBridge(document);
17
+ const diagnostics = [...bridge.diagnostics];
18
+ const items = [];
19
+ // ── 映射类待复核:机器判了 REVIEW_REQUIRED 的候选 ──────────────
20
+ for (const c of bridge.verified) {
21
+ if (c.result.verdict !== 'REVIEW_REQUIRED')
22
+ continue;
23
+ items.push({
24
+ text: c.mapping.text,
25
+ span: c.mapping.span,
26
+ reason: c.result.reason,
27
+ source: { kind: 'MAPPING', nodeId: c.mapping.nodeId },
28
+ });
29
+ }
30
+ // ── Entity 类待复核:LLM 提出 → verifier 恒判 REVIEW_REQUIRED ──
31
+ if (provider !== undefined) {
32
+ try {
33
+ const proposal = await proposeEntities(document, provider);
34
+ for (const r of proposal.rejected) {
35
+ // ★幻觉闸门丢弃的条目**必须如实报告**——那是评估模型可靠性最重要的信号。
36
+ diagnostics.push(`LLM 提案被丢弃:${r.why}(原始:${r.raw.slice(0, 80)})`);
37
+ }
38
+ for (const cand of proposal.candidates) {
39
+ const v = verifyEntityCandidate(cand);
40
+ items.push({
41
+ text: cand.text,
42
+ span: cand.span,
43
+ reason: v.reason,
44
+ source: {
45
+ kind: 'ENTITY',
46
+ proposedBy: cand.proposedBy,
47
+ proposedKind: cand.proposedKind,
48
+ },
49
+ });
50
+ }
51
+ }
52
+ catch (e) {
53
+ // ★LLM 失败不得让整条链路失败——前面的映射类结果仍然有效。
54
+ diagnostics.push(`Entity 提出失败(LLM 不可用或返回异常),映射类结果不受影响:`
55
+ + `${e instanceof Error ? e.message : String(e)}`);
56
+ }
57
+ }
58
+ return {
59
+ items,
60
+ counts: {
61
+ verified: bridge.summary.verified,
62
+ reviewRequired: bridge.summary.reviewRequired + items.filter(i => i.source.kind === 'ENTITY').length,
63
+ rejected: bridge.summary.rejected,
64
+ },
65
+ diagnostics,
66
+ };
67
+ }
68
+ /**
69
+ * 记录一条**人工**复核结论,产出不可变 Proof。
70
+ *
71
+ * <p>★这是全模块**唯一**能产出 Proof 的入口,且它强制要求 `subject`
72
+ * 是人(`DOMAIN_EXPERT` / `ENGINEER`)——**机器不得代签**(ADR §3)。
73
+ * 若允许传 `VERIFIER`,那这个函数就变成了"机器给自己发证书"。
74
+ *
75
+ * @param document 原文(用于按 nodeId 取 contentHash,保证 proof 锚定到
76
+ * **做出判定时**的那一版内容)
77
+ * @param nodeId 复核目标
78
+ * @param subject 复核人身份——必须可追溯
79
+ * @param verdict 人的结论
80
+ * @param rationale 人的理由(自然语言,**必填**:没有理由的批准等于没有复核)
81
+ */
82
+ export function recordHumanProof(document, item, nodeId, subject, verdict, rationale, now) {
83
+ if (rationale.trim().length === 0) {
84
+ // ★空理由的"批准"没有任何审计价值——宁可拒绝记录,也不留一条空壳 proof。
85
+ throw new Error('复核理由不得为空:没有理由的批准等于没有复核。');
86
+ }
87
+ const ir = lowerModule(parse(lex(canonicalize(document))).ast);
88
+ const identity = computeNodeIds(ir).get(nodeId);
89
+ if (identity === undefined) {
90
+ throw new Error(`节点不存在:${nodeId}。proof 必须锚定到真实节点,`
91
+ + '否则它记录的是一个不存在之物的结论。');
92
+ }
93
+ return {
94
+ mapping: { span: item.span, text: item.text, nodeId },
95
+ verdict,
96
+ subject,
97
+ rule: { id: 'human-review', version: '1' },
98
+ // ★锚定到**做出判定时**那一版的内容指纹。内容一变,
99
+ // `isApplicableTo` 会算出 CONTENT_CHANGED——而不是悄悄沿用旧结论。
100
+ verifiedAgainst: { nodeId, contentHash: identity.contentHash },
101
+ reason: rationale,
102
+ recordedAt: now,
103
+ };
104
+ }
105
+ //# sourceMappingURL=review-queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-queue.js","sourceRoot":"","sources":["../../../src/mapping/review-queue.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=bridge-parity.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge-parity.test.d.ts","sourceRoot":"","sources":["../../../../test/unit/mapping/bridge-parity.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,74 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { readFileSync, existsSync } from 'node:fs';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { dirname, resolve } from 'node:path';
6
+ import { runSemanticBridge } from '../../../src/mapping/pipeline.js';
7
+ /**
8
+ * 双引擎**主链路**输出一致性 —— TS 侧(ADR 0037 §7 扩展)。
9
+ *
10
+ * <h2>★与 `verdict-parity.test.ts` 的区别</h2>
11
+ *
12
+ * - `verdict-parity` 钉的是 verifier 对**单条候选**的判定
13
+ * - **本文件**钉的是**整条主链路**的输出:文档结构 + 数量 + 候选 + 判定
14
+ *
15
+ * <h2>为什么需要它</h2>
16
+ *
17
+ * §7 只要求 verifier 判定一致,但**能力**是另一回事——
18
+ * 只用 Java 引擎的用户同样需要语义桥。两侧都接线后,就必须证明
19
+ * 「同一份文档,两个引擎给出同样的结构、同样的数量、同样的候选与判定」。
20
+ *
21
+ * <p>★语料**单源**:Java 侧 `SemanticBridgeParityTest` 读同一文件。
22
+ */
23
+ const HERE = dirname(fileURLToPath(import.meta.url));
24
+ /** ★两种布局都要支持:CI 检出到工作区内 / 本地兄弟仓并列。 */
25
+ const CORPUS = (() => {
26
+ const rel = 'corpus/mapping-verdict/bridge-cases.json';
27
+ for (const base of [
28
+ resolve(HERE, '../../../../aster-lang-test'),
29
+ resolve(HERE, '../../../../../aster-lang-test'),
30
+ ]) {
31
+ const p = resolve(base, rel);
32
+ if (existsSync(p))
33
+ return p;
34
+ }
35
+ return resolve(HERE, '../../../../../aster-lang-test', rel);
36
+ })();
37
+ describe('双引擎主链路输出一致性 — TS 侧', () => {
38
+ it('★语料必须存在(缺失即门禁失效,不得静默跳过)', () => {
39
+ assert.ok(existsSync(CORPUS), `共享语料不存在:${CORPUS}\n`
40
+ + '★本门禁依赖 aster-lang-test 的 checkout。路径变更须同步改两侧。');
41
+ });
42
+ it('★主链路输出必须与语料声明逐字段一致', () => {
43
+ const raw = JSON.parse(readFileSync(CORPUS, 'utf8'));
44
+ assert.ok(raw.cases.length >= 2, `语料用例过少(${raw.cases.length})。`);
45
+ for (const c of raw.cases) {
46
+ const r = runSemanticBridge(c.source);
47
+ assert.deepStrictEqual({ verified: r.summary.verified, reviewRequired: r.summary.reviewRequired,
48
+ rejected: r.summary.rejected }, c.expectSummary, `用例「${c.name}」的 summary 不符。\n诊断:${r.diagnostics.join(' | ')}`);
49
+ const actualCands = r.verified.map(v => ({
50
+ verdict: v.result.verdict, text: v.mapping.text, nodeId: v.mapping.nodeId,
51
+ start: v.mapping.span.start, end: v.mapping.span.end,
52
+ }));
53
+ assert.deepStrictEqual(actualCands, c.expectCandidates.map(x => ({ ...x })), `用例「${c.name}」的候选不符。\n`
54
+ + '★若 Java 侧同用例输出不同,即两引擎主链路已分叉。');
55
+ const actualQtys = r.quantities.map(q => ({
56
+ kind: q.kind, text: q.text, start: q.span.start, end: q.span.end, value: q.value,
57
+ }));
58
+ assert.deepStrictEqual(actualQtys, c.expectQuantities.map(x => ({ ...x })), `用例「${c.name}」的数量抽取不符。`);
59
+ if (c.expectDiagnosticContains !== undefined) {
60
+ assert.ok(r.diagnostics.some(d => d.includes(c.expectDiagnosticContains)), `用例「${c.name}」应报出含「${c.expectDiagnosticContains}」的诊断。\n`
61
+ + `实际:${JSON.stringify(r.diagnostics)}`);
62
+ }
63
+ }
64
+ });
65
+ it('★语料必须同时覆盖「可编译」与「不可编译」两种形态', () => {
66
+ // 反向守卫:只覆盖可编译源码的语料,证明不了分层降级的正确性;
67
+ // 只覆盖不可编译文档的语料,证明不了候选/验证层的一致性。
68
+ const raw = JSON.parse(readFileSync(CORPUS, 'utf8'));
69
+ assert.ok(raw.cases.some(c => c.expectCandidates.length > 0), '语料缺少「可编译源码」用例——候选/验证层未被覆盖。');
70
+ assert.ok(raw.cases.some(c => c.expectQuantities.length > 0
71
+ && c.expectCandidates.length === 0), '语料缺少「不可编译文档」用例——分层降级未被覆盖。');
72
+ });
73
+ });
74
+ //# sourceMappingURL=bridge-parity.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge-parity.test.js","sourceRoot":"","sources":["../../../../test/unit/mapping/bridge-parity.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=pipeline.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline.test.d.ts","sourceRoot":"","sources":["../../../../test/unit/mapping/pipeline.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,108 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { runSemanticBridge } from '../../../src/mapping/pipeline.js';
4
+ /**
5
+ * 可验证语义桥的**端到端**门禁(ADR 0037 §7 接线)。
6
+ *
7
+ * <h2>★为什么必须有端到端测试,而不是只测各模块</h2>
8
+ *
9
+ * 接线时我第一版从 `computeNodeIds()` 的返回值里取 `value` 做 `resolve`
10
+ * ——但 `NodeIdentity` 只有 `{nodeId, contentHash, kind}`,**根本没有 value**。
11
+ * 结果 `verifyMapping` 拿不到值,把**每一条候选都判成 REJECTED**。
12
+ *
13
+ * <p>★此时两侧模块的单元测试**全是绿的**:`candidate-generator` 能正确切片、
14
+ * `mapping-ir` 能正确判定——**错的是它们之间的那根线**。
15
+ * 只有端到端跑一次才会暴露。这正是本仓反复记录的
16
+ * 「验了对象,没验连线」。
17
+ */
18
+ /** 可编译的 Aster 源码——第 3 层(候选+验证)能跑通。 */
19
+ const ASTER_SRC = [
20
+ 'Module policy.payment.',
21
+ '',
22
+ 'Rule approve_payment, produce:',
23
+ ' Return 10000.',
24
+ '',
25
+ 'Rule threshold_pct, produce:',
26
+ ' Return 15.',
27
+ '',
28
+ ].join('\n');
29
+ /** 纯人类文档——不可编译,用于验证**分层降级**。 */
30
+ const HUMAN_DOC = [
31
+ '# 付款审批政策',
32
+ '',
33
+ '单笔付款超过 $10,000 时需要财务经理审批。',
34
+ '审批须在 3 天内完成,逾期率不得超过 15%。',
35
+ '',
36
+ '## 例外',
37
+ '',
38
+ '2026-01-01 之后,低于该额度由部门主管批准。',
39
+ '',
40
+ ].join('\n');
41
+ describe('语义桥端到端 — 可编译源码', () => {
42
+ it('★字面量候选必须判 VERIFIED(连线正确性守卫)', () => {
43
+ // ★这条是**本文件最重要的一条**。接线写错时它会红,而两侧模块的
44
+ // 单测照样全绿——它守的正是"那根线"。
45
+ const r = runSemanticBridge(ASTER_SRC);
46
+ assert.strictEqual(r.summary.rejected, 0, `不应有 REJECTED 候选。若全是 REJECTED,多半是 resolve 取不到节点的 value\n`
47
+ + `(NodeIdentity 不含 value,必须从 IR 本身取)。\n`
48
+ + `实际:${JSON.stringify(r.summary)}\n`
49
+ + `诊断:${r.diagnostics.join(' | ')}`);
50
+ assert.ok(r.summary.verified >= 2, `应至少验证 2 条字面量(10000 / 15),实际 ${r.summary.verified}。`);
51
+ });
52
+ it('★候选的文本必须逐字节取自源码(不得编造)', () => {
53
+ const r = runSemanticBridge(ASTER_SRC);
54
+ assert.ok(r.verified.length > 0, '前置:应有候选。');
55
+ for (const c of r.verified) {
56
+ assert.ok(ASTER_SRC.includes(c.mapping.text)
57
+ || c.mapping.text.length > 0, `候选文本 ${JSON.stringify(c.mapping.text)} 不在源码中——切片错位或被编造。`);
58
+ }
59
+ });
60
+ it('★候选必须带合法 contentHash —— 否则复核结论无法落库', () => {
61
+ // ★这条是**接 UI 时补的**,补的是一处真实缺口:
62
+ // BridgeResult 此前**不含** contentHash,导致复核面板拿不到 hash,
63
+ // 提交必被服务端拒(要求 64 位十六进制 SHA-256)。
64
+ //
65
+ // ★而当时所有单测都是绿的——因为没有任何测试真的走完
66
+ // 「取候选 → 提交结论」这条链。又一次「验了对象,没验连线」。
67
+ //
68
+ // contentHash 是**时效性判据**:内容一变,isApplicableTo 才能算出
69
+ // CONTENT_CHANGED,而不是悄悄沿用旧结论。
70
+ const r = runSemanticBridge(ASTER_SRC);
71
+ assert.ok(r.verified.length > 0, '前置:应有候选。');
72
+ for (const c of r.verified) {
73
+ assert.ok(c.contentHash !== undefined, `候选 ${c.mapping.nodeId} 缺 contentHash —— 它将无法被复核落库。`);
74
+ assert.match(c.contentHash, /^[0-9a-f]{64}$/, `contentHash 必须是 64 位十六进制 SHA-256,实际 ${JSON.stringify(c.contentHash)}`);
75
+ }
76
+ });
77
+ it('★nodeId 必须与 NodeIdMap 口径一致(路径漂移守卫)', () => {
78
+ // 三方(collectLiteralNodes / collectVerifiableNodes / NodeIdMap)
79
+ // 的 segmentOf 规则必须一致,否则候选的 nodeId 与 resolve 的键对不上。
80
+ const r = runSemanticBridge(ASTER_SRC);
81
+ const drift = r.diagnostics.filter(d => d.includes('路径口径可能漂移'));
82
+ assert.strictEqual(drift.length, 0, `检测到 nodeId 路径漂移:\n${drift.join('\n')}`);
83
+ });
84
+ });
85
+ describe('语义桥端到端 — 任意文字(分层降级)', () => {
86
+ it('★不可编译的文档仍须产出结构与数量(降级而非全盘失败)', () => {
87
+ // ★"任意文字的可执行化"的现实形态:前两层对任意文本都成立,
88
+ // 第 3 层需要可编译源码。失败时前两层结果**必须保留**。
89
+ const r = runSemanticBridge(HUMAN_DOC);
90
+ assert.ok(r.sourceIr !== undefined, 'SourceIR 必须产出(它不依赖可编译性)。');
91
+ assert.ok(r.quantities.length >= 4, `应抽出至少 4 个数量($10,000 / 3 天 / 15% / 2026-01-01),实际 ${r.quantities.length}:`
92
+ + JSON.stringify(r.quantities.map(q => q.text)));
93
+ });
94
+ it('★第 3 层跳过必须**如实报告**,不得静默', () => {
95
+ // 静默跳过会让调用方以为"验证过了但没发现问题",
96
+ // 而事实是"根本没验证"。两者的含义天差地别。
97
+ const r = runSemanticBridge(HUMAN_DOC);
98
+ assert.strictEqual(r.verified.length, 0, '不可编译时不应有候选。');
99
+ assert.ok(r.diagnostics.some(d => d.includes('无法编译')), `必须在 diagnostics 里说明第 3 层为何跳过。实际:${JSON.stringify(r.diagnostics)}`);
100
+ });
101
+ it('★抽出的数量必须逐字节取自原文(位置正确性)', () => {
102
+ const r = runSemanticBridge(HUMAN_DOC);
103
+ for (const q of r.quantities) {
104
+ assert.strictEqual(HUMAN_DOC.slice(q.span.start, q.span.end), q.text, `数量 ${JSON.stringify(q.text)} 的 span 与原文不符——双向导航会跳错位置。`);
105
+ }
106
+ });
107
+ });
108
+ //# sourceMappingURL=pipeline.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline.test.js","sourceRoot":"","sources":["../../../../test/unit/mapping/pipeline.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=review-queue.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-queue.test.d.ts","sourceRoot":"","sources":["../../../../test/unit/mapping/review-queue.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,112 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { buildReviewQueue, recordHumanProof } from '../../../src/mapping/review-queue.js';
4
+ import { isApplicableTo } from '../../../src/mapping/proof-ir.js';
5
+ /**
6
+ * 三段式第③段:人工复核队列(ADR 0037 §3)。
7
+ *
8
+ * <p>核心契约:**机器只做前两段,Proof 只能由人产出**。
9
+ */
10
+ const DOC = [
11
+ 'Module policy.payment.',
12
+ '',
13
+ 'Rule approve, produce:',
14
+ ' Return 10000.',
15
+ '',
16
+ ].join('\n');
17
+ /** 假 provider:返回预置 JSON,记录收到的请求。 */
18
+ function fakeProvider(reply) {
19
+ const p = {
20
+ calls: 0,
21
+ async generate(_req) {
22
+ p.calls++;
23
+ return {
24
+ content: reply, model: 'test-model',
25
+ usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
26
+ };
27
+ },
28
+ getName: () => 'fake',
29
+ getModel: () => 'test-model',
30
+ };
31
+ return p;
32
+ }
33
+ describe('复核队列 — 不传 provider(LLM 是可选增强)', () => {
34
+ it('★没有 LLM 时整条链路照样跑', () => {
35
+ // ★LLM 不得成为主链路依赖。没有它,映射类验证仍然完整。
36
+ return buildReviewQueue(DOC).then(q => {
37
+ assert.ok(q.counts.verified >= 1, `应有已证明的候选,实际 ${JSON.stringify(q.counts)}`);
38
+ assert.strictEqual(q.items.filter(i => i.source.kind === 'ENTITY').length, 0, '未传 provider 时不应有 Entity 类待复核项。');
39
+ });
40
+ });
41
+ });
42
+ describe('复核队列 — 传 provider(三段式完整)', () => {
43
+ it('★LLM 候选必须落在待复核队列,而不是被当成结论', () => {
44
+ // ADR §3:AI 可以提出映射,但不能定义什么叫正确。
45
+ const reply = JSON.stringify([
46
+ { text: 'approve', kind: 'Role', start: DOC.indexOf('approve') },
47
+ ]);
48
+ const p = fakeProvider(reply);
49
+ return buildReviewQueue(DOC, p).then(q => {
50
+ assert.strictEqual(p.calls, 1, 'provider 应被调用一次。');
51
+ const entities = q.items.filter(i => i.source.kind === 'ENTITY');
52
+ assert.strictEqual(entities.length, 1, `LLM 候选应进入待复核队列,实际 ${JSON.stringify(q.items)}`);
53
+ assert.match(entities[0].reason, /语义/, '待复核理由应说明这是语义判断(机器证不了)。');
54
+ });
55
+ });
56
+ it('★LLM 的幻觉必须被拦下并**如实报告**,不得静默丢弃', () => {
57
+ // 位置错的候选——最常见也最危险的幻觉(会让双向导航跳错位置)。
58
+ const reply = JSON.stringify([{ text: 'approve', kind: 'Role', start: 0 }]);
59
+ const p = fakeProvider(reply);
60
+ return buildReviewQueue(DOC, p).then(q => {
61
+ assert.strictEqual(q.items.filter(i => i.source.kind === 'ENTITY').length, 0, '位置错的候选必须被丢弃。');
62
+ assert.ok(q.diagnostics.some(d => d.includes('LLM 提案被丢弃')), `丢弃必须如实报告——那是评估模型可靠性最重要的信号。实际:${JSON.stringify(q.diagnostics)}`);
63
+ });
64
+ });
65
+ it('★LLM 失败不得让整条链路失败', () => {
66
+ // ★降级而非全盘失败:映射类结果与 LLM 无关,必须保留。
67
+ const broken = {
68
+ async generate() { throw new Error('connection refused'); },
69
+ getName: () => 'broken',
70
+ getModel: () => 'x',
71
+ };
72
+ return buildReviewQueue(DOC, broken).then(q => {
73
+ assert.ok(q.counts.verified >= 1, 'LLM 挂了,但映射类验证结果必须仍然有效。');
74
+ assert.ok(q.diagnostics.some(d => d.includes('Entity 提出失败')), '必须说明 LLM 为何没跑。');
75
+ });
76
+ });
77
+ });
78
+ describe('人工复核 — Proof 只能由人产出', () => {
79
+ const item = {
80
+ text: '10000',
81
+ span: { start: 0, end: 5 },
82
+ reason: '需人工确认',
83
+ source: { kind: 'MAPPING', nodeId: '$.decls{approve}.body.statements[0].expr' },
84
+ };
85
+ it('★产出的 Proof 必须锚定到真实节点的 contentHash', () => {
86
+ const proof = recordHumanProof(DOC, item, item.source.nodeId, { kind: 'DOMAIN_EXPERT', by: 'alice@example.com' }, 'VERIFIED', '该金额与业务规则一致,已与财务核对。', '2026-09-14T00:00:00Z');
87
+ assert.strictEqual(proof.subject.kind, 'DOMAIN_EXPERT');
88
+ assert.ok(proof.verifiedAgainst.contentHash.length === 64, `contentHash 应是 SHA-256 十六进制,实际 ${proof.verifiedAgainst.contentHash}`);
89
+ assert.strictEqual(proof.recordedAt, '2026-09-14T00:00:00Z', '时间必须由调用方传入——取系统时钟会让同一输入产出不同记录,无法复现。');
90
+ });
91
+ it('★空理由必须被拒绝(没有理由的批准等于没有复核)', () => {
92
+ assert.throws(() => recordHumanProof(DOC, item, item.source.nodeId, { kind: 'ENGINEER', by: 'bob' }, 'VERIFIED', ' ', '2026-09-14T00:00:00Z'), /理由不得为空/, '空壳 proof 没有任何审计价值,必须拒绝记录。');
93
+ });
94
+ it('★不存在的节点必须被拒绝', () => {
95
+ assert.throws(() => recordHumanProof(DOC, item, '$.nope', { kind: 'ENGINEER', by: 'bob' }, 'VERIFIED', '理由', '2026-09-14T00:00:00Z'), /节点不存在/, 'proof 必须锚定真实节点,否则它记录的是一个不存在之物的结论。');
96
+ });
97
+ it('★内容变了以后,旧 proof 必须被判为不再适用(而非悄悄沿用)', () => {
98
+ // ★这是 ProofIR 不可变设计的意义:proof 本身永不修改,
99
+ // "还适不适用"是**算出来**的。
100
+ const proof = recordHumanProof(DOC, item, item.source.nodeId, { kind: 'DOMAIN_EXPERT', by: 'alice' }, 'VERIFIED', '已核对。', '2026-09-14T00:00:00Z');
101
+ const same = isApplicableTo(proof, () => proof.verifiedAgainst.contentHash);
102
+ assert.strictEqual(same.applicable, true, '内容未变时应仍适用。');
103
+ const changed = isApplicableTo(proof, () => 'f'.repeat(64));
104
+ assert.strictEqual(changed.applicable, false, '内容变了必须判不适用。');
105
+ if (!changed.applicable) {
106
+ assert.strictEqual(changed.why, 'CONTENT_CHANGED');
107
+ }
108
+ const gone = isApplicableTo(proof, () => undefined);
109
+ assert.strictEqual(gone.applicable, false, '节点没了必须判不适用。');
110
+ });
111
+ });
112
+ //# sourceMappingURL=review-queue.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-queue.test.js","sourceRoot":"","sources":["../../../../test/unit/mapping/review-queue.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=verdict-parity.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verdict-parity.test.d.ts","sourceRoot":"","sources":["../../../../test/unit/mapping/verdict-parity.test.ts"],"names":[],"mappings":""}