@sema-agent/settings-schema 2.1.0 → 3.0.1

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 (203) hide show
  1. package/CHANGELOG.md +253 -1
  2. package/README.md +45 -10
  3. package/conformance/VERSION +1 -0
  4. package/conformance/baseline.json +55 -0
  5. package/conformance/cases/c01-entry-salvage-then-typo/expected.json +105 -0
  6. package/conformance/cases/c01-entry-salvage-then-typo/input.json +22 -0
  7. package/conformance/cases/c01-entry-salvage-then-typo/meta.json +10 -0
  8. package/conformance/cases/c02-hosts-clamp-stacked/expected.json +96 -0
  9. package/conformance/cases/c02-hosts-clamp-stacked/input.json +21 -0
  10. package/conformance/cases/c02-hosts-clamp-stacked/meta.json +10 -0
  11. package/conformance/cases/c03-literal-dotted-top-key/expected.json +112 -0
  12. package/conformance/cases/c03-literal-dotted-top-key/input.json +26 -0
  13. package/conformance/cases/c03-literal-dotted-top-key/meta.json +5 -0
  14. package/conformance/cases/c04-all-bad-collection-not-salvaged/expected.json +77 -0
  15. package/conformance/cases/c04-all-bad-collection-not-salvaged/input.json +18 -0
  16. package/conformance/cases/c04-all-bad-collection-not-salvaged/meta.json +5 -0
  17. package/conformance/cases/c05-gate-domain-bad-entry-throws/expected.json +16 -0
  18. package/conformance/cases/c05-gate-domain-bad-entry-throws/input.json +18 -0
  19. package/conformance/cases/c05-gate-domain-bad-entry-throws/meta.json +5 -0
  20. package/conformance/cases/c06-legacy-governance-lift/expected.json +99 -0
  21. package/conformance/cases/c06-legacy-governance-lift/input.json +20 -0
  22. package/conformance/cases/c06-legacy-governance-lift/meta.json +9 -0
  23. package/conformance/cases/c07-hooks-is-not-a-config-domain/expected.json +72 -0
  24. package/conformance/cases/c07-hooks-is-not-a-config-domain/input.json +43 -0
  25. package/conformance/cases/c07-hooks-is-not-a-config-domain/meta.json +6 -0
  26. package/conformance/cases/c08-limits-unknown-carried-64/expected.json +336 -0
  27. package/conformance/cases/c08-limits-unknown-carried-64/input.json +74 -0
  28. package/conformance/cases/c08-limits-unknown-carried-64/meta.json +5 -0
  29. package/conformance/cases/c09-limits-unknown-carried-65/expected.json +338 -0
  30. package/conformance/cases/c09-limits-unknown-carried-65/input.json +75 -0
  31. package/conformance/cases/c09-limits-unknown-carried-65/meta.json +7 -0
  32. package/conformance/cases/c10-record-key-secret-redacted-twice/expected.json +106 -0
  33. package/conformance/cases/c10-record-key-secret-redacted-twice/input.json +29 -0
  34. package/conformance/cases/c10-record-key-secret-redacted-twice/meta.json +13 -0
  35. package/conformance/cases/c11-unicode-unknown-keys/expected.json +88 -0
  36. package/conformance/cases/c11-unicode-unknown-keys/input.json +13 -0
  37. package/conformance/cases/c11-unicode-unknown-keys/meta.json +5 -0
  38. package/conformance/cases/c12-prototype-name-top-keys/expected.json +72 -0
  39. package/conformance/cases/c12-prototype-name-top-keys/input.json +20 -0
  40. package/conformance/cases/c12-prototype-name-top-keys/meta.json +11 -0
  41. package/conformance/cases/c13-models-field-null/expected.json +77 -0
  42. package/conformance/cases/c13-models-field-null/input.json +11 -0
  43. package/conformance/cases/c13-models-field-null/meta.json +5 -0
  44. package/conformance/cases/c14-models-field-empty-array/expected.json +72 -0
  45. package/conformance/cases/c14-models-field-empty-array/input.json +11 -0
  46. package/conformance/cases/c14-models-field-empty-array/meta.json +5 -0
  47. package/conformance/cases/c15-models-field-number/expected.json +77 -0
  48. package/conformance/cases/c15-models-field-number/input.json +11 -0
  49. package/conformance/cases/c15-models-field-number/meta.json +5 -0
  50. package/conformance/cases/c16-deep-nesting-205/expected.json +83 -0
  51. package/conformance/cases/c16-deep-nesting-205/input.json +421 -0
  52. package/conformance/cases/c16-deep-nesting-205/meta.json +5 -0
  53. package/conformance/cases/c17-empty-domain-file/expected.json +73 -0
  54. package/conformance/cases/c17-empty-domain-file/input.json +10 -0
  55. package/conformance/cases/c17-empty-domain-file/meta.json +5 -0
  56. package/conformance/cases/c18-wire-envelope-not-an-object/expected.json +74 -0
  57. package/conformance/cases/c18-wire-envelope-not-an-object/input.json +8 -0
  58. package/conformance/cases/c18-wire-envelope-not-an-object/meta.json +5 -0
  59. package/conformance/cases/c19-wire-envelope-no-version/expected.json +74 -0
  60. package/conformance/cases/c19-wire-envelope-no-version/input.json +13 -0
  61. package/conformance/cases/c19-wire-envelope-no-version/meta.json +5 -0
  62. package/conformance/cases/c20-governance-absent-lifts/expected.json +84 -0
  63. package/conformance/cases/c20-governance-absent-lifts/input.json +11 -0
  64. package/conformance/cases/c20-governance-absent-lifts/meta.json +9 -0
  65. package/conformance/cases/c21-governance-empty-object-wins/expected.json +72 -0
  66. package/conformance/cases/c21-governance-empty-object-wins/input.json +12 -0
  67. package/conformance/cases/c21-governance-empty-object-wins/meta.json +5 -0
  68. package/conformance/cases/c22-governance-null-throws/expected.json +16 -0
  69. package/conformance/cases/c22-governance-null-throws/input.json +9 -0
  70. package/conformance/cases/c22-governance-null-throws/meta.json +5 -0
  71. package/conformance/cases/c23-mixed-validity-models/expected.json +106 -0
  72. package/conformance/cases/c23-mixed-validity-models/input.json +27 -0
  73. package/conformance/cases/c23-mixed-validity-models/meta.json +9 -0
  74. package/conformance/cases/c24-valid-entries-with-unknown-keys/expected.json +100 -0
  75. package/conformance/cases/c24-valid-entries-with-unknown-keys/input.json +25 -0
  76. package/conformance/cases/c24-valid-entries-with-unknown-keys/meta.json +5 -0
  77. package/conformance/cases/c25-published-roundtrip-zero-warnings/expected.json +89 -0
  78. package/conformance/cases/c25-published-roundtrip-zero-warnings/input.json +24 -0
  79. package/conformance/cases/c25-published-roundtrip-zero-warnings/meta.json +5 -0
  80. package/conformance/cases/c26-hosts-array-1024/expected.json +7241 -0
  81. package/conformance/cases/c26-hosts-array-1024/input.json +3084 -0
  82. package/conformance/cases/c26-hosts-array-1024/meta.json +5 -0
  83. package/conformance/cases/c27-hosts-array-1025/expected.json +7248 -0
  84. package/conformance/cases/c27-hosts-array-1025/input.json +3087 -0
  85. package/conformance/cases/c27-hosts-array-1025/meta.json +5 -0
  86. package/conformance/cases/c28-large-document-over-1mb/expected.json +82 -0
  87. package/conformance/cases/c28-large-document-over-1mb/input.json +11 -0
  88. package/conformance/cases/c28-large-document-over-1mb/meta.json +5 -0
  89. package/conformance/cases/c29-salvage-then-domain-refine-fails/expected.json +77 -0
  90. package/conformance/cases/c29-salvage-then-domain-refine-fails/input.json +27 -0
  91. package/conformance/cases/c29-salvage-then-domain-refine-fails/meta.json +5 -0
  92. package/conformance/cases/c30-version-triple-a-first/expected.json +85 -0
  93. package/conformance/cases/c30-version-triple-a-first/input.json +18 -0
  94. package/conformance/cases/c30-version-triple-a-first/meta.json +5 -0
  95. package/conformance/cases/c31-version-triple-b-changed/expected.json +85 -0
  96. package/conformance/cases/c31-version-triple-b-changed/input.json +18 -0
  97. package/conformance/cases/c31-version-triple-b-changed/meta.json +5 -0
  98. package/conformance/cases/c32-version-triple-a-restored/expected.json +85 -0
  99. package/conformance/cases/c32-version-triple-a-restored/input.json +18 -0
  100. package/conformance/cases/c32-version-triple-a-restored/meta.json +5 -0
  101. package/conformance/cases/c33-limits-nested-carried-key/expected.json +91 -0
  102. package/conformance/cases/c33-limits-nested-carried-key/input.json +14 -0
  103. package/conformance/cases/c33-limits-nested-carried-key/meta.json +5 -0
  104. package/conformance/cases/c34-skills-content-vs-manifest/expected.json +160 -0
  105. package/conformance/cases/c34-skills-content-vs-manifest/input.json +18 -0
  106. package/conformance/cases/c34-skills-content-vs-manifest/meta.json +5 -0
  107. package/conformance/cases/c35-portable-domain-file-is-null/expected.json +76 -0
  108. package/conformance/cases/c35-portable-domain-file-is-null/input.json +9 -0
  109. package/conformance/cases/c35-portable-domain-file-is-null/meta.json +5 -0
  110. package/conformance/cases/c36-skills-entry-without-content/expected.json +144 -0
  111. package/conformance/cases/c36-skills-entry-without-content/input.json +16 -0
  112. package/conformance/cases/c36-skills-entry-without-content/meta.json +5 -0
  113. package/conformance/cases/c37-skills-field-null/expected.json +134 -0
  114. package/conformance/cases/c37-skills-field-null/input.json +9 -0
  115. package/conformance/cases/c37-skills-field-null/meta.json +5 -0
  116. package/conformance/cases/c38-remote-exec-is-a-non-domain-file/expected.json +72 -0
  117. package/conformance/cases/c38-remote-exec-is-a-non-domain-file/input.json +11 -0
  118. package/conformance/cases/c38-remote-exec-is-a-non-domain-file/meta.json +5 -0
  119. package/conformance/cases/c39-hidden-file-in-config-d/expected.json +72 -0
  120. package/conformance/cases/c39-hidden-file-in-config-d/input.json +11 -0
  121. package/conformance/cases/c39-hidden-file-in-config-d/meta.json +5 -0
  122. package/conformance/cases/cc01-mcp-toolfaces-non-object/expected.json +112 -0
  123. package/conformance/cases/cc01-mcp-toolfaces-non-object/input.json +39 -0
  124. package/conformance/cases/cc01-mcp-toolfaces-non-object/meta.json +8 -0
  125. package/conformance/cases/cc02-mcp-source-key-is-transported/expected.json +85 -0
  126. package/conformance/cases/cc02-mcp-source-key-is-transported/input.json +20 -0
  127. package/conformance/cases/cc02-mcp-source-key-is-transported/meta.json +8 -0
  128. package/conformance/cases/cc03-model-compat-unknown-subkey/expected.json +89 -0
  129. package/conformance/cases/cc03-model-compat-unknown-subkey/input.json +22 -0
  130. package/conformance/cases/cc03-model-compat-unknown-subkey/meta.json +8 -0
  131. package/conformance/cases/cli01-model-name-nfc-vs-nfd/expected.json +102 -0
  132. package/conformance/cases/cli01-model-name-nfc-vs-nfd/input.json +30 -0
  133. package/conformance/cases/cli01-model-name-nfc-vs-nfd/meta.json +9 -0
  134. package/conformance/cases/cli02-control-and-zero-width-chars/expected.json +115 -0
  135. package/conformance/cases/cli02-control-and-zero-width-chars/input.json +42 -0
  136. package/conformance/cases/cli02-control-and-zero-width-chars/meta.json +9 -0
  137. package/conformance/cases/cli03-credential-pasted-into-unknown-key/expected.json +92 -0
  138. package/conformance/cases/cli03-credential-pasted-into-unknown-key/input.json +16 -0
  139. package/conformance/cases/cli03-credential-pasted-into-unknown-key/meta.json +9 -0
  140. package/conformance/cases/cli04-env-value-with-newline/expected.json +93 -0
  141. package/conformance/cases/cli04-env-value-with-newline/input.json +23 -0
  142. package/conformance/cases/cli04-env-value-with-newline/meta.json +5 -0
  143. package/conformance/cases/cli05-long-and-prefix-colliding-names/expected.json +109 -0
  144. package/conformance/cases/cli05-long-and-prefix-colliding-names/input.json +30 -0
  145. package/conformance/cases/cli05-long-and-prefix-colliding-names/meta.json +5 -0
  146. package/conformance/cases/t01-absent-domain-key-not-null/expected.json +93 -0
  147. package/conformance/cases/t01-absent-domain-key-not-null/input.json +29 -0
  148. package/conformance/cases/t01-absent-domain-key-not-null/meta.json +6 -0
  149. package/conformance/cases/t02-entry-salvage-vs-whole-domain-refusal/expected.json +16 -0
  150. package/conformance/cases/t02-entry-salvage-vs-whole-domain-refusal/input.json +32 -0
  151. package/conformance/cases/t02-entry-salvage-vs-whole-domain-refusal/meta.json +6 -0
  152. package/conformance/cases/t03-prototype-name-key-not-swallowed/expected.json +107 -0
  153. package/conformance/cases/t03-prototype-name-key-not-swallowed/input.json +25 -0
  154. package/conformance/cases/t03-prototype-name-key-not-swallowed/meta.json +6 -0
  155. package/conformance/cases/t04-secret-literal-never-in-warning-value/expected.json +105 -0
  156. package/conformance/cases/t04-secret-literal-never-in-warning-value/input.json +25 -0
  157. package/conformance/cases/t04-secret-literal-never-in-warning-value/meta.json +14 -0
  158. package/conformance/cases/z01-every-domain-empty-object/expected.json +72 -0
  159. package/conformance/cases/z01-every-domain-empty-object/input.json +25 -0
  160. package/conformance/cases/z01-every-domain-empty-object/meta.json +5 -0
  161. package/conformance/cases/z02-every-domain-unknown-key/expected.json +228 -0
  162. package/conformance/cases/z02-every-domain-unknown-key/input.json +59 -0
  163. package/conformance/cases/z02-every-domain-unknown-key/meta.json +5 -0
  164. package/conformance/historical/settings-schema@1.0.0.json +1080 -0
  165. package/conformance/historical/settings-schema@1.10.0.json +1080 -0
  166. package/conformance/historical/settings-schema@1.5.0.json +1080 -0
  167. package/conformance/historical/settings-schema@2.0.0.json +1080 -0
  168. package/conformance/historical/settings-schema@2.1.0.json +1080 -0
  169. package/dist/api/auth.d.ts +42 -1
  170. package/dist/api/auth.js +71 -4
  171. package/dist/api/scopes.d.ts +9 -1
  172. package/dist/api/scopes.js +8 -2
  173. package/dist/config-fns.d.ts +60 -28
  174. package/dist/config-fns.js +94 -77
  175. package/dist/conformance/canonical.d.ts +36 -0
  176. package/dist/conformance/canonical.js +114 -0
  177. package/dist/conformance/contract-adapter.d.ts +20 -0
  178. package/dist/conformance/contract-adapter.js +74 -0
  179. package/dist/conformance/corpus.d.ts +73 -0
  180. package/dist/conformance/corpus.js +99 -0
  181. package/dist/conformance/derive.d.ts +48 -0
  182. package/dist/conformance/derive.js +136 -0
  183. package/dist/conformance/index.d.ts +22 -0
  184. package/dist/conformance/index.js +22 -0
  185. package/dist/conformance/run.d.ts +27 -0
  186. package/dist/conformance/run.js +424 -0
  187. package/dist/conformance/types.d.ts +288 -0
  188. package/dist/conformance/types.js +4 -0
  189. package/dist/conformance/warnings.d.ts +88 -0
  190. package/dist/conformance/warnings.js +144 -0
  191. package/dist/cross-domain.d.ts +1 -1
  192. package/dist/cross-domain.js +1 -1
  193. package/dist/file-store.d.ts +7 -0
  194. package/dist/file-store.js +6 -2
  195. package/dist/hooks.d.ts +2 -2
  196. package/dist/hooks.js +2 -2
  197. package/dist/scheduler-store.d.ts +1 -1
  198. package/dist/scheduler-store.js +1 -1
  199. package/dist/types.d.ts +2 -2
  200. package/dist/types.js +3 -3
  201. package/package.json +7 -5
  202. package/dist/api/wire.d.ts +0 -60
  203. package/dist/api/wire.js +0 -89
