@aster-cloud/aster-lang-ts 1.0.28 → 1.0.29
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/dist/scripts/generate_error_codes.js +49 -6
- package/dist/scripts/generate_error_codes.js.map +1 -1
- package/dist/src/ai/providers/openai.d.ts +10 -0
- package/dist/src/ai/providers/openai.d.ts.map +1 -1
- package/dist/src/ai/providers/openai.js +4 -0
- package/dist/src/ai/providers/openai.js.map +1 -1
- package/dist/src/config/lexicons/regex-guard.d.ts.map +1 -1
- package/dist/src/config/lexicons/regex-guard.js +128 -0
- package/dist/src/config/lexicons/regex-guard.js.map +1 -1
- package/dist/src/core/interpreter.d.ts.map +1 -1
- package/dist/src/core/interpreter.js +221 -27
- package/dist/src/core/interpreter.js.map +1 -1
- package/dist/src/cst/cst_builder.js +0 -14
- package/dist/src/cst/cst_builder.js.map +1 -1
- package/dist/src/cst/cst_printer.d.ts.map +1 -1
- package/dist/src/cst/cst_printer.js +20 -3
- package/dist/src/cst/cst_printer.js.map +1 -1
- package/dist/src/diagnostics/error_codes.d.ts +3 -3
- package/dist/src/diagnostics/error_codes.d.ts.map +1 -1
- package/dist/src/diagnostics/error_codes.js +509 -78
- package/dist/src/diagnostics/error_codes.js.map +1 -1
- package/dist/src/formatter.d.ts +7 -0
- package/dist/src/formatter.d.ts.map +1 -1
- package/dist/src/formatter.js +175 -5
- package/dist/src/formatter.js.map +1 -1
- package/dist/src/frontend/canonicalizer.d.ts.map +1 -1
- package/dist/src/frontend/canonicalizer.js +47 -6
- package/dist/src/frontend/canonicalizer.js.map +1 -1
- package/dist/src/frontend/lexer.d.ts +2 -1
- package/dist/src/frontend/lexer.d.ts.map +1 -1
- package/dist/src/frontend/lexer.js +10 -3
- package/dist/src/frontend/lexer.js.map +1 -1
- package/dist/src/frontend/transformers.d.ts.map +1 -1
- package/dist/src/frontend/transformers.js +50 -3
- package/dist/src/frontend/transformers.js.map +1 -1
- package/dist/src/lower_to_core.js +101 -4
- package/dist/src/lower_to_core.js.map +1 -1
- package/dist/src/lsp/canonicalize-options.d.ts +26 -0
- package/dist/src/lsp/canonicalize-options.d.ts.map +1 -0
- package/dist/src/lsp/canonicalize-options.js +14 -0
- package/dist/src/lsp/canonicalize-options.js.map +1 -0
- package/dist/src/lsp/completion.d.ts.map +1 -1
- package/dist/src/lsp/completion.js +22 -4
- package/dist/src/lsp/completion.js.map +1 -1
- package/dist/src/lsp/server.js +29 -1
- package/dist/src/lsp/server.js.map +1 -1
- package/dist/src/lsp/tenant-init.d.ts +55 -0
- package/dist/src/lsp/tenant-init.d.ts.map +1 -0
- package/dist/src/lsp/tenant-init.js +88 -0
- package/dist/src/lsp/tenant-init.js.map +1 -0
- package/dist/src/mapping/candidate-generator.d.ts +84 -0
- package/dist/src/mapping/candidate-generator.d.ts.map +1 -0
- package/dist/src/mapping/candidate-generator.js +114 -0
- package/dist/src/mapping/candidate-generator.js.map +1 -0
- package/dist/src/mapping/entity-proposer.d.ts +85 -0
- package/dist/src/mapping/entity-proposer.d.ts.map +1 -0
- package/dist/src/mapping/entity-proposer.js +162 -0
- package/dist/src/mapping/entity-proposer.js.map +1 -0
- package/dist/src/mapping/mapping-ir.d.ts +85 -0
- package/dist/src/mapping/mapping-ir.d.ts.map +1 -0
- package/dist/src/mapping/mapping-ir.js +195 -0
- package/dist/src/mapping/mapping-ir.js.map +1 -0
- package/dist/src/mapping/proof-ir.d.ts +125 -0
- package/dist/src/mapping/proof-ir.d.ts.map +1 -0
- package/dist/src/mapping/proof-ir.js +48 -0
- package/dist/src/mapping/proof-ir.js.map +1 -0
- package/dist/src/mapping/quantity-ir.d.ts +93 -0
- package/dist/src/mapping/quantity-ir.d.ts.map +1 -0
- package/dist/src/mapping/quantity-ir.js +151 -0
- package/dist/src/mapping/quantity-ir.js.map +1 -0
- package/dist/src/mapping/source-ir.d.ts +82 -0
- package/dist/src/mapping/source-ir.d.ts.map +1 -0
- package/dist/src/mapping/source-ir.js +231 -0
- package/dist/src/mapping/source-ir.js.map +1 -0
- package/dist/src/nodeid/change-impact.d.ts +77 -0
- package/dist/src/nodeid/change-impact.d.ts.map +1 -0
- package/dist/src/nodeid/change-impact.js +132 -0
- package/dist/src/nodeid/change-impact.js.map +1 -0
- package/dist/src/nodeid/node-id-map.d.ts +69 -0
- package/dist/src/nodeid/node-id-map.d.ts.map +1 -0
- package/dist/src/nodeid/node-id-map.js +193 -0
- package/dist/src/nodeid/node-id-map.js.map +1 -0
- package/dist/src/parser/decl-parser.d.ts.map +1 -1
- package/dist/src/parser/decl-parser.js +10 -4
- package/dist/src/parser/decl-parser.js.map +1 -1
- package/dist/src/parser/expr-stmt-parser.d.ts.map +1 -1
- package/dist/src/parser/expr-stmt-parser.js +36 -24
- package/dist/src/parser/expr-stmt-parser.js.map +1 -1
- package/dist/src/parser/span-utils.d.ts +25 -0
- package/dist/src/parser/span-utils.d.ts.map +1 -1
- package/dist/src/parser/span-utils.js +43 -0
- package/dist/src/parser/span-utils.js.map +1 -1
- package/dist/src/parser/type-parser.d.ts.map +1 -1
- package/dist/src/parser/type-parser.js +21 -0
- package/dist/src/parser/type-parser.js.map +1 -1
- package/dist/src/typecheck/expression.d.ts.map +1 -1
- package/dist/src/typecheck/expression.js +26 -0
- package/dist/src/typecheck/expression.js.map +1 -1
- package/dist/test/fuzz/fuzz.test.js +62 -2
- package/dist/test/fuzz/fuzz.test.js.map +1 -1
- package/dist/test/unit/call-arity.test.d.ts +2 -0
- package/dist/test/unit/call-arity.test.d.ts.map +1 -0
- package/dist/test/unit/call-arity.test.js +71 -0
- package/dist/test/unit/call-arity.test.js.map +1 -0
- package/dist/test/unit/canonicalizer/canonicalizer.test.js +42 -4
- package/dist/test/unit/canonicalizer/canonicalizer.test.js.map +1 -1
- package/dist/test/unit/canonicalizer/line-preservation.test.d.ts +2 -0
- package/dist/test/unit/canonicalizer/line-preservation.test.d.ts.map +1 -0
- package/dist/test/unit/canonicalizer/line-preservation.test.js +71 -0
- package/dist/test/unit/canonicalizer/line-preservation.test.js.map +1 -0
- package/dist/test/unit/canonicalizer/marker-restore-linear.test.d.ts +2 -0
- package/dist/test/unit/canonicalizer/marker-restore-linear.test.d.ts.map +1 -0
- package/dist/test/unit/canonicalizer/marker-restore-linear.test.js +98 -0
- package/dist/test/unit/canonicalizer/marker-restore-linear.test.js.map +1 -0
- package/dist/test/unit/canonicalizer/possessive-anchor.test.d.ts +2 -0
- package/dist/test/unit/canonicalizer/possessive-anchor.test.d.ts.map +1 -0
- package/dist/test/unit/canonicalizer/possessive-anchor.test.js +101 -0
- package/dist/test/unit/canonicalizer/possessive-anchor.test.js.map +1 -0
- package/dist/test/unit/formatter-redos.test.d.ts +2 -0
- package/dist/test/unit/formatter-redos.test.d.ts.map +1 -0
- package/dist/test/unit/formatter-redos.test.js +196 -0
- package/dist/test/unit/formatter-redos.test.js.map +1 -0
- package/dist/test/unit/interpreter/interpreter.test.js +7 -2
- package/dist/test/unit/interpreter/interpreter.test.js.map +1 -1
- package/dist/test/unit/lexer/bom.test.d.ts +2 -0
- package/dist/test/unit/lexer/bom.test.d.ts.map +1 -0
- package/dist/test/unit/lexer/bom.test.js +49 -0
- package/dist/test/unit/lexer/bom.test.js.map +1 -0
- package/dist/test/unit/list-get-bounds.test.d.ts +2 -0
- package/dist/test/unit/list-get-bounds.test.d.ts.map +1 -0
- package/dist/test/unit/list-get-bounds.test.js +66 -0
- package/dist/test/unit/list-get-bounds.test.js.map +1 -0
- package/dist/test/unit/long-bigint-exact.test.d.ts +2 -0
- package/dist/test/unit/long-bigint-exact.test.d.ts.map +1 -0
- package/dist/test/unit/long-bigint-exact.test.js +79 -0
- package/dist/test/unit/long-bigint-exact.test.js.map +1 -0
- package/dist/test/unit/lsp/completion-lexicon.test.d.ts +2 -0
- package/dist/test/unit/lsp/completion-lexicon.test.d.ts.map +1 -0
- package/dist/test/unit/lsp/completion-lexicon.test.js +83 -0
- package/dist/test/unit/lsp/completion-lexicon.test.js.map +1 -0
- package/dist/test/unit/lsp/tenant-vocabulary.test.d.ts +2 -0
- package/dist/test/unit/lsp/tenant-vocabulary.test.d.ts.map +1 -0
- package/dist/test/unit/lsp/tenant-vocabulary.test.js +187 -0
- package/dist/test/unit/lsp/tenant-vocabulary.test.js.map +1 -0
- package/dist/test/unit/map-builtins.test.js +163 -3
- package/dist/test/unit/map-builtins.test.js.map +1 -1
- package/dist/test/unit/map-structural-equality.test.d.ts +2 -0
- package/dist/test/unit/map-structural-equality.test.d.ts.map +1 -0
- package/dist/test/unit/map-structural-equality.test.js +64 -0
- package/dist/test/unit/map-structural-equality.test.js.map +1 -0
- package/dist/test/unit/map-type-guard.test.js +8 -5
- package/dist/test/unit/map-type-guard.test.js.map +1 -1
- package/dist/test/unit/mapping/candidate-generator.test.d.ts +2 -0
- package/dist/test/unit/mapping/candidate-generator.test.d.ts.map +1 -0
- package/dist/test/unit/mapping/candidate-generator.test.js +177 -0
- package/dist/test/unit/mapping/candidate-generator.test.js.map +1 -0
- package/dist/test/unit/mapping/entity-proposer.test.d.ts +2 -0
- package/dist/test/unit/mapping/entity-proposer.test.d.ts.map +1 -0
- package/dist/test/unit/mapping/entity-proposer.test.js +168 -0
- package/dist/test/unit/mapping/entity-proposer.test.js.map +1 -0
- package/dist/test/unit/mapping/mapping-ir.test.d.ts +2 -0
- package/dist/test/unit/mapping/mapping-ir.test.d.ts.map +1 -0
- package/dist/test/unit/mapping/mapping-ir.test.js +144 -0
- package/dist/test/unit/mapping/mapping-ir.test.js.map +1 -0
- package/dist/test/unit/mapping/proof-ir.test.d.ts +2 -0
- package/dist/test/unit/mapping/proof-ir.test.d.ts.map +1 -0
- package/dist/test/unit/mapping/proof-ir.test.js +119 -0
- package/dist/test/unit/mapping/proof-ir.test.js.map +1 -0
- package/dist/test/unit/mapping/quantity-ir.test.d.ts +2 -0
- package/dist/test/unit/mapping/quantity-ir.test.d.ts.map +1 -0
- package/dist/test/unit/mapping/quantity-ir.test.js +144 -0
- package/dist/test/unit/mapping/quantity-ir.test.js.map +1 -0
- package/dist/test/unit/mapping/quantity-redos-property.test.d.ts +2 -0
- package/dist/test/unit/mapping/quantity-redos-property.test.d.ts.map +1 -0
- package/dist/test/unit/mapping/quantity-redos-property.test.js +182 -0
- package/dist/test/unit/mapping/quantity-redos-property.test.js.map +1 -0
- package/dist/test/unit/mapping/redos-budget.test.d.ts +2 -0
- package/dist/test/unit/mapping/redos-budget.test.d.ts.map +1 -0
- package/dist/test/unit/mapping/redos-budget.test.js +168 -0
- package/dist/test/unit/mapping/redos-budget.test.js.map +1 -0
- package/dist/test/unit/mapping/source-ir.test.d.ts +2 -0
- package/dist/test/unit/mapping/source-ir.test.d.ts.map +1 -0
- package/dist/test/unit/mapping/source-ir.test.js +113 -0
- package/dist/test/unit/mapping/source-ir.test.js.map +1 -0
- package/dist/test/unit/nodeid/change-impact.test.d.ts +2 -0
- package/dist/test/unit/nodeid/change-impact.test.d.ts.map +1 -0
- package/dist/test/unit/nodeid/change-impact.test.js +152 -0
- package/dist/test/unit/nodeid/change-impact.test.js.map +1 -0
- package/dist/test/unit/nodeid/rename-declaration.test.d.ts +2 -0
- package/dist/test/unit/nodeid/rename-declaration.test.d.ts.map +1 -0
- package/dist/test/unit/nodeid/rename-declaration.test.js +75 -0
- package/dist/test/unit/nodeid/rename-declaration.test.js.map +1 -0
- package/dist/test/unit/parse-type-recursion.test.d.ts +2 -0
- package/dist/test/unit/parse-type-recursion.test.d.ts.map +1 -0
- package/dist/test/unit/parse-type-recursion.test.js +58 -0
- package/dist/test/unit/parse-type-recursion.test.js.map +1 -0
- package/dist/test/unit/regex-guard.test.js +64 -0
- package/dist/test/unit/regex-guard.test.js.map +1 -1
- package/dist/test/unit/strict-int-coercion.test.d.ts +2 -0
- package/dist/test/unit/strict-int-coercion.test.d.ts.map +1 -0
- package/dist/test/unit/strict-int-coercion.test.js +96 -0
- package/dist/test/unit/strict-int-coercion.test.js.map +1 -0
- package/dist/test/unit/ufcs-receiver.test.d.ts +2 -0
- package/dist/test/unit/ufcs-receiver.test.d.ts.map +1 -0
- package/dist/test/unit/ufcs-receiver.test.js +75 -0
- package/dist/test/unit/ufcs-receiver.test.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { parseProposals, proposeEntities, verifyEntityCandidate } from '../../../src/mapping/entity-proposer.js';
|
|
4
|
+
import { parseSourceIr } from '../../../src/mapping/source-ir.js';
|
|
5
|
+
import { extractQuantities } from '../../../src/mapping/quantity-ir.js';
|
|
6
|
+
/**
|
|
7
|
+
* Entity 提出器(ADR 0037 §3/§11.5)—— 三段式的第①段。
|
|
8
|
+
*
|
|
9
|
+
* <p>核心契约:**LLM 只能提出候选,不能定义什么叫正确**。
|
|
10
|
+
* 本测试用假 provider 驱动,不打真实 LLM——判定逻辑必须可离线验证。
|
|
11
|
+
*/
|
|
12
|
+
const DOC = [
|
|
13
|
+
'# 付款审批政策', '',
|
|
14
|
+
'单笔付款超过 $10,000 时需要财务经理审批。',
|
|
15
|
+
'低于该额度由部门主管批准。', '',
|
|
16
|
+
].join('\n');
|
|
17
|
+
const BY = 'llm:fake/test-model';
|
|
18
|
+
/** 假 provider:返回预置文本,记录收到的请求。 */
|
|
19
|
+
function fakeProvider(reply) {
|
|
20
|
+
// ★用 `| undefined` 而非 `?`:tsconfig 开了 exactOptionalPropertyTypes,
|
|
21
|
+
// 「属性可为 undefined」与「属性可缺席」是两回事。
|
|
22
|
+
const p = {
|
|
23
|
+
lastRequest: undefined,
|
|
24
|
+
async generate(request) {
|
|
25
|
+
p.lastRequest = request;
|
|
26
|
+
return {
|
|
27
|
+
content: reply, model: 'test-model',
|
|
28
|
+
usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
|
|
29
|
+
};
|
|
30
|
+
},
|
|
31
|
+
getName: () => 'fake',
|
|
32
|
+
getModel: () => 'test-model',
|
|
33
|
+
};
|
|
34
|
+
return p;
|
|
35
|
+
}
|
|
36
|
+
/** 造一条「位置与文本都正确」的提议。 */
|
|
37
|
+
function truthful(text) {
|
|
38
|
+
return JSON.stringify([{ text, kind: 'Role', start: DOC.indexOf(text) }]);
|
|
39
|
+
}
|
|
40
|
+
describe('Entity 提出器 — 三段式第①段', () => {
|
|
41
|
+
it('★位置与文本都对的候选被接受', () => {
|
|
42
|
+
const r = parseProposals(truthful('财务经理'), DOC, 0, BY);
|
|
43
|
+
assert.strictEqual(r.candidates.length, 1, `应接受 1 条,实际 ${JSON.stringify(r)}`);
|
|
44
|
+
assert.strictEqual(r.candidates[0].text, '财务经理');
|
|
45
|
+
assert.strictEqual(r.candidates[0].proposedKind, 'Role');
|
|
46
|
+
assert.strictEqual(r.candidates[0].proposedBy, BY);
|
|
47
|
+
assert.strictEqual(DOC.slice(r.candidates[0].span.start, r.candidates[0].span.end), '财务经理');
|
|
48
|
+
});
|
|
49
|
+
it('★幻觉闸门:文本不在声称位置上 → 丢弃并报告', () => {
|
|
50
|
+
// LLM 最常见的幻觉:文本对但位置错。这会让双向导航指向错误的地方。
|
|
51
|
+
const wrongPos = JSON.stringify([{ text: '财务经理', kind: 'Role', start: 0 }]);
|
|
52
|
+
const r = parseProposals(wrongPos, DOC, 0, BY);
|
|
53
|
+
assert.strictEqual(r.candidates.length, 0, '位置错的候选必须丢弃。');
|
|
54
|
+
assert.strictEqual(r.rejected.length, 1);
|
|
55
|
+
assert.match(r.rejected[0].why, /不符/, `应说明不符,实际:${r.rejected[0].why}`);
|
|
56
|
+
});
|
|
57
|
+
it('★幻觉闸门:文档里根本没有的文本 → 丢弃', () => {
|
|
58
|
+
// 凭空编造的实体。
|
|
59
|
+
const invented = JSON.stringify([{ text: '首席合规官', kind: 'Role', start: 20 }]);
|
|
60
|
+
const r = parseProposals(invented, DOC, 0, BY);
|
|
61
|
+
assert.strictEqual(r.candidates.length, 0, '文档里不存在的文本必须丢弃。');
|
|
62
|
+
assert.strictEqual(r.rejected.length, 1);
|
|
63
|
+
});
|
|
64
|
+
it('★被丢弃的条目必须如实报告,不得静默吞掉', () => {
|
|
65
|
+
// 反向守卫:若 rejected 恒为空,「LLM 产出了不合规内容」这件事就消失了,
|
|
66
|
+
// 而那恰恰是评估模型可靠性最重要的信号。
|
|
67
|
+
const mixed = JSON.stringify([
|
|
68
|
+
{ text: '财务经理', kind: 'Role', start: DOC.indexOf('财务经理') }, // 对
|
|
69
|
+
{ text: '不存在的角色', kind: 'Role', start: 5 }, // 错
|
|
70
|
+
{ text: '部门主管', kind: 'Party', start: 999 }, // 越界
|
|
71
|
+
{ notText: 1 }, // 格式错
|
|
72
|
+
]);
|
|
73
|
+
const r = parseProposals(mixed, DOC, 0, BY);
|
|
74
|
+
assert.strictEqual(r.candidates.length, 1, '只应接受那条正确的。');
|
|
75
|
+
assert.strictEqual(r.rejected.length, 3, `三条不合规都要报告,实际 ${JSON.stringify(r.rejected)}`);
|
|
76
|
+
assert.ok(r.rejected.every(x => x.why.length > 0), '每条丢弃都要说明原因。');
|
|
77
|
+
});
|
|
78
|
+
it('非 JSON / 非数组输出整体丢弃,不崩', () => {
|
|
79
|
+
for (const bad of ['我找到了这些实体:财务经理', '{"text":"x"}', '']) {
|
|
80
|
+
const r = parseProposals(bad, DOC, 0, BY);
|
|
81
|
+
assert.strictEqual(r.candidates.length, 0);
|
|
82
|
+
assert.strictEqual(r.rejected.length, 1, `输入 ${JSON.stringify(bad)} 应报告 1 条丢弃`);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
it('容忍 LLM 套的 ```json 围栏(prompt 已要求不要,但模型常忘)', () => {
|
|
86
|
+
const fenced = '```json\n' + truthful('部门主管') + '\n```';
|
|
87
|
+
assert.strictEqual(parseProposals(fenced, DOC, 0, BY).candidates.length, 1);
|
|
88
|
+
});
|
|
89
|
+
it('★scope 偏移必须平移回原文(否则候选整体错位)', () => {
|
|
90
|
+
// LLM 看到的是切片,它给的 start 是切片内偏移;必须加上 scope 起点。
|
|
91
|
+
const base = 10;
|
|
92
|
+
const sliced = DOC.slice(base);
|
|
93
|
+
const inSlice = sliced.indexOf('财务经理');
|
|
94
|
+
const r = parseProposals(JSON.stringify([{ text: '财务经理', kind: 'Role', start: inSlice }]), DOC, base, BY);
|
|
95
|
+
assert.strictEqual(r.candidates.length, 1, `切片内偏移未被平移回原文 —— 实际 ${JSON.stringify(r.rejected)}`);
|
|
96
|
+
assert.strictEqual(r.candidates[0].span.start, base + inSlice);
|
|
97
|
+
});
|
|
98
|
+
it('★第②段恒判 REVIEW_REQUIRED —— 机器不得替人下结论', () => {
|
|
99
|
+
// ADR §3:AI 可以提出映射,但不能定义什么叫正确。
|
|
100
|
+
// Entity 的目标是**语义**对应,而 verifier 只能读 kind/value/name/origin
|
|
101
|
+
// (类型层是两引擎合法分叉的层,不在可依赖范围内,见 §5.1)。
|
|
102
|
+
const r = parseProposals(truthful('财务经理'), DOC, 0, BY);
|
|
103
|
+
const verdict = verifyEntityCandidate(r.candidates[0]);
|
|
104
|
+
assert.strictEqual(verdict.verdict, 'REVIEW_REQUIRED');
|
|
105
|
+
assert.match(verdict.reason, /语义/, '判定理由应说明这是语义判断。');
|
|
106
|
+
});
|
|
107
|
+
it('★temperature 必须为 0:同一文档反复提出应可复现', () => {
|
|
108
|
+
// 候选生成的可复现性是审计的前提——否则「上次为什么提了这条」永远说不清。
|
|
109
|
+
const p = fakeProvider('[]');
|
|
110
|
+
return proposeEntities(DOC, p).then(() => {
|
|
111
|
+
assert.strictEqual(p.lastRequest?.temperature, 0, `temperature 应为 0,实际 ${p.lastRequest?.temperature}`);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
it('★scope 限定时只把该节点的文本喂给 LLM', () => {
|
|
115
|
+
const ir = parseSourceIr(DOC);
|
|
116
|
+
const heading = ir.children[0];
|
|
117
|
+
const p = fakeProvider('[]');
|
|
118
|
+
return proposeEntities(DOC, p, heading).then(() => {
|
|
119
|
+
assert.strictEqual(p.lastRequest?.prompt, DOC.slice(heading.span.start, heading.span.end), 'scope 限定时应只喂该节点的文本,而不是整篇文档。');
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
it('★与 Quantity 分工不重叠:Entity 不碰金额', () => {
|
|
123
|
+
// Quantity 由确定性模块处理(ADR §11);两边都抽会产生重复候选。
|
|
124
|
+
const moneyTexts = extractQuantities(DOC).map(q => q.text);
|
|
125
|
+
assert.ok(moneyTexts.includes('$10,000'), '前置:文档里应有金额。');
|
|
126
|
+
// 即使 LLM 错误地提出了金额,它也只是一条普通候选——不会被特殊对待,
|
|
127
|
+
// 但契约上 systemPrompt 已明确要求不要输出。此处钉住 prompt 里那条要求。
|
|
128
|
+
const p = fakeProvider('[]');
|
|
129
|
+
return proposeEntities(DOC, p).then(() => {
|
|
130
|
+
assert.match(p.lastRequest?.systemPrompt ?? '', /不要输出金额/, 'systemPrompt 必须明确要求 LLM 不输出金额等数量 —— 那是确定性模块的职责。');
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
it('★注入守卫:kind 含标签字符 → 丢弃(防 reason 被渲染成 HTML)', () => {
|
|
134
|
+
// kind 是 **LLM 完全可控**的自由字符串,且原样进入 reason(人类可读文本)。
|
|
135
|
+
// 本仓有 6 处 dangerouslySetInnerHTML——一旦有人把 reason 接进去就是存储型 XSS。
|
|
136
|
+
// ★不能在此做 HTML 转义:那会改变 kind 的值,违反 §12.4「原样保留」。
|
|
137
|
+
// 正确做法是在源头限制**形态**——合法类别名本就不含这些字符。
|
|
138
|
+
const payload = JSON.stringify([
|
|
139
|
+
{ text: '财务经理', kind: '<img src=x onerror=alert(1)>', start: DOC.indexOf('财务经理') },
|
|
140
|
+
]);
|
|
141
|
+
const r = parseProposals(payload, DOC, 0, BY);
|
|
142
|
+
assert.strictEqual(r.candidates.length, 0, '含标签字符的 kind 必须丢弃。');
|
|
143
|
+
assert.match(r.rejected[0].why, /标签或控制字符/);
|
|
144
|
+
});
|
|
145
|
+
it('★合法类别(中英文)不得被注入守卫误伤', () => {
|
|
146
|
+
// 反向守卫:若字符白名单收得过紧,正常类别会被拒——那比不设防更糟,
|
|
147
|
+
// 因为它会静默丢掉真实候选。
|
|
148
|
+
for (const kind of ['Role', 'Obligation', '角色', '义务主体', 'Party_A']) {
|
|
149
|
+
const r = parseProposals(JSON.stringify([{ text: '财务经理', kind, start: DOC.indexOf('财务经理') }]), DOC, 0, BY);
|
|
150
|
+
assert.strictEqual(r.candidates.length, 1, `合法类别 ${JSON.stringify(kind)} 被误拒:${JSON.stringify(r.rejected)}`);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
it('★资源上限:条目数超限整批拒绝,且如实报告', () => {
|
|
154
|
+
// 防「LLM 返回巨量条目」耗尽下游内存。★不静默截断——
|
|
155
|
+
// 调用方必须知道「LLM 返回量异常」这件事。
|
|
156
|
+
const many = JSON.stringify(Array.from({ length: 5000 }, () => ({ text: '财务经理', kind: 'Role', start: DOC.indexOf('财务经理') })));
|
|
157
|
+
const r = parseProposals(many, DOC, 0, BY);
|
|
158
|
+
assert.strictEqual(r.candidates.length, 0, '超限应整批拒绝。');
|
|
159
|
+
assert.strictEqual(r.rejected.length, 1);
|
|
160
|
+
assert.match(r.rejected[0].why, /条目数超上限/);
|
|
161
|
+
});
|
|
162
|
+
it('★超长 kind → 丢弃(防 reason 变成一大段不可控内容)', () => {
|
|
163
|
+
const r = parseProposals(JSON.stringify([{ text: '财务经理', kind: 'x'.repeat(200), start: DOC.indexOf('财务经理') }]), DOC, 0, BY);
|
|
164
|
+
assert.strictEqual(r.candidates.length, 0);
|
|
165
|
+
assert.match(r.rejected[0].why, /过长/);
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
//# sourceMappingURL=entity-proposer.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity-proposer.test.js","sourceRoot":"","sources":["../../../../test/unit/mapping/entity-proposer.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mapping-ir.test.d.ts","sourceRoot":"","sources":["../../../../test/unit/mapping/mapping-ir.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { canonicalize } from '../../../src/frontend/canonicalizer.js';
|
|
4
|
+
import { lex } from '../../../src/frontend/lexer.js';
|
|
5
|
+
import { parse } from '../../../src/parser.js';
|
|
6
|
+
import { lowerModule } from '../../../src/lower_to_core.js';
|
|
7
|
+
import { computeNodeIds } from '../../../src/nodeid/node-id-map.js';
|
|
8
|
+
import { verifyMapping } from '../../../src/mapping/mapping-ir.js';
|
|
9
|
+
/**
|
|
10
|
+
* MappingIR verifier(ADR 0037 §4/§7)。
|
|
11
|
+
*
|
|
12
|
+
* <p>本测试钉的是**契约**:哪些映射机器能证明、哪些必须交人、哪些能证伪。
|
|
13
|
+
* 不钉实现细节。
|
|
14
|
+
*/
|
|
15
|
+
/** 从真实源码建「nodeId → 节点」解析器,走完整编译链。 */
|
|
16
|
+
function resolverOf(src) {
|
|
17
|
+
const ir = JSON.parse(JSON.stringify(lowerModule(parse(lex(canonicalize(src))).ast)));
|
|
18
|
+
const ids = computeNodeIds(ir);
|
|
19
|
+
// nodeId → 原始节点:按路径在 IR 上走一遍(nodeId 就是路径)
|
|
20
|
+
const nodeAt = (id) => {
|
|
21
|
+
let cur = ir;
|
|
22
|
+
for (const seg of id.slice(2).split('.')) {
|
|
23
|
+
if (seg === '')
|
|
24
|
+
continue;
|
|
25
|
+
const m = /^([A-Za-z0-9_]+)(?:\{(.*)\}|\[(\d+)\])?$/.exec(seg);
|
|
26
|
+
if (!m || cur === null || typeof cur !== 'object')
|
|
27
|
+
return undefined;
|
|
28
|
+
cur = cur[m[1]];
|
|
29
|
+
if (m[2] !== undefined) {
|
|
30
|
+
cur = cur.find(e => e?.name === m[2] || e?.path === m[2]);
|
|
31
|
+
}
|
|
32
|
+
else if (m[3] !== undefined) {
|
|
33
|
+
cur = cur[Number(m[3])];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return cur;
|
|
37
|
+
};
|
|
38
|
+
return {
|
|
39
|
+
resolve: (id) => {
|
|
40
|
+
const n = nodeAt(id);
|
|
41
|
+
if (!n || typeof n.kind !== 'string')
|
|
42
|
+
return undefined;
|
|
43
|
+
// ★不能写 `name: n.name as string | undefined`:tsconfig 开了
|
|
44
|
+
// exactOptionalPropertyTypes,显式 undefined 与「属性缺席」是两回事。
|
|
45
|
+
const out = { kind: n.kind, ...(n.value === undefined ? {} : { value: n.value }) };
|
|
46
|
+
return typeof n.name === 'string' ? { ...out, name: n.name } : out;
|
|
47
|
+
},
|
|
48
|
+
find: (kind, value) => {
|
|
49
|
+
for (const [id] of ids) {
|
|
50
|
+
const n = nodeAt(id);
|
|
51
|
+
if (n?.kind === kind && (value === undefined || n.value === value))
|
|
52
|
+
return id;
|
|
53
|
+
}
|
|
54
|
+
throw new Error(`语料里找不到 kind=${kind} value=${String(value)} 的节点`);
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
const POLICY = [
|
|
59
|
+
'Module demo.approve.', '',
|
|
60
|
+
'Rule approve given amount, produce:',
|
|
61
|
+
' If amount greater than 10000:',
|
|
62
|
+
' Return "REFER".',
|
|
63
|
+
' Otherwise:',
|
|
64
|
+
' Return "APPROVE".', '',
|
|
65
|
+
].join('\n');
|
|
66
|
+
const mapping = (text, nodeId, start = 0) => ({ span: { start, end: start + text.length }, text, nodeId });
|
|
67
|
+
describe('MappingIR verifier — 机器能证明的那一类', () => {
|
|
68
|
+
it('★`"$10,000"` ↔ Int(10000):跨越「人类书写 ↔ IR 值」的鸿沟', () => {
|
|
69
|
+
// ADR §4 的原始场景。货币符号与千分位是人类惯例,IR 里只有 10000。
|
|
70
|
+
const { resolve, find } = resolverOf(POLICY);
|
|
71
|
+
const r = verifyMapping(mapping('$10,000', find('Int', 10000)), resolve);
|
|
72
|
+
assert.strictEqual(r.verdict, 'VERIFIED', `应判 VERIFIED(这正是 MappingIR 的价值所在)。实际 ${r.verdict}:${r.reason}`);
|
|
73
|
+
});
|
|
74
|
+
it('字符串字面量按原值比较', () => {
|
|
75
|
+
const { resolve, find } = resolverOf(POLICY);
|
|
76
|
+
const r = verifyMapping(mapping('"REFER"', find('String', 'REFER')), resolve);
|
|
77
|
+
assert.strictEqual(r.verdict, 'VERIFIED', r.reason);
|
|
78
|
+
});
|
|
79
|
+
it('★值矛盾必须 REJECTED,不得放过', () => {
|
|
80
|
+
// 反向守卫:没有这条,verifier 可以退化成「一律 VERIFIED」,
|
|
81
|
+
// 上面两条照样绿,而它就彻底没用了。
|
|
82
|
+
const { resolve, find } = resolverOf(POLICY);
|
|
83
|
+
const r = verifyMapping(mapping('$20,000', find('Int', 10000)), resolve);
|
|
84
|
+
assert.strictEqual(r.verdict, 'REJECTED', `文本 20000 与节点值 10000 矛盾,必须证伪。实际 ${r.verdict}`);
|
|
85
|
+
assert.match(r.reason, /矛盾/);
|
|
86
|
+
});
|
|
87
|
+
it('★带业务含义的映射一律交人,不猜(ADR §3)', () => {
|
|
88
|
+
// `"requires approval"` ↔ 某个 If/Call 节点:机器证不了。
|
|
89
|
+
// 判 REVIEW_REQUIRED 而**不是** REJECTED —— 那不是「错」,是「机器管不了」。
|
|
90
|
+
const { resolve, find } = resolverOf(POLICY);
|
|
91
|
+
const r = verifyMapping(mapping('requires approval', find('If')), resolve);
|
|
92
|
+
assert.strictEqual(r.verdict, 'REVIEW_REQUIRED', `非精确值节点应交人复核,实际 ${r.verdict}:${r.reason}`);
|
|
93
|
+
});
|
|
94
|
+
it('★Double 不参与机械验证(源码文本与 IR 值不可逆)', () => {
|
|
95
|
+
// 实测:`1.0` → number 1 → 回写 "1";`1e3` → 1000。文本不可还原,
|
|
96
|
+
// 机器无法证明「这段文本就是这个 Double」。故交人,而不是用近似规则假装能证。
|
|
97
|
+
const r = verifyMapping(mapping('1.0', '$.fake'), () => ({ kind: 'Double', value: 1 }));
|
|
98
|
+
assert.strictEqual(r.verdict, 'REVIEW_REQUIRED', `Double 应交人复核(不可逆),实际 ${r.verdict}:${r.reason}`);
|
|
99
|
+
// ★必须断言**判定依据**,不只是判定结果。
|
|
100
|
+
// 变异验证发现:把 Double 加进 EXACT_VALUE_KINDS 后,结论仍是
|
|
101
|
+
// REVIEW_REQUIRED —— 因为 parseLiteralFromText 里也没有 Double 分支,
|
|
102
|
+
// 两层防御恰好重叠。只断言 verdict 的话,EXACT_VALUE_KINDS 这道门
|
|
103
|
+
// **结构上无法变红**(假门禁)。
|
|
104
|
+
// 断言 reason 提到「不属于可机械证明的精确值字面量」才真正钉住那道门。
|
|
105
|
+
assert.match(r.reason, /不属于可机械证明的精确值字面量/, `应因「kind 不在可验证清单」而交人,而不是因为「解析不出值」。实际:${r.reason}`);
|
|
106
|
+
});
|
|
107
|
+
it('★canonical 形态 ≠ 源码文本:`$10,000.00` 必须能验过 Decimal("10000")', () => {
|
|
108
|
+
// 100.00m 在 IR 里是 value:"100"(尾随零被规范化)。
|
|
109
|
+
// 若做字符串相等比较,这条会误判 REJECTED —— 一个数值上完全正确的映射。
|
|
110
|
+
const r = verifyMapping(mapping('$10,000.00', '$.fake'), () => ({ kind: 'Decimal', value: '10000' }));
|
|
111
|
+
assert.strictEqual(r.verdict, 'VERIFIED', `尾随零差异不应导致失败(必须按值比较而非按文本)。实际 ${r.verdict}:${r.reason}`);
|
|
112
|
+
});
|
|
113
|
+
it('★超安全整数的 Long 不得丢精度', () => {
|
|
114
|
+
// 9007199254740993 一旦过一次 JS number 就变 …992(本仓实测踩过)。
|
|
115
|
+
// verifier 全程字符串比较,必须验得过。
|
|
116
|
+
const big = '9007199254740993';
|
|
117
|
+
const ok = verifyMapping(mapping(big, '$.fake'), () => ({ kind: 'Long', value: big }));
|
|
118
|
+
assert.strictEqual(ok.verdict, 'VERIFIED', ok.reason);
|
|
119
|
+
// 且**差 1** 必须能被识别出来——若中途过了 number,这两个值会变得相等。
|
|
120
|
+
const bad = verifyMapping(mapping('9007199254740992', '$.fake'), () => ({ kind: 'Long', value: big }));
|
|
121
|
+
assert.strictEqual(bad.verdict, 'REJECTED', '相差 1 的大整数必须证伪 —— 若判 VERIFIED,说明中途经过了 JS number 并丢了精度。');
|
|
122
|
+
});
|
|
123
|
+
it('节点不存在 → REJECTED(不能静默当成「待复核」)', () => {
|
|
124
|
+
const { resolve } = resolverOf(POLICY);
|
|
125
|
+
const r = verifyMapping(mapping('$10,000', '$.decls{nonexistent}'), resolve);
|
|
126
|
+
assert.strictEqual(r.verdict, 'REJECTED');
|
|
127
|
+
});
|
|
128
|
+
it('★span 与 text 必须自洽,否则双向导航会跳错位置', () => {
|
|
129
|
+
const { resolve, find } = resolverOf(POLICY);
|
|
130
|
+
const id = find('Int', 10000);
|
|
131
|
+
// 区间宽度 3,但 text 长 7 —— span 指向的根本不是这段文本
|
|
132
|
+
const inconsistent = verifyMapping({ span: { start: 0, end: 3 }, text: '$10,000', nodeId: id }, resolve);
|
|
133
|
+
assert.strictEqual(inconsistent.verdict, 'REJECTED', inconsistent.reason);
|
|
134
|
+
// 非法区间
|
|
135
|
+
const bad = verifyMapping({ span: { start: 5, end: 5 }, text: '', nodeId: id }, resolve);
|
|
136
|
+
assert.strictEqual(bad.verdict, 'REJECTED', bad.reason);
|
|
137
|
+
});
|
|
138
|
+
it('每条结论都必须给出依据(含 VERIFIED,便于审计复核)', () => {
|
|
139
|
+
const { resolve, find } = resolverOf(POLICY);
|
|
140
|
+
const r = verifyMapping(mapping('$10,000', find('Int', 10000)), resolve);
|
|
141
|
+
assert.ok(r.reason.length > 0, 'VERIFIED 也必须说明依据,否则审计时无法复核。');
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
//# sourceMappingURL=mapping-ir.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mapping-ir.test.js","sourceRoot":"","sources":["../../../../test/unit/mapping/mapping-ir.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proof-ir.test.d.ts","sourceRoot":"","sources":["../../../../test/unit/mapping/proof-ir.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { canonicalize } from '../../../src/frontend/canonicalizer.js';
|
|
4
|
+
import { lex } from '../../../src/frontend/lexer.js';
|
|
5
|
+
import { parse } from '../../../src/parser.js';
|
|
6
|
+
import { lowerModule } from '../../../src/lower_to_core.js';
|
|
7
|
+
import { computeNodeIds } from '../../../src/nodeid/node-id-map.js';
|
|
8
|
+
import { isApplicableTo, resolveEffective } from '../../../src/mapping/proof-ir.js';
|
|
9
|
+
/**
|
|
10
|
+
* ProofIR(ADR 0037 §2/§6.1)。
|
|
11
|
+
*
|
|
12
|
+
* <p>核心契约:**proof 记录不可变,有效性算出来**。
|
|
13
|
+
* 用真实 IR 驱动——proof 锚定 nodeId + contentHash,改源码后自动失效。
|
|
14
|
+
*/
|
|
15
|
+
const V1 = [
|
|
16
|
+
'Module demo.approve.', '',
|
|
17
|
+
'Rule approve given amount, produce:',
|
|
18
|
+
' If amount greater than 10000:',
|
|
19
|
+
' Return "REFER".',
|
|
20
|
+
' Otherwise:',
|
|
21
|
+
' Return "APPROVE".', '',
|
|
22
|
+
].join('\n');
|
|
23
|
+
const idsOf = (src) => computeNodeIds(JSON.parse(JSON.stringify(lowerModule(parse(lex(canonicalize(src))).ast))));
|
|
24
|
+
/** 找到阈值字面量节点(`10000`)。 */
|
|
25
|
+
function thresholdNode(src) {
|
|
26
|
+
for (const [id, v] of idsOf(src)) {
|
|
27
|
+
if (v.kind === 'Int')
|
|
28
|
+
return { nodeId: id, contentHash: v.contentHash };
|
|
29
|
+
}
|
|
30
|
+
throw new Error('找不到 Int 节点');
|
|
31
|
+
}
|
|
32
|
+
function proofOf(anchor, overrides = {}) {
|
|
33
|
+
return {
|
|
34
|
+
mapping: { span: { start: 0, end: 7 }, text: '$10,000', nodeId: anchor.nodeId },
|
|
35
|
+
verdict: 'VERIFIED',
|
|
36
|
+
subject: { kind: 'VERIFIER', by: 'mapping-ir/v1' },
|
|
37
|
+
rule: { id: 'exact-value', version: '1' },
|
|
38
|
+
verifiedAgainst: anchor,
|
|
39
|
+
reason: '文本解析为 Int 10000,与目标节点一致。',
|
|
40
|
+
recordedAt: '2026-09-13T00:00:00Z',
|
|
41
|
+
...overrides,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const hashLookup = (src) => (id) => idsOf(src).get(id)?.contentHash;
|
|
45
|
+
describe('ProofIR — 不可变记录 + 有效性算出来', () => {
|
|
46
|
+
it('★内容未变 → proof 仍适用', () => {
|
|
47
|
+
const anchor = thresholdNode(V1);
|
|
48
|
+
const r = isApplicableTo(proofOf(anchor), hashLookup(V1));
|
|
49
|
+
assert.deepStrictEqual(r, { applicable: true });
|
|
50
|
+
});
|
|
51
|
+
it('★改了阈值 → proof 自动失效(记录本身不被修改)', () => {
|
|
52
|
+
const anchor = thresholdNode(V1);
|
|
53
|
+
const proof = proofOf(anchor);
|
|
54
|
+
const frozen = JSON.stringify(proof);
|
|
55
|
+
const v2 = V1.replace('10000', '20000');
|
|
56
|
+
assert.notStrictEqual(v2, V1, '★replace 未生效则本用例无效。');
|
|
57
|
+
const r = isApplicableTo(proof, hashLookup(v2));
|
|
58
|
+
assert.strictEqual(r.applicable, false, '阈值已变,proof 不应再适用。');
|
|
59
|
+
assert.strictEqual(r.applicable === false ? r.why : '', 'CONTENT_CHANGED');
|
|
60
|
+
// ★记录本身一个字节都没被改 —— 这是「不可变 + 水位线」的关键。
|
|
61
|
+
assert.strictEqual(JSON.stringify(proof), frozen, 'proof 记录被修改了 —— 应当只算有效性,绝不原地改写历史事实。');
|
|
62
|
+
});
|
|
63
|
+
it('节点已不存在 → NODE_GONE(与「内容变了」区分开)', () => {
|
|
64
|
+
const anchor = thresholdNode(V1);
|
|
65
|
+
const r = isApplicableTo(proofOf(anchor), () => undefined);
|
|
66
|
+
assert.strictEqual(r.applicable, false);
|
|
67
|
+
assert.strictEqual(r.applicable === false ? r.why : '', 'NODE_GONE');
|
|
68
|
+
});
|
|
69
|
+
it('★仲裁:只在「仍适用」的里面取最新', () => {
|
|
70
|
+
const anchor = thresholdNode(V1);
|
|
71
|
+
const old = proofOf(anchor, { recordedAt: '2026-01-01T00:00:00Z', verdict: 'REVIEW_REQUIRED' });
|
|
72
|
+
const recent = proofOf(anchor, { recordedAt: '2026-09-13T00:00:00Z', verdict: 'VERIFIED' });
|
|
73
|
+
const { effective } = resolveEffective([old, recent], hashLookup(V1));
|
|
74
|
+
assert.strictEqual(effective?.recordedAt, '2026-09-13T00:00:00Z', '应取最新的一条。');
|
|
75
|
+
});
|
|
76
|
+
it('★失效的 proof 不得参与仲裁(否则会拿旧版本的结论当现在的答案)', () => {
|
|
77
|
+
// 反向守卫:没有这条,resolveEffective 可以退化成「无脑取最新」——
|
|
78
|
+
// 上面那条照样绿,而一条早已失效的 proof 会被当成当前有效结论。
|
|
79
|
+
const anchor = thresholdNode(V1);
|
|
80
|
+
const stale = proofOf(anchor, { recordedAt: '2099-01-01T00:00:00Z' }); // 时间最新但内容已变
|
|
81
|
+
const v2 = V1.replace('10000', '20000');
|
|
82
|
+
const { effective } = resolveEffective([stale], hashLookup(v2));
|
|
83
|
+
assert.strictEqual(effective, undefined, '唯一的 proof 已失效,不应选出任何有效结论。');
|
|
84
|
+
});
|
|
85
|
+
it('★并存的分歧必须如实暴露,不静默吞掉', () => {
|
|
86
|
+
const anchor = thresholdNode(V1);
|
|
87
|
+
const machine = proofOf(anchor, {
|
|
88
|
+
recordedAt: '2026-09-13T00:00:00Z', verdict: 'REJECTED',
|
|
89
|
+
subject: { kind: 'VERIFIER', by: 'mapping-ir/v1' },
|
|
90
|
+
});
|
|
91
|
+
const expert = proofOf(anchor, {
|
|
92
|
+
recordedAt: '2026-09-12T00:00:00Z', verdict: 'VERIFIED',
|
|
93
|
+
subject: { kind: 'DOMAIN_EXPERT', by: 'alice' },
|
|
94
|
+
});
|
|
95
|
+
const { effective, conflicts } = resolveEffective([machine, expert], hashLookup(V1));
|
|
96
|
+
assert.strictEqual(effective?.verdict, 'REJECTED', '按时间应取机器那条。');
|
|
97
|
+
assert.strictEqual(conflicts.length, 1, '专家的相反结论必须出现在 conflicts 里 —— 静默吞掉会让「机器与专家分歧」这件事消失。');
|
|
98
|
+
assert.strictEqual(conflicts[0]?.subject.kind, 'DOMAIN_EXPERT');
|
|
99
|
+
});
|
|
100
|
+
it('结论相同的多条 proof 不算冲突', () => {
|
|
101
|
+
const anchor = thresholdNode(V1);
|
|
102
|
+
const a = proofOf(anchor, { recordedAt: '2026-09-13T00:00:00Z' });
|
|
103
|
+
const b = proofOf(anchor, { recordedAt: '2026-09-12T00:00:00Z' });
|
|
104
|
+
const { conflicts } = resolveEffective([a, b], hashLookup(V1));
|
|
105
|
+
assert.strictEqual(conflicts.length, 0, '同结论不应报冲突(否则噪声淹没真分歧)。');
|
|
106
|
+
});
|
|
107
|
+
it('空输入不崩', () => {
|
|
108
|
+
assert.deepStrictEqual(resolveEffective([], () => 'x'), { conflicts: [] });
|
|
109
|
+
});
|
|
110
|
+
it('★proof 必须记录主体与规则(§2 的「谁验的、按什么规则」)', () => {
|
|
111
|
+
const anchor = thresholdNode(V1);
|
|
112
|
+
const p = proofOf(anchor);
|
|
113
|
+
// 这两项是 ADR §2 明确要求 ProofIR 回答的问题;类型上是必填,
|
|
114
|
+
// 此处再钉一次运行时形态,防止将来被改成可选后悄悄缺失。
|
|
115
|
+
assert.ok(p.subject.kind && p.subject.by, 'subject 必须可追溯到具体主体。');
|
|
116
|
+
assert.ok(p.rule.id && p.rule.version, 'rule 必须带版本 —— 规则本身会演进,旧 proof 要说清当时用的哪一版。');
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
//# sourceMappingURL=proof-ir.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proof-ir.test.js","sourceRoot":"","sources":["../../../../test/unit/mapping/proof-ir.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AAExC,OAAO,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAC;AACtE,OAAO,EAAE,GAAG,EAAE,MAAM,gCAAgC,CAAC;AACrD,OAAO,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAE5D,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAGpF;;;;;GAKG;AAEH,MAAM,EAAE,GAAG;IACT,sBAAsB,EAAE,EAAE;IAC1B,qCAAqC;IACrC,iCAAiC;IACjC,qBAAqB;IACrB,cAAc;IACd,uBAAuB,EAAE,EAAE;CAC5B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb,MAAM,KAAK,GAAG,CAAC,GAAW,EAAE,EAAE,CAC5B,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;AAE1G,0BAA0B;AAC1B,SAAS,aAAa,CAAC,GAAW;IAChC,KAAK,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACjC,IAAI,CAAC,CAAC,IAAI,KAAK,KAAK;YAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IAC1E,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,YAAY,CAAC,CAAC;AAChC,CAAC;AAED,SAAS,OAAO,CAAC,MAA+C,EAC/C,YAA4B,EAAE;IAC7C,OAAO;QACL,OAAO,EAAE,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;QAC/E,OAAO,EAAE,UAAU;QACnB,OAAO,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,eAAe,EAAE;QAClD,IAAI,EAAE,EAAE,EAAE,EAAE,aAAa,EAAE,OAAO,EAAE,GAAG,EAAE;QACzC,eAAe,EAAE,MAAM;QACvB,MAAM,EAAE,0BAA0B;QAClC,UAAU,EAAE,sBAAsB;QAClC,GAAG,SAAS;KACb,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,CAAC,EAAU,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,WAAW,CAAC;AAEpF,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACxC,EAAE,CAAC,mBAAmB,EAAE,GAAG,EAAE;QAC3B,MAAM,MAAM,GAAG,aAAa,CAAC,EAAE,CAAC,CAAC;QACjC,MAAM,CAAC,GAAG,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;QAC1D,MAAM,CAAC,eAAe,CAAC,CAAC,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,MAAM,GAAG,aAAa,CAAC,EAAE,CAAC,CAAC;QACjC,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;QAC9B,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAErC,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxC,MAAM,CAAC,cAAc,CAAC,EAAE,EAAE,EAAE,EAAE,qBAAqB,CAAC,CAAC;QAErD,MAAM,CAAC,GAAG,cAAc,CAAC,KAAK,EAAE,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;QAChD,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,EAAE,KAAK,EAAE,mBAAmB,CAAC,CAAC;QAC7D,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,iBAAiB,CAAC,CAAC;QAE3E,qCAAqC;QACrC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,MAAM,EAC9C,qCAAqC,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,MAAM,GAAG,aAAa,CAAC,EAAE,CAAC,CAAC;QACjC,MAAM,CAAC,GAAG,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC3D,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QACxC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mBAAmB,EAAE,GAAG,EAAE;QAC3B,MAAM,MAAM,GAAG,aAAa,CAAC,EAAE,CAAC,CAAC;QACjC,MAAM,GAAG,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,UAAU,EAAE,sBAAsB,EAAE,OAAO,EAAE,iBAAiB,EAAE,CAAC,CAAC;QAChG,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,UAAU,EAAE,sBAAsB,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC;QAE5F,MAAM,EAAE,SAAS,EAAE,GAAG,gBAAgB,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;QACtE,MAAM,CAAC,WAAW,CAAC,SAAS,EAAE,UAAU,EAAE,sBAAsB,EAC9D,UAAU,CAAC,CAAC;IAChB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,4CAA4C;QAC5C,qCAAqC;QACrC,MAAM,MAAM,GAAG,aAAa,CAAC,EAAE,CAAC,CAAC;QACjC,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,UAAU,EAAE,sBAAsB,EAAE,CAAC,CAAC,CAAC,YAAY;QAEnF,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxC,MAAM,EAAE,SAAS,EAAE,GAAG,gBAAgB,CAAC,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;QAEhE,MAAM,CAAC,WAAW,CAAC,SAAS,EAAE,SAAS,EACrC,2BAA2B,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oBAAoB,EAAE,GAAG,EAAE;QAC5B,MAAM,MAAM,GAAG,aAAa,CAAC,EAAE,CAAC,CAAC;QACjC,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,EAAE;YAC9B,UAAU,EAAE,sBAAsB,EAAE,OAAO,EAAE,UAAU;YACvD,OAAO,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,eAAe,EAAE;SACnD,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE;YAC7B,UAAU,EAAE,sBAAsB,EAAE,OAAO,EAAE,UAAU;YACvD,OAAO,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,EAAE,EAAE,OAAO,EAAE;SAChD,CAAC,CAAC;QAEH,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,gBAAgB,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;QAErF,MAAM,CAAC,WAAW,CAAC,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC;QACjE,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,EACpC,mDAAmD,CAAC,CAAC;QACvD,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oBAAoB,EAAE,GAAG,EAAE;QAC5B,MAAM,MAAM,GAAG,aAAa,CAAC,EAAE,CAAC,CAAC;QACjC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,UAAU,EAAE,sBAAsB,EAAE,CAAC,CAAC;QAClE,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,UAAU,EAAE,sBAAsB,EAAE,CAAC,CAAC;QAElE,MAAM,EAAE,SAAS,EAAE,GAAG,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;QAC/D,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,sBAAsB,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;QACf,MAAM,CAAC,eAAe,CAAC,gBAAgB,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,MAAM,GAAG,aAAa,CAAC,EAAE,CAAC,CAAC;QACjC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;QAC1B,yCAAyC;QACzC,8BAA8B;QAC9B,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,qBAAqB,CAAC,CAAC;QACjE,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,EACnC,2CAA2C,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quantity-ir.test.d.ts","sourceRoot":"","sources":["../../../../test/unit/mapping/quantity-ir.test.ts"],"names":[],"mappings":""}
|