@@ -0,0 +1,73 @@
1
+ import type { WarningProjection } from "./warnings.js";
2
+ import type { LegName, RejectStage } from "./types.js";
3
+ /** 本包的根目录 —— `src/conformance/*.ts` 与 `dist/conformance/*.js` 距根同深,两处求值同一个地方。 */
4
+ export declare const PACKAGE_ROOT: string;
5
+ /** 语料目录(默认 = 本包的 `conformance/`)。 */
6
+ export declare const DEFAULT_CORPUS_DIR: string;
7
+ /** 语料里一条腿的期望(磁盘形)。 */
8
+ export type LegExpectationJson = {
9
+ derive: true;
10
+ localVersion?: number;
11
+ } | {
12
+ applicable: false;
13
+ why: string;
14
+ } | {
15
+ outcome: "effective";
16
+ effective: Record<string, unknown>;
17
+ warnings: WarningProjection[];
18
+ metadata: {
19
+ version?: number;
20
+ updatedAt: string;
21
+ };
22
+ why?: string;
23
+ } | {
24
+ outcome: "rejected";
25
+ stage: RejectStage;
26
+ code?: string;
27
+ why?: string;
28
+ };
29
+ export interface CaseInput {
30
+ meta: {
31
+ version: number;
32
+ updatedAt: string;
33
+ };
34
+ domains: Record<string, unknown>;
35
+ /** 派生不出来的 wire 入参(坏信封:不是对象 / 缺 version / version 非有限数)。 */
36
+ wirePayload?: unknown;
37
+ /** 派生不出来的 `config.d/` 快照(空文件、非 JSON 文本、多余文件)。键 = 文件名。 */
38
+ localFiles?: Record<string, string>;
39
+ }
40
+ export interface CaseMeta {
41
+ /** **只写真交了的仓**:未交的仓留空位,不代写。 */
42
+ contributor: "server" | "web-admin" | "cli" | "client-core" | "test" | "core" | "contract";
43
+ /** 这条想打谁、为什么会读错。非空是对抗律的硬判据。 */
44
+ claim: string;
45
+ /** 语料里埋的秘密字面(告警面零命中门)。 */
46
+ secrets?: string[];
47
+ /** **historical 模式真跑**出来的「在哪些旧版上这条是红的」。空 = 护栏(也收,计数分开)。 */
48
+ firstRedOn: string[];
49
+ /** 人读补注(可空)。 */
50
+ note?: string;
51
+ }
52
+ export interface ConformanceCase {
53
+ id: string;
54
+ dir: string;
55
+ input: CaseInput;
56
+ meta: CaseMeta;
57
+ expected: Record<LegName, LegExpectationJson>;
58
+ }
59
+ export interface Corpus {
60
+ version: string;
61
+ /** 空文档集的 effective(冻结的 golden;非便携域的那一格由它供给,见 derive.ts T3)。 */
62
+ baseline: Record<string, unknown>;
63
+ cases: readonly ConformanceCase[];
64
+ }
65
+ /** 一个 case 目录 → 校验过的 case。 */
66
+ export declare function loadCase(dir: string, id: string): Promise<ConformanceCase>;
67
+ /** 整套语料。 */
68
+ export declare function loadCorpus(corpusDir?: string): Promise<Corpus>;
69
+ /** kit 自己所在那份包的身份(名 + 版号)—— 来源律拿它去认「适配器加载的是不是同一个包」。 */
70
+ export declare function kitIdentity(): Promise<{
71
+ name: string;
72
+ version: string;
73
+ }>;
@@ -0,0 +1,99 @@
1
+ /**
2
+ * 语料的**磁盘形**与加载器。语料随 npm 发(`package.json` 的 `files` 里登记 `conformance`),所以
3
+ * 消费方装上包就有整套金样,零复制。
4
+ *
5
+ * 一条 case = 一个目录 `conformance/cases/<id>/`:
6
+ * · `input.json` —— **真源是 `domains`**(各域原始文档);`wire` / `local-root` 两腿的入参由
7
+ * `derive.ts` 的变换表派生。个别腿的入参派生不出来(坏 wire 信封、空文件)时,
8
+ * 用 `wirePayload` / `localFiles` 显式给。
9
+ * · `expected.json` —— `domains` 腿的期望是**显式 golden**;另两腿默认写 `{"derive":true}`,由变换表
10
+ * 派生;派生解释不了的腿写显式期望并带 `why`(那是明示声明,不是暗门)。
11
+ * · `meta.json` —— `contributor`(**只写真交了的仓**)、`claim`(这条想打谁、为什么会读错)、
12
+ * `secrets`(语料里埋的秘密字面,告警面零命中门用)、`firstRedOn`(**由 historical
13
+ * 模式对旧版真跑填**,不许手写)。
14
+ *
15
+ * 加载器对语料本身 fail-loud:形不对就抛。一份被悄悄读错的语料,比没有语料更坏。
16
+ */
17
+ import { readFile, readdir } from "node:fs/promises";
18
+ import { dirname, join } from "node:path";
19
+ import { fileURLToPath } from "node:url";
20
+ import { REJECT_STAGES } from "./types.js";
21
+ /** 本包的根目录 —— `src/conformance/*.ts` 与 `dist/conformance/*.js` 距根同深,两处求值同一个地方。 */
22
+ export const PACKAGE_ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
23
+ /** 语料目录(默认 = 本包的 `conformance/`)。 */
24
+ export const DEFAULT_CORPUS_DIR = join(PACKAGE_ROOT, "conformance");
25
+ const readJson = async (p) => JSON.parse(await readFile(p, "utf8"));
26
+ const isRecord = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
27
+ function requireRecord(v, where) {
28
+ if (!isRecord(v))
29
+ throw new Error(`conformance corpus: ${where} must be a JSON object`);
30
+ return v;
31
+ }
32
+ function checkLegExpectation(v, where) {
33
+ const r = requireRecord(v, where);
34
+ if (r.derive === true) {
35
+ if (r.localVersion !== undefined && typeof r.localVersion !== "number")
36
+ throw new Error(`conformance corpus: ${where}.localVersion must be a number`);
37
+ return r;
38
+ }
39
+ if (r.applicable === false) {
40
+ if (typeof r.why !== "string" || r.why.length === 0)
41
+ throw new Error(`conformance corpus: ${where} declares applicable:false without a \`why\` — a leg that does not apply must say why`);
42
+ return r;
43
+ }
44
+ if (r.outcome === "effective") {
45
+ requireRecord(r.effective, `${where}.effective`);
46
+ if (!Array.isArray(r.warnings))
47
+ throw new Error(`conformance corpus: ${where}.warnings must be an array`);
48
+ const meta = requireRecord(r.metadata, `${where}.metadata`);
49
+ if (typeof meta.updatedAt !== "string")
50
+ throw new Error(`conformance corpus: ${where}.metadata.updatedAt must be a string`);
51
+ return r;
52
+ }
53
+ if (r.outcome === "rejected") {
54
+ if (!REJECT_STAGES.includes(r.stage))
55
+ throw new Error(`conformance corpus: ${where}.stage must be one of ${REJECT_STAGES.join("|")}`);
56
+ return r;
57
+ }
58
+ throw new Error(`conformance corpus: ${where} is none of {derive:true} | {applicable:false,why} | {outcome:"effective"} | {outcome:"rejected"}`);
59
+ }
60
+ /** 一个 case 目录 → 校验过的 case。 */
61
+ export async function loadCase(dir, id) {
62
+ const [inputRaw, metaRaw, expectedRaw] = await Promise.all([readJson(join(dir, "input.json")), readJson(join(dir, "meta.json")), readJson(join(dir, "expected.json"))]);
63
+ const input = requireRecord(inputRaw, `${id}/input.json`);
64
+ const meta = requireRecord(metaRaw, `${id}/meta.json`);
65
+ const expected = requireRecord(expectedRaw, `${id}/expected.json`);
66
+ const inputMeta = requireRecord(input.meta, `${id}/input.json#meta`);
67
+ if (typeof inputMeta.version !== "number" || typeof inputMeta.updatedAt !== "string")
68
+ throw new Error(`conformance corpus: ${id}/input.json#meta needs {version:number, updatedAt:string}`);
69
+ requireRecord(input.domains, `${id}/input.json#domains`);
70
+ if (typeof meta.claim !== "string" || meta.claim.trim().length === 0)
71
+ throw new Error(`conformance corpus: ${id}/meta.json#claim is empty — 每条语料必须写它想打谁`);
72
+ if (!Array.isArray(meta.firstRedOn))
73
+ throw new Error(`conformance corpus: ${id}/meta.json#firstRedOn must be an array`);
74
+ const legs = {};
75
+ for (const leg of ["domains", "wire", "localRoot"]) {
76
+ if (expected[leg] === undefined)
77
+ throw new Error(`conformance corpus: ${id}/expected.json has no \`${leg}\` — 每腿都要表态(派生 / 显式 / 不适用)`);
78
+ legs[leg] = checkLegExpectation(expected[leg], `${id}/expected.json#${leg}`);
79
+ }
80
+ if ("derive" in legs.domains)
81
+ throw new Error(`conformance corpus: ${id}/expected.json#domains cannot be derived — domains 腿是真源`);
82
+ return { id, dir, input: input, meta: meta, expected: legs };
83
+ }
84
+ /** 整套语料。 */
85
+ export async function loadCorpus(corpusDir = DEFAULT_CORPUS_DIR) {
86
+ const version = (await readFile(join(corpusDir, "VERSION"), "utf8")).trim();
87
+ const baseline = requireRecord(await readJson(join(corpusDir, "baseline.json")), "conformance/baseline.json");
88
+ const casesDir = join(corpusDir, "cases");
89
+ const ids = (await readdir(casesDir, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name).sort();
90
+ const cases = await Promise.all(ids.map((id) => loadCase(join(casesDir, id), id)));
91
+ return { version, baseline, cases };
92
+ }
93
+ /** kit 自己所在那份包的身份(名 + 版号)—— 来源律拿它去认「适配器加载的是不是同一个包」。 */
94
+ export async function kitIdentity() {
95
+ const pkg = requireRecord(await readJson(join(PACKAGE_ROOT, "package.json")), "package.json");
96
+ if (typeof pkg.name !== "string" || typeof pkg.version !== "string")
97
+ throw new Error("conformance: package.json has no name/version");
98
+ return { name: pkg.name, version: pkg.version };
99
+ }
@@ -0,0 +1,48 @@
1
+ import { type DomainName } from "../types.js";
2
+ import type { WarningProjection } from "./warnings.js";
3
+ import type { LegName, LegInput, RejectStage } from "./types.js";
4
+ /** 一条登记在案的表示差异。 */
5
+ export interface DerivationTransform {
6
+ id: string;
7
+ legs: readonly LegName[];
8
+ what: string;
9
+ }
10
+ /** 变换表(本文件头注的机器形;`docs/CONFORMANCE.md` 与 `test/conformance-coverage.test.ts` 都读它)。 */
11
+ export declare const DERIVATION_TRANSFORMS: readonly DerivationTransform[];
12
+ /** 非便携域 —— 由 {@link PORTABLE_DOMAINS} 派生,零手抄。 */
13
+ export declare const NON_PORTABLE_DOMAINS: readonly DomainName[];
14
+ /** 一份 `domains` 文档集里,**不是域名**的那些顶层键(T4 的触发面)。 */
15
+ export declare const foreignKeysOf: (docs: Readonly<Record<string, unknown>>) => string[];
16
+ /** T2 的**入参**半场:域形的 skills 文档 → wire 形的 manifest 文档。结构变换,不解析 —— 坏形原样传下去,
17
+ * 让 wire 腿自己的判据去裁(否则语料就没法喂一份坏 manifest 了)。 */
18
+ export declare function toManifestDoc(doc: unknown): unknown;
19
+ /** `domains` 真源 → `wire` 腿入参(T1 缺席进缺席出 + T2 表示换 + T4 未知键照原样带上)。 */
20
+ export declare function deriveWireInput(docs: Readonly<Record<string, unknown>>, version: number, updatedAt: string): LegInput;
21
+ /** `domains` 真源 → `local-root` 腿入参(每个顶层键一个 `config.d/<键>.json`,含非域名的那些 —— T3/T4
22
+ * 的告警正是靠它们在目录里在场才会响)。 */
23
+ export declare function deriveLocalInput(docs: Readonly<Record<string, unknown>>, mtime: string): LegInput;
24
+ /** 一条腿的期望(派生结果与语料显式期望共用同一形)。 */
25
+ export type LegExpectation = {
26
+ outcome: "effective";
27
+ effective: Record<string, unknown>;
28
+ warnings: readonly WarningProjection[];
29
+ metadata: {
30
+ version?: number;
31
+ updatedAt: string;
32
+ };
33
+ } | {
34
+ outcome: "rejected";
35
+ stage: RejectStage;
36
+ code?: string;
37
+ };
38
+ /** T1 + T2 + T4:`domains` 期望 → `wire` 期望。 */
39
+ export declare function deriveWireExpectation(source: Extract<LegExpectation, {
40
+ outcome: "effective";
41
+ }>, docs: Readonly<Record<string, unknown>>): LegExpectation;
42
+ /** 纪元时刻 —— 一个 `config.d/` 里没有任何**便携**域文件时 `updatedAt` 的取值(`#maxMtimeIso` 只 stat 便携域)。 */
43
+ export declare const EPOCH_ISO: string;
44
+ /** T3 + T4 + T5:`domains` 期望 → `local-root` 期望。`baseline` = 空文档集的 effective(语料里冻结的
45
+ * `conformance/baseline.json`),非便携域取它的那一格 —— 不现算,免得 schema 默认一改 golden 就跟着动。 */
46
+ export declare function deriveLocalExpectation(source: Extract<LegExpectation, {
47
+ outcome: "effective";
48
+ }>, docs: Readonly<Record<string, unknown>>, baseline: Readonly<Record<string, unknown>>, localVersion: number | undefined): LegExpectation;
@@ -0,0 +1,136 @@
1
+ /**
2
+ * **三腿等价先定义再比**:语料以 `domains`(各域原始文档)为真源,`wire` 与 `local-root` 两腿的**入参**
3
+ * 与**期望**都由本文件派生 —— 派生规则就是三条腿之间那几处**表示差异**的完整登记表。
4
+ *
5
+ * 为什么派生期望而不是每腿各写一份 golden:三份手写 golden 之间的关系是**隐含**的,谁也说不清某一处
6
+ * 不同是「设计如此」还是「某一腿读错了」。派生把那层关系变成代码:凡不能由登记的变换解释的差异,一定
7
+ * 显示成 diff。于是「有没有第四条变换没登记」这件事不再靠人回忆 —— 没登记的那条会自己红出来。
8
+ *
9
+ * 变换表(每条写明:动了什么、影响哪几条腿、出处):
10
+ * · **T1 prompts 物化**(wire):`buildEffective` 恒物化 `prompts: {bindings:[]}`;wire 腿按**在场性**
11
+ * verbatim 挂回(`readEffectiveWire` 的 `"prompts" in r` 判据)—— 缺席进缺席出,且在场时是**未解析的
12
+ * 原文档**。
13
+ * · **T2 skills 表示**(wire):域形带 `content`,wire 形带 `contentHash`(manifest)。判据 schema 随表示走。
14
+ * · **T3 非便携域**(local-root):`config.d/` 只读 {@link PORTABLE_DOMAINS};其余域的文件在场则报
15
+ * `unread-config-file{why:"non-portable-domain"}`,域本身取 schema 默认。**由 `PORTABLE_DOMAINS` 派生,
16
+ * 不手抄域名** —— 设计稿只点了 `hosts` 一个实例,类是「非便携域」。
17
+ * · **T4 未知顶层键**(wire + local-root):wire 是 open-world,不认识的顶层键**逐字承运**;`domains` 腿
18
+ * 只按域名取文档,多出来的键根本不会被问到(无声);local-root 报
19
+ * `unread-config-file{why:"not-a-domain"}`。三腿三样 —— 这条是本车对着真读数补登记的第四条。
20
+ * · **T5 文件层先判**(local-root):`config.d/<域>.json` 的内容必须是 JSON **对象**(空文件 / `null` /
21
+ * 数组 / 标量一律 throw,fail-closed),这一层在域解析**之前**,`domains` 腿没有它。
22
+ * · **T6 文件名分类**(local-root):`config.d/` 里的文件分三类 —— 域文件(读)、
23
+ * {@link NON_DOMAIN_CONFIG_FILES}(`remote-exec.json`:**有自己的读取与校验路径**,既不报未知文件、
24
+ * 坏形时也由它自己那条腿拒)、隐藏文件 / 非 `.json`(扫描忽略,不报)。只有剩下的才是
25
+ * `unread-config-file{not-a-domain}`。这条是本车在对抗复审后补登记的第六条 —— 把 `remote-exec.json`
26
+ * 当普通未知文件报,门会对着一份完全合法的部署喊错。
27
+ *
28
+ * 语料可以对某一腿写**显式期望**(带 `why`)来覆盖派生 —— 那是「这条 case 的这条腿不由变换解释」的
29
+ * 明示声明,不是一条暗门。
30
+ */
31
+ import { NON_DOMAIN_CONFIG_FILES, PORTABLE_DOMAINS } from "../file-store.js";
32
+ import { skillContentHash } from "../skills-manifest.js";
33
+ import { DOMAINS, isDomainName } from "../types.js";
34
+ /** 变换表(本文件头注的机器形;`docs/CONFORMANCE.md` 与 `test/conformance-coverage.test.ts` 都读它)。 */
35
+ export const DERIVATION_TRANSFORMS = [
36
+ { id: "T1-prompts-materialisation", legs: ["wire"], what: "buildEffective 恒物化 prompts:{bindings:[]};wire 按在场性 verbatim 挂回(缺席进缺席出)" },
37
+ { id: "T2-skills-representation", legs: ["wire"], what: "域形 content ↔ wire 形 contentHash(manifest),判据 schema 随表示走" },
38
+ { id: "T3-non-portable-domains", legs: ["localRoot"], what: "config.d/ 只读 PORTABLE_DOMAINS;其余域取默认 + unread-config-file{non-portable-domain}" },
39
+ { id: "T4-unknown-top-level-key", legs: ["wire", "localRoot"], what: "wire open-world 逐字承运;domains 腿按域名取文档故无声;local 报 unread-config-file{not-a-domain}" },
40
+ { id: "T5-file-layer-first", legs: ["localRoot"], what: "config.d/<域>.json 必须是 JSON 对象(空/null/数组/标量 throw),这一层在域解析之前" },
41
+ { id: "T6-file-name-classification", legs: ["localRoot"], what: "config.d/ 的文件分三类:域文件 / NON_DOMAIN_CONFIG_FILES(remote-exec.json 自带读取与校验路径)/ 隐藏文件与非 .json(忽略);只有剩下的才报 not-a-domain" },
42
+ ];
43
+ /** 非便携域 —— 由 {@link PORTABLE_DOMAINS} 派生,零手抄。 */
44
+ export const NON_PORTABLE_DOMAINS = DOMAINS.filter((d) => !PORTABLE_DOMAINS.includes(d));
45
+ const isPlainObject = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
46
+ /** 一份 `domains` 文档集里,**不是域名**的那些顶层键(T4 的触发面)。 */
47
+ export const foreignKeysOf = (docs) => Object.keys(docs).filter((k) => !isDomainName(k));
48
+ /** T2 的**入参**半场:域形的 skills 文档 → wire 形的 manifest 文档。结构变换,不解析 —— 坏形原样传下去,
49
+ * 让 wire 腿自己的判据去裁(否则语料就没法喂一份坏 manifest 了)。 */
50
+ export function toManifestDoc(doc) {
51
+ if (!isPlainObject(doc) || !Array.isArray(doc.skills))
52
+ return doc;
53
+ return {
54
+ ...doc,
55
+ skills: doc.skills.map((entry) => {
56
+ if (!isPlainObject(entry) || typeof entry.content !== "string")
57
+ return entry;
58
+ const { content, ...rest } = entry;
59
+ return { ...rest, contentHash: skillContentHash(content) };
60
+ }),
61
+ };
62
+ }
63
+ /** `domains` 真源 → `wire` 腿入参(T1 缺席进缺席出 + T2 表示换 + T4 未知键照原样带上)。 */
64
+ export function deriveWireInput(docs, version, updatedAt) {
65
+ // fromEntries(不是逐键赋值):语料里的顶层键可以叫 `__proto__`(S-332 的那一类),逐键赋值会把它
66
+ // 变成一次原型改写,载荷里就再也没有这个键 —— 整条 case 于是测了个寂寞。
67
+ const payload = Object.fromEntries([
68
+ ["version", version],
69
+ ["updatedAt", updatedAt],
70
+ ...Object.entries(docs).map(([k, v]) => [k, k === "skills" ? toManifestDoc(v) : v]),
71
+ ]);
72
+ return { leg: "wire", payload };
73
+ }
74
+ /** `domains` 真源 → `local-root` 腿入参(每个顶层键一个 `config.d/<键>.json`,含非域名的那些 —— T3/T4
75
+ * 的告警正是靠它们在目录里在场才会响)。 */
76
+ export function deriveLocalInput(docs, mtime) {
77
+ const files = {};
78
+ for (const [k, v] of Object.entries(docs))
79
+ files[`${k}.json`] = JSON.stringify(v, null, 2) + "\n";
80
+ return { leg: "localRoot", files, mtime };
81
+ }
82
+ const clone = (v) => structuredClone(v);
83
+ /** T1 + T2 + T4:`domains` 期望 → `wire` 期望。 */
84
+ export function deriveWireExpectation(source, docs) {
85
+ const base = clone(source.effective);
86
+ // T1:缺席进缺席出;在场则是**未解析的原文档**(wire 腿从不 parse prompts)。
87
+ // T2:域形条目的 content → contentHash(manifest 的条目 schema = 域条目 omit content + contentHash)。
88
+ // T4:open-world 顶层键逐字承运 —— 用 fromEntries 而不是逐键赋值,否则一个叫 `__proto__` 的顶层键
89
+ // 会变成一次原型改写(期望侧与实际侧被同样吃掉 = 比对器对这一类全盲,S-332 同族)。
90
+ const effective = Object.fromEntries([
91
+ ...Object.entries(base).filter(([k]) => k !== "prompts" && k !== "skills"),
92
+ ["skills", toManifestDoc(base.skills)],
93
+ ...(Object.prototype.hasOwnProperty.call(docs, "prompts") ? [["prompts", clone(docs.prompts)]] : []),
94
+ ...foreignKeysOf(docs).map((k) => [k, clone(docs[k])]),
95
+ ]);
96
+ const warnings = source.warnings.filter((w) => {
97
+ if ("domain" in w && w.domain === "prompts")
98
+ return false; // T1:wire 腿不解析 prompts ⇒ 没有它的告警
99
+ if ("domain" in w && w.domain === "skills" && (w.kind === "unknown-keys-dropped" || w.kind === "unknown-keys-carried"))
100
+ return false; // T2:manifest 不走剥键扫描
101
+ return true;
102
+ });
103
+ return { outcome: "effective", effective, warnings, metadata: { ...source.metadata } };
104
+ }
105
+ /** 纪元时刻 —— 一个 `config.d/` 里没有任何**便携**域文件时 `updatedAt` 的取值(`#maxMtimeIso` 只 stat 便携域)。 */
106
+ export const EPOCH_ISO = new Date(0).toISOString();
107
+ /** T3 + T4 + T5:`domains` 期望 → `local-root` 期望。`baseline` = 空文档集的 effective(语料里冻结的
108
+ * `conformance/baseline.json`),非便携域取它的那一格 —— 不现算,免得 schema 默认一改 golden 就跟着动。 */
109
+ export function deriveLocalExpectation(source, docs, baseline, localVersion) {
110
+ // T5:文件层先判 —— 任何一个**便携**域的文档不是 JSON 对象 ⇒ 整条腿在解析层被拒。
111
+ const badFile = PORTABLE_DOMAINS.find((d) => Object.prototype.hasOwnProperty.call(docs, d) && !isPlainObject(docs[d]));
112
+ if (badFile !== undefined)
113
+ return { outcome: "rejected", stage: "parse" };
114
+ const effective = clone(source.effective);
115
+ for (const d of NON_PORTABLE_DOMAINS)
116
+ effective[d] = clone(baseline[d]);
117
+ const warnings = source.warnings.filter((w) => !("domain" in w && w.domain !== undefined && NON_PORTABLE_DOMAINS.includes(w.domain)));
118
+ for (const d of NON_PORTABLE_DOMAINS) {
119
+ if (Object.prototype.hasOwnProperty.call(docs, d))
120
+ warnings.push({ kind: "unread-config-file", file: `${d}.json`, domain: d, why: "non-portable-domain" });
121
+ }
122
+ // T6:只有**既不是专用文件、也不是隐藏文件**的那些才报 not-a-domain(分类表从 file-store 派生,零手抄)。
123
+ for (const k of foreignKeysOf(docs)) {
124
+ const file = `${k}.json`;
125
+ if (NON_DOMAIN_CONFIG_FILES.has(file) || file.startsWith("."))
126
+ continue;
127
+ warnings.push({ kind: "unread-config-file", file, why: "not-a-domain" });
128
+ }
129
+ const anyPortable = PORTABLE_DOMAINS.some((d) => Object.prototype.hasOwnProperty.call(docs, d));
130
+ return {
131
+ outcome: "effective",
132
+ effective,
133
+ warnings,
134
+ metadata: { updatedAt: anyPortable ? source.metadata.updatedAt : EPOCH_ISO, ...(localVersion === undefined ? {} : { version: localVersion }) },
135
+ };
136
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * `@sema-agent/settings-schema/conformance` —— 跨仓一致性门的 kit。
3
+ *
4
+ * 一句话:契约仓自带**金样语料 + 比对器**;每个消费方把**自己的生产读腿**塞给 kit 跑一遍 ⇒ 同一份
5
+ * settings 语料在 center / worker / 管理面 / 契约仓自身读出的 effective 逐字节一致、告警多重集一致。
6
+ *
7
+ * 三步接线(消费方侧):
8
+ * 1. 造一个 {@link ConformanceAdapter}:`provenance` = 你**实际加载到**的那份契约包(package.json 路径、
9
+ * 版号、完整性串);`legs` = 你自己的生产读腿,每条带 `entry{module,export}`(生产入口的绝对路径)。
10
+ * 2. `const report = await runConformance(adapter, { mode: "release" })`。
11
+ * 3. 断言 `report.ok`;不绿就读 `report.violations` 与每格的 `diff`(effective 的 JSON patch、告警多重集差、
12
+ * 元数据差三面分开)。
13
+ *
14
+ * 语料贡献形、四律、报告形见 `docs/CONFORMANCE.md`。Node-only(语料在磁盘上,local-root 腿要落盘)。
15
+ */
16
+ export { runConformance, materializeLocalRoot, resolveLegExpectation } from "./run.js";
17
+ export { createContractAdapter, type ContractAdapterOptions, type ContractModule } from "./contract-adapter.js";
18
+ export { loadCorpus, loadCase, kitIdentity, DEFAULT_CORPUS_DIR, PACKAGE_ROOT, type Corpus, type ConformanceCase, type CaseInput, type CaseMeta, type LegExpectationJson } from "./corpus.js";
19
+ export { DERIVATION_TRANSFORMS, NON_PORTABLE_DOMAINS, EPOCH_ISO, deriveWireInput, deriveLocalInput, deriveWireExpectation, deriveLocalExpectation, foreignKeysOf, toManifestDoc, type DerivationTransform, type LegExpectation, } from "./derive.js";
20
+ export { canonicalize, deepEqual, diffJson, stableKey, type JsonPatchOp } from "./canonical.js";
21
+ export { WARNING_KINDS, projectWarning, normalizeProjection, projectionKey, projectionValue, diffWarningMultiset, findSecretLeaks, type WarningKind, type WarningProjection, } from "./warnings.js";
22
+ export { LEG_NAMES, REJECT_STAGES, type ConformanceAdapter, type ConformanceLeg, type ConformanceMode, type ConformanceNormalizer, type ConformanceOptions, type ConformanceProvenance, type ConformanceReport, type ConformanceResult, type ConformanceStatus, type KnownDivergence, type OutcomeDivergence, type PathDivergence, type LedgerRow, type LegEntry, type LegInput, type LegName, type LegOutcome, type RejectStage, } from "./types.js";
@@ -0,0 +1,22 @@
1
+ /**
2
+ * `@sema-agent/settings-schema/conformance` —— 跨仓一致性门的 kit。
3
+ *
4
+ * 一句话:契约仓自带**金样语料 + 比对器**;每个消费方把**自己的生产读腿**塞给 kit 跑一遍 ⇒ 同一份
5
+ * settings 语料在 center / worker / 管理面 / 契约仓自身读出的 effective 逐字节一致、告警多重集一致。
6
+ *
7
+ * 三步接线(消费方侧):
8
+ * 1. 造一个 {@link ConformanceAdapter}:`provenance` = 你**实际加载到**的那份契约包(package.json 路径、
9
+ * 版号、完整性串);`legs` = 你自己的生产读腿,每条带 `entry{module,export}`(生产入口的绝对路径)。
10
+ * 2. `const report = await runConformance(adapter, { mode: "release" })`。
11
+ * 3. 断言 `report.ok`;不绿就读 `report.violations` 与每格的 `diff`(effective 的 JSON patch、告警多重集差、
12
+ * 元数据差三面分开)。
13
+ *
14
+ * 语料贡献形、四律、报告形见 `docs/CONFORMANCE.md`。Node-only(语料在磁盘上,local-root 腿要落盘)。
15
+ */
16
+ export { runConformance, materializeLocalRoot, resolveLegExpectation } from "./run.js";
17
+ export { createContractAdapter } from "./contract-adapter.js";
18
+ export { loadCorpus, loadCase, kitIdentity, DEFAULT_CORPUS_DIR, PACKAGE_ROOT } from "./corpus.js";
19
+ export { DERIVATION_TRANSFORMS, NON_PORTABLE_DOMAINS, EPOCH_ISO, deriveWireInput, deriveLocalInput, deriveWireExpectation, deriveLocalExpectation, foreignKeysOf, toManifestDoc, } from "./derive.js";
20
+ export { canonicalize, deepEqual, diffJson, stableKey } from "./canonical.js";
21
+ export { WARNING_KINDS, projectWarning, normalizeProjection, projectionKey, projectionValue, diffWarningMultiset, findSecretLeaks, } from "./warnings.js";
22
+ export { LEG_NAMES, REJECT_STAGES, } from "./types.js";
@@ -0,0 +1,27 @@
1
+ import { type LegExpectation } from "./derive.js";
2
+ import { type ConformanceCase } from "./corpus.js";
3
+ import { type ConformanceAdapter, type ConformanceOptions, type ConformanceReport, type LegName } from "./types.js";
4
+ /**
5
+ * 把一份 `config.d/` 快照落成真目录 —— **kit 单铸**,消费方零复述(每个消费方各写一遍落盘逻辑,
6
+ * mtime 怎么设、目录怎么建就会各不相同,而 `updatedAt` 正是按 mtime 算的)。
7
+ *
8
+ * 返回 `root` 与 `cleanup`;所有文件的 mtime 统一设成 `mtime`,于是 `updatedAt` 对同一份快照恒定。
9
+ */
10
+ export declare function materializeLocalRoot(files: Readonly<Record<string, string>>, mtime: string): Promise<{
11
+ root: string;
12
+ cleanup: () => Promise<void>;
13
+ }>;
14
+ /** 磁盘形的腿期望 → 运行期形(`derive:true` 在这里真的派生)。`null` = 语料声明本腿不适用。
15
+ *
16
+ * **导出**给覆盖律用:覆盖矩阵要数的是「这条腿最终被期望成什么」,那必须是**驱动真正用的那一份**期望
17
+ * ——测试自己再算一遍派生,就是第二份真源,而两份真源的分歧正是本门要抓的病。 */
18
+ export declare function resolveLegExpectation(c: ConformanceCase, leg: LegName, baseline: Readonly<Record<string, unknown>>): LegExpectation | null;
19
+ /**
20
+ * 跑一遍门。
21
+ *
22
+ * `release`:版本三方对齐(适配器加载到的包 == 语料 VERSION == kit 所在包)+ 每条登记的腿都指向一个
23
+ * **真存在、且不在契约包 node_modules 之下**的生产入口 + 禁 `only` + 至少登记一条腿 + **登记的腿必须跑**
24
+ * (未登记的腿只记 `skipped: no adapter`,不算违例)+ 账本条目的形 + 零陈腐账本条目。
25
+ * `historical`:对旧版包跑,只记行为 diff;归一器可在场且必须自陈有损。
26
+ */
27
+ export declare function runConformance(adapter: ConformanceAdapter, opts: ConformanceOptions): Promise<ConformanceReport>;