noteconnection 1.7.0 → 1.8.0

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 (211) hide show
  1. package/LICENSE +674 -21
  2. package/README.md +242 -62
  3. package/dist/src/agent_workspace.contract.parity.test.js +475 -0
  4. package/dist/src/agent_workspace.frontend.test.js +9989 -0
  5. package/dist/src/agent_workspace.locale.contract.test.js +95 -0
  6. package/dist/src/agent_workspace.runtime.behavior.test.js +5072 -0
  7. package/dist/src/copy.assets.contract.test.js +130 -0
  8. package/dist/src/core/PathBridge.js +28 -3
  9. package/dist/src/export/WorkspaceExportBundle.js +1130 -0
  10. package/dist/src/export/WorkspaceExportBundle.test.js +1480 -0
  11. package/dist/src/export/index.js +18 -0
  12. package/dist/src/export/types.js +2 -0
  13. package/dist/src/fixrisk.issue.verifier.contract.test.js +3 -1
  14. package/dist/src/foundation.ann.runtime.contract.test.js +81 -0
  15. package/dist/src/foundation.release.evidence.contract.test.js +406 -0
  16. package/dist/src/foundation.sqlite.runtime.contract.test.js +82 -0
  17. package/dist/src/frontend/README.md +242 -62
  18. package/dist/src/frontend/agent_workspace.js +5353 -0
  19. package/dist/src/frontend/agent_workspace_runtime.js +4434 -0
  20. package/dist/src/frontend/analysis.js +31 -4
  21. package/dist/src/frontend/app.js +2178 -62
  22. package/dist/src/frontend/focus_mode_interactions.js +33 -0
  23. package/dist/src/frontend/godot_future_path_renderer.js +540 -0
  24. package/dist/src/frontend/godot_tree_interactions.js +178 -0
  25. package/dist/src/frontend/graph_state.mjs +105 -0
  26. package/dist/src/frontend/hosted_future_path_runtime.js +157 -0
  27. package/dist/src/frontend/i18n.mjs +186 -0
  28. package/dist/src/frontend/index.html +641 -266
  29. package/dist/src/frontend/layout_gpu.js +12 -4
  30. package/dist/src/frontend/locales/en.json +721 -5
  31. package/dist/src/frontend/locales/zh.json +721 -5
  32. package/dist/src/frontend/main.mjs +60 -0
  33. package/dist/src/frontend/markdown_runtime.js +827 -0
  34. package/dist/src/frontend/notemd.css +49 -0
  35. package/dist/src/frontend/notemd.html +7 -1
  36. package/dist/src/frontend/notemd.js +64 -0
  37. package/dist/src/frontend/path.html +107 -0
  38. package/dist/src/frontend/path_app.js +2108 -130
  39. package/dist/src/frontend/path_layout.mjs +143 -0
  40. package/dist/src/frontend/path_mermaid_utils.mjs +108 -0
  41. package/dist/src/frontend/path_modules_bridge.js +486 -0
  42. package/dist/src/frontend/path_state.mjs +118 -0
  43. package/dist/src/frontend/path_styles.css +146 -0
  44. package/dist/src/frontend/path_worker_bridge.mjs +85 -0
  45. package/dist/src/frontend/reader.js +393 -33
  46. package/dist/src/frontend/runtime_bridge.js +67 -54
  47. package/dist/src/frontend/runtime_bridge.mjs +279 -0
  48. package/dist/src/frontend/settings.js +130 -12
  49. package/dist/src/frontend/source_manager.js +190 -21
  50. package/dist/src/frontend/styles.css +2720 -72
  51. package/dist/src/frontend/workbench_state.mjs +101 -0
  52. package/dist/src/frontend/workspace_panes.js +10168 -0
  53. package/dist/src/frontend.locale.contract.test.js +62 -0
  54. package/dist/src/godot.sidecar.bootstrap.contract.test.js +244 -0
  55. package/dist/src/indexing/IndexLifecycle.js +195 -0
  56. package/dist/src/indexing/IndexLifecycle.test.js +49 -0
  57. package/dist/src/indexing/SegmentBuilder.js +64 -0
  58. package/dist/src/indexing/UnitBuilder.js +48 -0
  59. package/dist/src/indexing/types.js +2 -0
  60. package/dist/src/knowledge.api.contract.test.js +170 -0
  61. package/dist/src/learning/KnowledgeLearningPlatform.js +10386 -0
  62. package/dist/src/learning/KnowledgeLearningPlatform.persistence.test.js +327 -0
  63. package/dist/src/learning/KnowledgeLearningPlatform.program-f.test.js +99 -0
  64. package/dist/src/learning/KnowledgeLearningPlatform.test.js +2971 -0
  65. package/dist/src/learning/KnowledgeWorkspaceConversationRegression.js +2974 -0
  66. package/dist/src/learning/KnowledgeWorkspaceConversationRegression.test.js +3928 -0
  67. package/dist/src/learning/answerReleaseReview.js +4319 -0
  68. package/dist/src/learning/answerReleaseReview.test.js +2888 -0
  69. package/dist/src/learning/api.js +2 -0
  70. package/dist/src/learning/conversationComposer.js +1480 -0
  71. package/dist/src/learning/conversationComposer.test.js +1817 -0
  72. package/dist/src/learning/domains/ConversationManager.js +53 -0
  73. package/dist/src/learning/domains/KnowledgeIngestor.js +238 -0
  74. package/dist/src/learning/domains/KnowledgeQuerier.js +187 -0
  75. package/dist/src/learning/domains/MasteryEngine.js +387 -0
  76. package/dist/src/learning/domains/MemoryPolicyManager.js +408 -0
  77. package/dist/src/learning/domains/QualityEvaluator.js +307 -0
  78. package/dist/src/learning/domains/TutorRouter.js +313 -0
  79. package/dist/src/learning/domains/index.js +33 -0
  80. package/dist/src/learning/domains/types.js +7 -0
  81. package/dist/src/learning/errors.js +29 -0
  82. package/dist/src/learning/evidenceContextAssembler.js +1176 -0
  83. package/dist/src/learning/evidenceContextAssembler.test.js +6332 -0
  84. package/dist/src/learning/graphContextAssembler.js +870 -0
  85. package/dist/src/learning/graphContextAssembler.test.js +1033 -0
  86. package/dist/src/learning/index.js +28 -0
  87. package/dist/src/learning/queryBackend.js +1898 -0
  88. package/dist/src/learning/queryBackend.test.js +955 -0
  89. package/dist/src/learning/ragContextPack.js +257 -0
  90. package/dist/src/learning/ragContextPack.test.js +160 -0
  91. package/dist/src/learning/ragPublicText.js +38 -0
  92. package/dist/src/learning/ragSufficiencyJudge.js +161 -0
  93. package/dist/src/learning/ragSufficiencyJudge.test.js +177 -0
  94. package/dist/src/learning/ragSufficiencyProviderJudge.js +227 -0
  95. package/dist/src/learning/ragSufficiencyProviderJudge.test.js +156 -0
  96. package/dist/src/learning/requestNormalization.js +198 -0
  97. package/dist/src/learning/runtimeCapability.js +4677 -0
  98. package/dist/src/learning/runtimeCapability.test.js +3635 -0
  99. package/dist/src/learning/store.js +1240 -0
  100. package/dist/src/learning/store.test.js +1126 -0
  101. package/dist/src/learning/tutorAdapter.js +2 -0
  102. package/dist/src/learning/types.js +2 -0
  103. package/dist/src/learning/vectorAccelerationAdapter.js +942 -0
  104. package/dist/src/learning/vectorAccelerationAdapter.test.js +382 -0
  105. package/dist/src/lfs.asset.policy.contract.test.js +153 -0
  106. package/dist/src/license.policy.contract.test.js +66 -0
  107. package/dist/src/memory/MemoryGovernance.js +74 -0
  108. package/dist/src/memory/MemoryGovernance.test.js +46 -0
  109. package/dist/src/memory/types.js +2 -0
  110. package/dist/src/mermaid.frontend.guard.contract.test.js +77 -0
  111. package/dist/src/middleware/auth.js +17 -0
  112. package/dist/src/middleware/body-parser.js +45 -0
  113. package/dist/src/middleware/cors.js +44 -0
  114. package/dist/src/middleware/index.js +21 -0
  115. package/dist/src/middleware/request-trace.js +96 -0
  116. package/dist/src/notemd/AppConfigToml.js +6 -4
  117. package/dist/src/notemd/MermaidProcessor.js +400 -50
  118. package/dist/src/notemd/NotemdService.js +498 -13
  119. package/dist/src/notemd/PromptManager.js +15 -0
  120. package/dist/src/notemd/cli/commands.js +357 -0
  121. package/dist/src/notemd/cli/dispatcher.js +225 -0
  122. package/dist/src/notemd/cli/index.js +169 -0
  123. package/dist/src/notemd/cli/parser.js +68 -0
  124. package/dist/src/notemd/cli/types.js +2 -0
  125. package/dist/src/notemd/constants.js +43 -0
  126. package/dist/src/notemd/diagram/diagramGenerationService.js +78 -0
  127. package/dist/src/notemd/diagram/diagramSpec.js +79 -0
  128. package/dist/src/notemd/diagram/diagramSpecResponseParser.js +131 -0
  129. package/dist/src/notemd/diagram/intent.js +95 -0
  130. package/dist/src/notemd/diagram/planner.js +71 -0
  131. package/dist/src/notemd/diagram/prompts/diagramSpecPrompt.js +42 -0
  132. package/dist/src/notemd/diagram/types.js +18 -0
  133. package/dist/src/notemd/index.js +26 -0
  134. package/dist/src/notemd/operations/capabilityManifest.js +23 -0
  135. package/dist/src/notemd/operations/cliContracts.js +17 -0
  136. package/dist/src/notemd/operations/configProfileCommands.js +85 -0
  137. package/dist/src/notemd/operations/registry.contract.test.js +95 -0
  138. package/dist/src/notemd/operations/registry.js +991 -0
  139. package/dist/src/notemd/operations/types.js +2 -0
  140. package/dist/src/notemd/providerDiagnostics.js +220 -0
  141. package/dist/src/notemd/providerProfiles.js +42 -0
  142. package/dist/src/notemd/providerTemplates.js +231 -0
  143. package/dist/src/notemd/search/DuckDuckGoProvider.js +39 -0
  144. package/dist/src/notemd/search/SearchManager.js +13 -0
  145. package/dist/src/notemd/search/SearchProvider.js +2 -0
  146. package/dist/src/notemd/search/TavilyProvider.js +44 -0
  147. package/dist/src/notemd.agent.manifest.test.js +85 -0
  148. package/dist/src/notemd.api.contract.test.js +14 -1
  149. package/dist/src/notemd.app_config_toml.test.js +2 -1
  150. package/dist/src/notemd.batch.workflow.test.js +117 -0
  151. package/dist/src/notemd.cli.e2e.test.js +136 -0
  152. package/dist/src/notemd.core.test.js +51 -0
  153. package/dist/src/notemd.diagram.pipeline.test.js +233 -0
  154. package/dist/src/notemd.providerTemplates.test.js +34 -0
  155. package/dist/src/notemd.server.integration.test.js +143 -35
  156. package/dist/src/notemd.workflow.pipeline.test.js +162 -0
  157. package/dist/src/pathbridge.handshake.contract.test.js +16 -2
  158. package/dist/src/pathmode.background.contract.test.js +69 -0
  159. package/dist/src/pathmode.settings.api.contract.test.js +9 -0
  160. package/dist/src/pkg.sidecar.contract.test.js +9 -3
  161. package/dist/src/platform/ExportProfile.js +58 -0
  162. package/dist/src/platform/PlatformCapabilities.js +45 -0
  163. package/dist/src/platform/PlatformCapabilities.test.js +30 -0
  164. package/dist/src/platform/RenderMaterializer.js +33 -0
  165. package/dist/src/platform/RenderMaterializer.test.js +32 -0
  166. package/dist/src/query_backend.external_http.integration.test.js +410 -0
  167. package/dist/src/reader_renderer.js +404 -3
  168. package/dist/src/reader_renderer.test.js +87 -0
  169. package/dist/src/release.godot.mirror.contract.test.js +73 -0
  170. package/dist/src/resources/ResourceRegistry.js +223 -0
  171. package/dist/src/resources/ResourceRegistry.test.js +61 -0
  172. package/dist/src/resources/types.js +2 -0
  173. package/dist/src/routes/agentWorkspaceDiagnostics.js +173 -0
  174. package/dist/src/routes/data.js +267 -0
  175. package/dist/src/routes/diagnostics.js +51 -0
  176. package/dist/src/routes/index.js +23 -0
  177. package/dist/src/routes/knowledge.js +968 -0
  178. package/dist/src/routes/markdown.js +287 -0
  179. package/dist/src/routes/notemd.js +565 -0
  180. package/dist/src/routes/registry.contract.test.js +130 -0
  181. package/dist/src/routes/render.js +285 -0
  182. package/dist/src/routes/runtimeRunbookRouteOps.js +149 -0
  183. package/dist/src/routes/runtimeRunbookRouteOps.test.js +194 -0
  184. package/dist/src/routes/settings.js +6 -0
  185. package/dist/src/routes/staticFiles.js +94 -0
  186. package/dist/src/routes/types.js +2 -0
  187. package/dist/src/runtime.transport.adapter.contract.test.js +81 -0
  188. package/dist/src/server.js +11090 -1549
  189. package/dist/src/server.migration.test.js +193 -21
  190. package/dist/src/server.port.fallback.contract.test.js +63 -0
  191. package/dist/src/session/SessionStateStore.js +81 -0
  192. package/dist/src/session/SessionStateStore.test.js +58 -0
  193. package/dist/src/session/types.js +2 -0
  194. package/dist/src/settings.runtime.contract.test.js +50 -0
  195. package/dist/src/shared/types.contract.test.js +107 -0
  196. package/dist/src/shared/types.js +22 -0
  197. package/dist/src/sidecar.replacement.boundary.contract.test.js +128 -0
  198. package/dist/src/sidecar.supply.readiness.contract.test.js +144 -0
  199. package/dist/src/source_manager.loadflow.test.js +46 -0
  200. package/dist/src/startup.layout.snapshot.contract.test.js +57 -0
  201. package/dist/src/tauri.frontend.build.contract.test.js +60 -0
  202. package/dist/src/tauri.sidecar.cleanup.contract.test.js +21 -0
  203. package/dist/src/utils/RuntimePaths.js +4 -13
  204. package/dist/src/utils/platform.js +153 -0
  205. package/dist/src/workflows/WorkflowArtifactStore.js +96 -0
  206. package/dist/src/workflows/WorkflowArtifactStore.test.js +80 -0
  207. package/dist/src/workflows/types.js +2 -0
  208. package/dist/src/workspace/WorkspaceRegistry.js +122 -0
  209. package/dist/src/workspace/WorkspaceRegistry.test.js +29 -0
  210. package/dist/src/workspace/types.js +2 -0
  211. package/package.json +53 -10
@@ -0,0 +1,2888 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const answerReleaseReview_1 = require("./answerReleaseReview");
4
+ const scopedWaterglass = {
5
+ source: 'scoped',
6
+ workspaceId: 'waterglass',
7
+ corpusId: 'waterglass',
8
+ documentIds: ['doc_water_glass'],
9
+ atomIds: [],
10
+ sourcePathPrefixes: ['Knowledge_Base/waterglass'],
11
+ languages: [],
12
+ matchedAtomCount: 0,
13
+ readiness: {
14
+ status: 'ready',
15
+ message: 'The scoped learning workspace is ready.',
16
+ workspaceId: 'waterglass',
17
+ corpusId: 'waterglass',
18
+ activeResourceCount: 1,
19
+ activeProjectionCount: 1,
20
+ indexedUnitCount: 1,
21
+ indexedSegmentCount: 4,
22
+ matchedDocumentCount: 1,
23
+ },
24
+ missDiagnostics: {
25
+ reason: 'retrieval_candidates_below_threshold',
26
+ message: 'The planner found likely documents, but retrieval did not return evidence-bearing candidates.',
27
+ query: '什么是waterglass?',
28
+ normalizedQuery: '什么是waterglass?',
29
+ plannerQuery: '什么是water glass',
30
+ titleLikeQueries: ['waterglass', 'water glass'],
31
+ titleHitDocumentIds: ['doc_water_glass'],
32
+ indexedScopeAtomCount: 4,
33
+ },
34
+ };
35
+ function makeKnowledgePoint(overrides = {}) {
36
+ const citation = {
37
+ citationId: 'citation_water_glass',
38
+ atomId: 'atom_water_glass',
39
+ documentId: 'doc_water_glass',
40
+ sourcePath: 'Knowledge_Base/waterglass/water-glass.md',
41
+ title: 'Water Glass',
42
+ snippet: 'Water glass is a transparent container filled with water.',
43
+ startLine: 3,
44
+ endLine: 3,
45
+ score: 0.92,
46
+ };
47
+ return {
48
+ atomId: 'atom_water_glass',
49
+ atomIds: ['atom_water_glass'],
50
+ documentId: 'doc_water_glass',
51
+ sourcePath: 'Knowledge_Base/waterglass/water-glass.md',
52
+ title: 'Water Glass',
53
+ summary: 'Water glass is a transparent container filled with water.',
54
+ evidenceSnippet: 'Water glass is a transparent container filled with water.',
55
+ score: 0.92,
56
+ citation,
57
+ citations: [citation],
58
+ matchedSpans: [],
59
+ matchCount: 1,
60
+ relationPath: [],
61
+ relationPathAtomIds: [],
62
+ relationKinds: [],
63
+ temporalValidity: {
64
+ isValid: true,
65
+ checkedAt: '2026-06-18T00:00:00.000Z',
66
+ reasons: [],
67
+ details: [],
68
+ },
69
+ capabilities: [],
70
+ ...overrides,
71
+ };
72
+ }
73
+ function makeGraphContext(overrides = {}) {
74
+ return {
75
+ anchorAtomId: 'atom_water_glass',
76
+ anchorTitle: 'Water Glass',
77
+ supportingAtomIds: [],
78
+ supportingTitles: [],
79
+ relationKinds: [],
80
+ relationSummaries: [],
81
+ temporalValidity: {
82
+ checkedAt: '2026-06-18T00:00:00.000Z',
83
+ allPointsValid: true,
84
+ warningReasons: [],
85
+ invalidKnowledgePointTitles: [],
86
+ edgeKinds: [],
87
+ details: [],
88
+ },
89
+ ...overrides,
90
+ };
91
+ }
92
+ function makeOrderedGraphContext(overrides = {}) {
93
+ return {
94
+ anchorAtomId: 'atom_ground_state',
95
+ anchorTitle: 'Ground State',
96
+ supportingAtomIds: ['atom_bridge_layer'],
97
+ supportingTitles: ['Bridge Layer'],
98
+ relationKinds: ['prerequisite'],
99
+ relationSummaries: [
100
+ {
101
+ relationKind: 'prerequisite',
102
+ edgeIds: ['edge_bridge_to_ground'],
103
+ sourceAtomIds: ['atom_bridge_layer'],
104
+ targetAtomIds: ['atom_ground_state'],
105
+ averageConfidence: 0.93,
106
+ },
107
+ ],
108
+ connectionPaths: [
109
+ {
110
+ sourceAtomId: 'atom_bridge_layer',
111
+ sourceTitle: 'Bridge Layer',
112
+ targetAtomId: 'atom_ground_state',
113
+ targetTitle: 'Ground State',
114
+ pathAtomIds: ['atom_bridge_layer', 'atom_ground_state'],
115
+ pathTitles: ['Bridge Layer', 'Ground State'],
116
+ pathEdges: [
117
+ {
118
+ fromAtomId: 'atom_bridge_layer',
119
+ toAtomId: 'atom_ground_state',
120
+ relationKind: 'prerequisite',
121
+ },
122
+ ],
123
+ length: 1,
124
+ },
125
+ ],
126
+ predecessorWindow: [
127
+ {
128
+ atomId: 'atom_bridge_layer',
129
+ title: 'Bridge Layer',
130
+ relationKind: 'prerequisite',
131
+ confidence: 0.93,
132
+ },
133
+ ],
134
+ successorWindow: [],
135
+ temporalValidity: {
136
+ checkedAt: '2026-06-19T02:00:00.000Z',
137
+ allPointsValid: true,
138
+ warningReasons: [],
139
+ invalidKnowledgePointTitles: [],
140
+ edgeKinds: [],
141
+ details: [],
142
+ },
143
+ ...overrides,
144
+ };
145
+ }
146
+ function makeComparisonGraphContext(relationKind, leftTitle, rightTitle, overrides = {}) {
147
+ return {
148
+ anchorAtomId: 'atom_left',
149
+ anchorTitle: leftTitle,
150
+ supportingAtomIds: ['atom_right'],
151
+ supportingTitles: [rightTitle],
152
+ relationKinds: [relationKind],
153
+ relationSummaries: [
154
+ {
155
+ relationKind,
156
+ edgeIds: ['edge_graph_comparison'],
157
+ sourceAtomIds: ['atom_left'],
158
+ targetAtomIds: ['atom_right'],
159
+ averageConfidence: 0.91,
160
+ },
161
+ ],
162
+ knowledgePointRelations: [
163
+ {
164
+ edgeId: 'edge_graph_comparison',
165
+ relationKind,
166
+ sourceAtomId: 'atom_left',
167
+ sourceTitle: leftTitle,
168
+ targetAtomId: 'atom_right',
169
+ targetTitle: rightTitle,
170
+ confidence: 0.91,
171
+ },
172
+ ],
173
+ connectionPaths: [
174
+ {
175
+ sourceAtomId: 'atom_left',
176
+ sourceTitle: leftTitle,
177
+ targetAtomId: 'atom_right',
178
+ targetTitle: rightTitle,
179
+ pathAtomIds: ['atom_left', 'atom_right'],
180
+ pathTitles: [leftTitle, rightTitle],
181
+ pathEdges: [
182
+ {
183
+ fromAtomId: 'atom_left',
184
+ toAtomId: 'atom_right',
185
+ relationKind,
186
+ },
187
+ ],
188
+ length: 1,
189
+ },
190
+ ],
191
+ predecessorWindow: [],
192
+ successorWindow: [],
193
+ temporalValidity: {
194
+ checkedAt: '2026-06-19T03:40:00.000Z',
195
+ allPointsValid: true,
196
+ warningReasons: [],
197
+ invalidKnowledgePointTitles: [],
198
+ edgeKinds: [],
199
+ details: [],
200
+ },
201
+ ...overrides,
202
+ };
203
+ }
204
+ describe('answerReleaseReview', () => {
205
+ test('downgrades unsupported debug-style answers into concise abstentions', () => {
206
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
207
+ message: '什么是waterglass?',
208
+ draftAnswer: 'No scoped knowledge points matched "什么是waterglass?". The planner found likely documents, but retrieval did not return evidence-bearing candidates.',
209
+ knowledgePoints: [],
210
+ citations: [],
211
+ usedScope: scopedWaterglass,
212
+ graphContext: null,
213
+ reviewedAt: '2026-06-18T09:00:00.000Z',
214
+ });
215
+ expect(review.decision).toBe('abstain');
216
+ expect(review.publicAnswer).toContain('waterglass');
217
+ expect(review.publicAnswer).toContain('当前范围');
218
+ expect(review.publicAnswer).not.toContain('No scoped knowledge points matched');
219
+ expect(review.publicAnswer).not.toContain('retrieval');
220
+ expect(review.failedGateIds).toEqual(expect.arrayContaining([
221
+ 'evidence_sufficiency',
222
+ 'internal_diagnostic_leakage',
223
+ ]));
224
+ expect(review.gates).toEqual(expect.arrayContaining([
225
+ expect.objectContaining({
226
+ gateId: 'abstention_hygiene',
227
+ passed: true,
228
+ }),
229
+ ]));
230
+ });
231
+ test('revises grounded answers when draft text leaks support framing', () => {
232
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
233
+ message: 'what is water glass',
234
+ draftAnswer: 'Grounded by 1 citation. Water glass is a transparent container filled with water.',
235
+ knowledgePoints: [makeKnowledgePoint()],
236
+ citations: [makeKnowledgePoint().citation],
237
+ usedScope: scopedWaterglass,
238
+ graphContext: makeGraphContext(),
239
+ reviewedAt: '2026-06-18T09:05:00.000Z',
240
+ });
241
+ expect(review.decision).toBe('revise');
242
+ expect(review.publicAnswer).toBe('Water glass is a transparent container filled with water.');
243
+ expect(review.failedGateIds).toContain('public_surface_contraction');
244
+ expect(review.leakedInternalFragments).toHaveLength(0);
245
+ });
246
+ test('revises grounded answers when draft claims drift away from cited support', () => {
247
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
248
+ message: 'what is water glass',
249
+ draftAnswer: 'Water glass is a copper energy-storage device used for industrial voltage buffering.',
250
+ knowledgePoints: [makeKnowledgePoint()],
251
+ citations: [makeKnowledgePoint().citation],
252
+ usedScope: scopedWaterglass,
253
+ graphContext: makeGraphContext(),
254
+ reviewedAt: '2026-06-18T09:10:00.000Z',
255
+ });
256
+ expect(review.decision).toBe('revise');
257
+ expect(review.failedGateIds).toContain('claim_grounding_alignment');
258
+ expect(review.publicAnswer).toBe('Water glass is a transparent container filled with water.');
259
+ expect(review.gates).toEqual(expect.arrayContaining([
260
+ expect.objectContaining({
261
+ gateId: 'claim_grounding_alignment',
262
+ passed: false,
263
+ }),
264
+ ]));
265
+ });
266
+ test('revises definition-query answers when the draft describes the document instead of the concept', () => {
267
+ const point = makeKnowledgePoint({
268
+ title: 'Water Glass',
269
+ summary: 'This technical document analyzes water glass as a physical system. Water glass is a transparent container filled with water.',
270
+ evidenceSnippet: 'This technical document analyzes water glass as a physical system. Water glass is a transparent container filled with water.',
271
+ citation: {
272
+ ...makeKnowledgePoint().citation,
273
+ title: 'Water Glass',
274
+ snippet: 'This technical document analyzes water glass as a physical system. Water glass is a transparent container filled with water.',
275
+ },
276
+ citations: [
277
+ {
278
+ ...makeKnowledgePoint().citation,
279
+ title: 'Water Glass',
280
+ snippet: 'This technical document analyzes water glass as a physical system. Water glass is a transparent container filled with water.',
281
+ },
282
+ ],
283
+ });
284
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
285
+ message: 'what is water glass',
286
+ draftAnswer: 'This technical document analyzes water glass as a physical system.',
287
+ knowledgePoints: [point],
288
+ citations: [point.citation],
289
+ usedScope: scopedWaterglass,
290
+ graphContext: makeGraphContext(),
291
+ reviewedAt: '2026-06-19T00:05:00.000Z',
292
+ });
293
+ expect(review.decision).toBe('revise');
294
+ expect(review.failedGateIds).toContain('query_intent_alignment');
295
+ expect(review.publicAnswer).toBe('Water Glass is a transparent container filled with water.');
296
+ expect(review.gates).toEqual(expect.arrayContaining([
297
+ expect.objectContaining({
298
+ gateId: 'query_intent_alignment',
299
+ passed: false,
300
+ }),
301
+ ]));
302
+ });
303
+ test('revises Chinese definition-query answers when the draft only repeats document framing', () => {
304
+ const point = makeKnowledgePoint({
305
+ title: '水杯 (water glass)',
306
+ summary: '本技术文档旨在对“水杯”这一系统进行全面的科学分析。此处的“水杯”被定义为一个由特定流体(水)和透明非晶态固体容器(玻璃杯)组成的物理系统。',
307
+ evidenceSnippet: '本技术文档旨在对“水杯”这一系统进行全面的科学分析。此处的“水杯”被定义为一个由特定流体(水)和透明非晶态固体容器(玻璃杯)组成的物理系统。',
308
+ citation: {
309
+ ...makeKnowledgePoint().citation,
310
+ title: '水杯 (water glass)',
311
+ snippet: '本技术文档旨在对“水杯”这一系统进行全面的科学分析。此处的“水杯”被定义为一个由特定流体(水)和透明非晶态固体容器(玻璃杯)组成的物理系统。',
312
+ },
313
+ citations: [
314
+ {
315
+ ...makeKnowledgePoint().citation,
316
+ title: '水杯 (water glass)',
317
+ snippet: '本技术文档旨在对“水杯”这一系统进行全面的科学分析。此处的“水杯”被定义为一个由特定流体(水)和透明非晶态固体容器(玻璃杯)组成的物理系统。',
318
+ },
319
+ ],
320
+ });
321
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
322
+ message: '什么是waterglass?',
323
+ draftAnswer: '水杯 (water glass) 本技术文档旨在对“水杯”这一系统进行全面的科学分析。',
324
+ knowledgePoints: [point],
325
+ citations: [point.citation],
326
+ usedScope: scopedWaterglass,
327
+ graphContext: makeGraphContext(),
328
+ reviewedAt: '2026-06-19T00:07:00.000Z',
329
+ });
330
+ expect(review.decision).toBe('revise');
331
+ expect(review.failedGateIds).toContain('query_intent_alignment');
332
+ expect(review.publicAnswer).toBe('水杯 (water glass) 是一个由特定流体(水)和透明非晶态固体容器(玻璃杯)组成的物理系统。');
333
+ });
334
+ test('keeps definition revisions comprehensive with augmented evidence and graph neighborhood context', () => {
335
+ const definitionCitation = {
336
+ ...makeKnowledgePoint().citation,
337
+ atomId: 'atom_water_glass_definition',
338
+ title: '水杯 (water glass)',
339
+ snippet: '本技术文档旨在对“水杯”这一系统进行全面的科学分析。此处的“水杯”被定义为一个由特定流体(水)和透明非晶态固体容器(玻璃杯)组成的物理系统。',
340
+ };
341
+ const thermalCitation = {
342
+ ...makeKnowledgePoint().citation,
343
+ citationId: 'citation_thermal',
344
+ atomId: 'atom_water_glass_thermal',
345
+ title: '4. 热力学:热量传递',
346
+ snippet: '水杯系统与环境之间通过传导、对流和辐射进行热交换。',
347
+ };
348
+ const specificationCitation = {
349
+ ...makeKnowledgePoint().citation,
350
+ citationId: 'citation_specs',
351
+ atomId: 'atom_water_glass_specs',
352
+ title: '关键技术规格',
353
+ snippet: '标准水杯系统在标准温度和压力下包含钠钙玻璃和水的密度、杨氏模量、泊松比等参数。',
354
+ };
355
+ const point = makeKnowledgePoint({
356
+ atomId: 'atom_water_glass_definition',
357
+ atomIds: ['atom_water_glass_definition', 'atom_water_glass_thermal', 'atom_water_glass_specs'],
358
+ title: '水杯 (water glass)',
359
+ summary: definitionCitation.snippet,
360
+ evidenceSnippet: definitionCitation.snippet,
361
+ citation: definitionCitation,
362
+ citations: [definitionCitation, thermalCitation, specificationCitation],
363
+ matchedSpans: [
364
+ {
365
+ atomId: 'atom_water_glass_thermal',
366
+ title: '4. 热力学:热量传递',
367
+ snippet: thermalCitation.snippet,
368
+ sourcePath: thermalCitation.sourcePath,
369
+ startLine: 57,
370
+ endLine: 89,
371
+ score: 0.81,
372
+ citation: thermalCitation,
373
+ },
374
+ {
375
+ atomId: 'atom_water_glass_specs',
376
+ title: '关键技术规格',
377
+ snippet: specificationCitation.snippet,
378
+ sourcePath: specificationCitation.sourcePath,
379
+ startLine: 90,
380
+ endLine: 105,
381
+ score: 0.79,
382
+ citation: specificationCitation,
383
+ },
384
+ ],
385
+ });
386
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
387
+ message: '什么是waterglass?',
388
+ draftAnswer: '水杯 (water glass) 本技术文档旨在对“水杯”这一系统进行全面的科学分析。',
389
+ knowledgePoints: [point],
390
+ citations: [definitionCitation, thermalCitation, specificationCitation],
391
+ usedScope: scopedWaterglass,
392
+ graphContext: makeGraphContext({
393
+ anchorTitle: '水杯 (water glass)',
394
+ anchorGraphProfile: {
395
+ atomId: 'atom_water_glass_definition',
396
+ title: '水杯 (water glass)',
397
+ inDegree: 1,
398
+ outDegree: 2,
399
+ },
400
+ predecessorWindow: [
401
+ {
402
+ atomId: 'atom_material_science',
403
+ title: '材料科学',
404
+ relationKind: 'prerequisite',
405
+ confidence: 0.92,
406
+ },
407
+ ],
408
+ successorWindow: [
409
+ {
410
+ atomId: 'atom_thermal_model',
411
+ title: '热量传递模型',
412
+ relationKind: 'sequence',
413
+ confidence: 0.88,
414
+ },
415
+ ],
416
+ }),
417
+ reviewedAt: '2026-07-04T08:00:00.000Z',
418
+ });
419
+ expect(review.decision).toBe('revise');
420
+ expect(review.failedGateIds).toContain('query_intent_alignment');
421
+ expect(review.publicAnswer).toContain('水杯 (water glass) 是一个由特定流体(水)和透明非晶态固体容器(玻璃杯)组成的物理系统。');
422
+ expect(review.publicAnswer).toContain('热力学:热量传递');
423
+ expect(review.publicAnswer).toContain('关键技术规格');
424
+ expect(review.publicAnswer).toContain('证据摘要还显示');
425
+ expect(review.publicAnswer).toContain('入度为 1');
426
+ expect(review.publicAnswer).toContain('出度为 2');
427
+ expect(review.publicAnswer).toContain('紧邻前置节点包括 材料科学');
428
+ expect(review.publicAnswer).toContain('后续分支包括 热量传递模型');
429
+ });
430
+ test('enriches releasable definition answers with bounded evidence and graph context', () => {
431
+ const baseCitation = makeKnowledgePoint().citation;
432
+ const thermalCitation = {
433
+ ...baseCitation,
434
+ citationId: 'citation_thermal_model',
435
+ atomId: 'atom_thermal_model',
436
+ title: 'Thermal Model',
437
+ snippet: 'Thermal Model explains that the water glass exchanges heat with its environment through conduction and convection.',
438
+ startLine: 12,
439
+ endLine: 13,
440
+ score: 0.84,
441
+ };
442
+ const mermaidCitation = {
443
+ ...baseCitation,
444
+ citationId: 'citation_thermal_model_mermaid',
445
+ atomId: 'atom_thermal_model_mermaid',
446
+ title: 'Thermal Model (mermaid block)',
447
+ snippet: '```mermaid graph LR A[Water Glass] --> B[Thermal Model]',
448
+ startLine: 14,
449
+ endLine: 15,
450
+ score: 0.79,
451
+ };
452
+ const point = makeKnowledgePoint({
453
+ citations: [baseCitation, thermalCitation, mermaidCitation],
454
+ matchedSpans: [
455
+ {
456
+ atomId: 'atom_thermal_model',
457
+ title: 'Thermal Model',
458
+ snippet: thermalCitation.snippet,
459
+ sourcePath: thermalCitation.sourcePath,
460
+ startLine: 12,
461
+ endLine: 13,
462
+ score: 0.84,
463
+ citation: thermalCitation,
464
+ },
465
+ ],
466
+ });
467
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
468
+ message: 'what is water glass',
469
+ draftAnswer: 'Water glass is a transparent container filled with water.',
470
+ knowledgePoints: [point],
471
+ citations: [baseCitation, thermalCitation, mermaidCitation],
472
+ usedScope: scopedWaterglass,
473
+ graphContext: makeGraphContext({
474
+ anchorGraphProfile: {
475
+ atomId: 'atom_water_glass',
476
+ title: 'Water Glass',
477
+ inDegree: 1,
478
+ outDegree: 1,
479
+ },
480
+ predecessorWindow: [
481
+ {
482
+ atomId: 'atom_water_glass',
483
+ title: 'Water Glass',
484
+ relationKind: 'reference',
485
+ confidence: 0.99,
486
+ },
487
+ {
488
+ atomId: 'atom_container_physics',
489
+ title: 'Container Physics',
490
+ relationKind: 'prerequisite',
491
+ confidence: 0.91,
492
+ },
493
+ ],
494
+ successorWindow: [
495
+ {
496
+ atomId: 'atom_water_glass_alias',
497
+ title: 'Water Glass',
498
+ relationKind: 'reference',
499
+ confidence: 0.98,
500
+ },
501
+ {
502
+ atomId: 'atom_thermal_model_mermaid',
503
+ title: 'Thermal Model (mermaid block)',
504
+ relationKind: 'sequence',
505
+ confidence: 0.85,
506
+ },
507
+ {
508
+ atomId: 'atom_thermal_model',
509
+ title: 'Thermal Model',
510
+ relationKind: 'sequence',
511
+ confidence: 0.84,
512
+ },
513
+ ],
514
+ }),
515
+ reviewedAt: '2026-07-04T08:20:00.000Z',
516
+ });
517
+ expect(review.decision).toBe('release');
518
+ expect(review.revised).toBe(true);
519
+ expect(review.publicAnswer).toContain('Water glass is a transparent container filled with water.');
520
+ expect(review.publicAnswer).toContain('The same knowledge point also covers Thermal Model.');
521
+ expect(review.publicAnswer).toContain('Evidence highlights: Thermal Model');
522
+ expect(review.publicAnswer).toContain('Water Glass has 1 incoming and 1 outgoing links');
523
+ expect(review.publicAnswer).toContain('its immediate predecessors include Container Physics');
524
+ expect(review.publicAnswer).toContain('likely next nodes include Thermal Model');
525
+ expect(review.publicAnswer).not.toContain('predecessors include Water Glass');
526
+ expect(review.publicAnswer).not.toContain('next nodes include Water Glass');
527
+ expect(review.publicAnswer).not.toContain('(mermaid block)');
528
+ expect(review.publicAnswer).not.toContain('```');
529
+ });
530
+ test('keeps RAG evidence in revised definition answers after public-surface contraction', () => {
531
+ const baseCitation = makeKnowledgePoint({
532
+ title: 'Water Glass',
533
+ summary: 'Water glass is a transparent drinking vessel containing water.',
534
+ evidenceSnippet: 'Water glass is a transparent drinking vessel containing water.',
535
+ citation: {
536
+ ...makeKnowledgePoint().citation,
537
+ title: 'Water Glass',
538
+ snippet: 'Water glass is a transparent drinking vessel containing water.',
539
+ },
540
+ }).citation;
541
+ const point = makeKnowledgePoint({
542
+ title: 'Water Glass',
543
+ summary: baseCitation.snippet,
544
+ evidenceSnippet: baseCitation.snippet,
545
+ citation: baseCitation,
546
+ citations: [baseCitation],
547
+ });
548
+ const ragContextPack = {
549
+ query: 'what is water glass?',
550
+ generatedAt: '2026-07-05T08:30:00.000Z',
551
+ sourceBoundary: 'full_document',
552
+ budget: {
553
+ maxFragments: 6,
554
+ maxCharsPerFragment: 600,
555
+ maxTotalChars: 2400,
556
+ },
557
+ fragments: [
558
+ {
559
+ fragmentId: 'rag_direct_water_glass',
560
+ role: 'direct_support',
561
+ text: 'Water glass is a transparent drinking vessel containing water.',
562
+ atomId: 'atom_water_glass',
563
+ documentId: 'doc_water_glass',
564
+ sourcePath: 'Knowledge_Base/waterglass/water-glass.md',
565
+ title: 'Water Glass',
566
+ headingPath: ['Water Glass', 'Definition'],
567
+ startLine: 3,
568
+ endLine: 3,
569
+ charCount: 61,
570
+ tokenEstimate: 16,
571
+ truncated: false,
572
+ citationIds: ['citation_water_glass'],
573
+ sourceBoundary: 'direct_span_only',
574
+ },
575
+ {
576
+ fragmentId: 'rag_parent_water_glass_boundary',
577
+ role: 'parent_context',
578
+ text: 'Mechanism: The vessel boundary and water surface jointly determine the observed optical behavior.',
579
+ atomId: 'atom_water_glass',
580
+ documentId: 'doc_water_glass',
581
+ sourcePath: 'Knowledge_Base/waterglass/water-glass.md',
582
+ title: 'Boundary',
583
+ headingPath: ['Water Glass', 'Boundary'],
584
+ startLine: 7,
585
+ endLine: 8,
586
+ charCount: 83,
587
+ tokenEstimate: 18,
588
+ truncated: false,
589
+ citationIds: ['citation_boundary'],
590
+ sourceBoundary: 'full_document',
591
+ },
592
+ {
593
+ fragmentId: 'rag_graph_water_glass_refraction',
594
+ role: 'graph_neighbor_support',
595
+ text: 'Graph caveat: Light refracts through air, glass, and water, so the cup can act like a simple optical lens.',
596
+ atomId: 'atom_refraction',
597
+ documentId: 'doc_water_glass',
598
+ sourcePath: 'Knowledge_Base/waterglass/water-glass.md',
599
+ title: 'Optical Refraction',
600
+ headingPath: ['Water Glass', 'Optics'],
601
+ startLine: 11,
602
+ endLine: 12,
603
+ charCount: 93,
604
+ tokenEstimate: 22,
605
+ truncated: false,
606
+ citationIds: ['citation_refraction'],
607
+ relationEdgeIds: ['edge_water_glass_refraction'],
608
+ sourceBoundary: 'direct_span_only',
609
+ },
610
+ ],
611
+ sourceDecisions: [],
612
+ totalCharCount: 237,
613
+ tokenEstimate: 56,
614
+ };
615
+ const ragSufficiencyReview = {
616
+ reviewedAt: '2026-07-05T08:30:00.000Z',
617
+ status: 'sufficient',
618
+ score: 0.91,
619
+ reasons: [],
620
+ deterministic: true,
621
+ recoveryAttempted: false,
622
+ llmJudgeUsed: false,
623
+ degradationState: 'none',
624
+ };
625
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
626
+ message: 'what is water glass?',
627
+ draftAnswer: [
628
+ 'Grounded by RAG context.',
629
+ 'Water glass is a transparent drinking vessel containing water.',
630
+ 'The vessel boundary and water surface jointly determine the observed optical behavior.',
631
+ 'Light refracts through air, glass, and water, so the cup can act like a simple optical lens.',
632
+ ].join(' '),
633
+ knowledgePoints: [point],
634
+ citations: [baseCitation],
635
+ usedScope: scopedWaterglass,
636
+ graphContext: makeGraphContext({
637
+ anchorTitle: 'Water Glass',
638
+ predecessorWindow: [
639
+ {
640
+ atomId: 'atom_container_physics',
641
+ title: 'Container Physics',
642
+ relationKind: 'prerequisite',
643
+ confidence: 0.88,
644
+ },
645
+ ],
646
+ successorWindow: [
647
+ {
648
+ atomId: 'atom_refraction',
649
+ title: 'Optical Refraction',
650
+ relationKind: 'sequence',
651
+ confidence: 0.86,
652
+ },
653
+ ],
654
+ }),
655
+ ragContextPack,
656
+ ragSufficiencyReview,
657
+ reviewedAt: '2026-07-05T08:31:00.000Z',
658
+ });
659
+ expect(review.decision).toBe('revise');
660
+ expect(review.failedGateIds).toContain('public_surface_contraction');
661
+ expect(review.publicAnswer).toContain('transparent drinking vessel');
662
+ expect(review.publicAnswer).toContain('vessel boundary and water surface');
663
+ expect(review.publicAnswer).toContain('Light refracts through air, glass, and water');
664
+ expect(review.publicAnswer).not.toContain('Mechanism:');
665
+ expect(review.publicAnswer).not.toContain('Graph caveat:');
666
+ expect(review.publicAnswer).not.toContain('Grounded by RAG context');
667
+ expect(review.publicAnswer).not.toContain('immediate predecessors');
668
+ expect(review.publicAnswer).not.toContain('likely next nodes');
669
+ });
670
+ test('revises RAG answers when a public claim lacks citation-backed fragment support', () => {
671
+ const baseCitation = {
672
+ ...makeKnowledgePoint().citation,
673
+ title: 'Water Glass',
674
+ snippet: 'Water glass is a transparent drinking vessel containing water.',
675
+ };
676
+ const point = makeKnowledgePoint({
677
+ title: 'Water Glass',
678
+ summary: baseCitation.snippet,
679
+ evidenceSnippet: baseCitation.snippet,
680
+ citation: baseCitation,
681
+ citations: [baseCitation],
682
+ });
683
+ const ragContextPack = {
684
+ query: 'what is water glass?',
685
+ generatedAt: '2026-07-05T09:10:00.000Z',
686
+ sourceBoundary: 'full_document',
687
+ budget: {
688
+ maxFragments: 4,
689
+ maxCharsPerFragment: 600,
690
+ maxTotalChars: 1600,
691
+ },
692
+ fragments: [
693
+ {
694
+ fragmentId: 'rag_direct_water_glass',
695
+ role: 'direct_support',
696
+ text: 'Water glass is a transparent drinking vessel containing water.',
697
+ atomId: 'atom_water_glass',
698
+ documentId: 'doc_water_glass',
699
+ sourcePath: 'Knowledge_Base/waterglass/water-glass.md',
700
+ title: 'Water Glass',
701
+ headingPath: ['Water Glass', 'Definition'],
702
+ startLine: 3,
703
+ endLine: 3,
704
+ charCount: 61,
705
+ tokenEstimate: 16,
706
+ truncated: false,
707
+ citationIds: ['citation_water_glass'],
708
+ sourceBoundary: 'direct_span_only',
709
+ },
710
+ ],
711
+ sourceDecisions: [],
712
+ totalCharCount: 61,
713
+ tokenEstimate: 16,
714
+ };
715
+ const ragSufficiencyReview = {
716
+ reviewedAt: '2026-07-05T09:10:00.000Z',
717
+ status: 'sufficient',
718
+ score: 0.89,
719
+ reasons: [],
720
+ deterministic: true,
721
+ recoveryAttempted: false,
722
+ llmJudgeUsed: false,
723
+ degradationState: 'none',
724
+ };
725
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
726
+ message: 'what is water glass?',
727
+ draftAnswer: 'Water glass is a transparent drinking vessel containing water. It is dishwasher safe and made in Italy.',
728
+ knowledgePoints: [point],
729
+ citations: [baseCitation],
730
+ usedScope: scopedWaterglass,
731
+ graphContext: makeGraphContext(),
732
+ ragContextPack,
733
+ ragSufficiencyReview,
734
+ reviewedAt: '2026-07-05T09:11:00.000Z',
735
+ });
736
+ expect(review.decision).toBe('revise');
737
+ expect(review.failedGateIds).toContain('rag_claim_citation_support');
738
+ expect(review.publicAnswer).toContain('transparent drinking vessel');
739
+ expect(review.publicAnswer).not.toContain('dishwasher');
740
+ expect(review.publicAnswer).not.toContain('Italy');
741
+ });
742
+ test('revises how-to RAG drafts that omit profile-required failure handling', () => {
743
+ const baseCitation = {
744
+ ...makeKnowledgePoint().citation,
745
+ title: 'Prism Alignment',
746
+ snippet: 'Step 1: clean the lens mount before calibration.',
747
+ };
748
+ const point = makeKnowledgePoint({
749
+ title: 'Prism Alignment',
750
+ summary: baseCitation.snippet,
751
+ evidenceSnippet: baseCitation.snippet,
752
+ citation: baseCitation,
753
+ citations: [baseCitation],
754
+ });
755
+ const ragContextPack = {
756
+ query: 'how to calibrate prism alignment?',
757
+ generatedAt: '2026-07-07T10:00:00.000Z',
758
+ sourceBoundary: 'full_document',
759
+ budget: {
760
+ maxFragments: 6,
761
+ maxCharsPerFragment: 900,
762
+ maxTotalChars: 2600,
763
+ },
764
+ fragments: [
765
+ {
766
+ fragmentId: 'rag_direct_prism_steps',
767
+ role: 'direct_support',
768
+ text: 'Step 1: clean the lens mount before calibration. Step 2: lock the clamp before measuring beam position.',
769
+ atomId: 'atom_prism_alignment',
770
+ documentId: 'doc_prism_alignment',
771
+ sourcePath: 'Knowledge_Base/test/prism-alignment.md',
772
+ title: 'Prism Alignment',
773
+ headingPath: ['Prism Alignment', 'Procedure'],
774
+ startLine: 8,
775
+ endLine: 10,
776
+ charCount: 101,
777
+ tokenEstimate: 24,
778
+ truncated: false,
779
+ citationIds: ['citation_prism_steps'],
780
+ sourceBoundary: 'direct_span_only',
781
+ },
782
+ {
783
+ fragmentId: 'rag_parent_prism_prerequisite',
784
+ role: 'parent_context',
785
+ text: 'Prerequisite: use a stable bench and confirm the laser is off before touching the mount.',
786
+ atomId: 'atom_prism_alignment',
787
+ documentId: 'doc_prism_alignment',
788
+ sourcePath: 'Knowledge_Base/test/prism-alignment.md',
789
+ title: 'Prerequisites',
790
+ headingPath: ['Prism Alignment', 'Prerequisites'],
791
+ startLine: 4,
792
+ endLine: 5,
793
+ charCount: 86,
794
+ tokenEstimate: 18,
795
+ truncated: false,
796
+ citationIds: ['citation_prism_prerequisite'],
797
+ sourceBoundary: 'full_document',
798
+ },
799
+ {
800
+ fragmentId: 'rag_graph_prism_failure',
801
+ role: 'graph_neighbor_support',
802
+ text: 'Failure mode: if the beam drifts, repeat clamp inspection before measuring.',
803
+ atomId: 'atom_beam_drift_check',
804
+ documentId: 'doc_beam_drift_check',
805
+ sourcePath: 'Knowledge_Base/test/beam-drift-check.md',
806
+ title: 'Failure Handling',
807
+ headingPath: ['Prism Alignment', 'Failure Handling'],
808
+ startLine: 14,
809
+ endLine: 15,
810
+ charCount: 78,
811
+ tokenEstimate: 16,
812
+ truncated: false,
813
+ citationIds: ['citation_prism_failure'],
814
+ relationEdgeIds: ['edge_prism_failure'],
815
+ sourceBoundary: 'full_document',
816
+ },
817
+ ],
818
+ sourceDecisions: [],
819
+ totalCharCount: 265,
820
+ tokenEstimate: 58,
821
+ };
822
+ const ragSufficiencyReview = {
823
+ reviewedAt: '2026-07-07T10:00:00.000Z',
824
+ status: 'sufficient',
825
+ score: 0.9,
826
+ reasons: [],
827
+ deterministic: true,
828
+ recoveryAttempted: false,
829
+ llmJudgeUsed: false,
830
+ degradationState: 'none',
831
+ };
832
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
833
+ message: 'how to calibrate prism alignment?',
834
+ draftAnswer: 'Step 1: clean the lens mount before calibration. Step 2: lock the clamp before measuring beam position.',
835
+ knowledgePoints: [point],
836
+ citations: [baseCitation],
837
+ usedScope: scopedWaterglass,
838
+ graphContext: makeGraphContext(),
839
+ ragContextPack,
840
+ ragSufficiencyReview,
841
+ reviewedAt: '2026-07-07T10:01:00.000Z',
842
+ });
843
+ expect(review.decision).toBe('revise');
844
+ expect(review.failedGateIds).toContain('rag_answer_completeness');
845
+ expect(review.publicAnswer).toContain('Step 1: clean the lens mount');
846
+ expect(review.publicAnswer).toContain('use a stable bench');
847
+ expect(review.publicAnswer).toContain('if the beam drifts');
848
+ expect(review.publicAnswer).not.toContain('Prerequisite:');
849
+ expect(review.publicAnswer).not.toContain('Failure mode:');
850
+ });
851
+ test('revises causal RAG drafts that omit downstream consequence evidence', () => {
852
+ const baseCitation = {
853
+ ...makeKnowledgePoint().citation,
854
+ title: 'Beam Drift Cause',
855
+ snippet: 'Beam drift occurs because clamp relaxation changes the prism angle.',
856
+ };
857
+ const point = makeKnowledgePoint({
858
+ title: 'Beam Drift Cause',
859
+ summary: baseCitation.snippet,
860
+ evidenceSnippet: baseCitation.snippet,
861
+ citation: baseCitation,
862
+ citations: [baseCitation],
863
+ });
864
+ const ragContextPack = {
865
+ query: 'why does beam drift happen?',
866
+ generatedAt: '2026-07-07T10:10:00.000Z',
867
+ sourceBoundary: 'full_document',
868
+ budget: {
869
+ maxFragments: 6,
870
+ maxCharsPerFragment: 900,
871
+ maxTotalChars: 2600,
872
+ },
873
+ fragments: [
874
+ {
875
+ fragmentId: 'rag_direct_beam_drift_cause',
876
+ role: 'direct_support',
877
+ text: 'Mechanism: Beam drift occurs because clamp relaxation changes the prism angle.',
878
+ atomId: 'atom_beam_drift_cause',
879
+ documentId: 'doc_beam_drift_cause',
880
+ sourcePath: 'Knowledge_Base/test/beam-drift-cause.md',
881
+ title: 'Beam Drift Cause',
882
+ headingPath: ['Beam Drift Cause', 'Mechanism'],
883
+ startLine: 8,
884
+ endLine: 9,
885
+ charCount: 78,
886
+ tokenEstimate: 18,
887
+ truncated: false,
888
+ citationIds: ['citation_beam_drift_cause'],
889
+ sourceBoundary: 'direct_span_only',
890
+ },
891
+ {
892
+ fragmentId: 'rag_graph_beam_drift_downstream',
893
+ role: 'graph_neighbor_support',
894
+ text: 'Downstream consequence: centroid drift invalidates the calibration reading.',
895
+ atomId: 'atom_beam_drift_downstream',
896
+ documentId: 'doc_beam_drift_downstream',
897
+ sourcePath: 'Knowledge_Base/test/beam-drift-downstream.md',
898
+ title: 'Beam Drift Downstream',
899
+ headingPath: ['Beam Drift Cause', 'Downstream Consequence'],
900
+ startLine: 13,
901
+ endLine: 14,
902
+ charCount: 74,
903
+ tokenEstimate: 16,
904
+ truncated: false,
905
+ citationIds: ['citation_beam_drift_downstream'],
906
+ relationEdgeIds: ['edge_beam_drift_downstream'],
907
+ sourceBoundary: 'full_document',
908
+ },
909
+ ],
910
+ sourceDecisions: [],
911
+ totalCharCount: 152,
912
+ tokenEstimate: 34,
913
+ };
914
+ const ragSufficiencyReview = {
915
+ reviewedAt: '2026-07-07T10:10:00.000Z',
916
+ status: 'sufficient',
917
+ score: 0.88,
918
+ reasons: [],
919
+ deterministic: true,
920
+ recoveryAttempted: false,
921
+ llmJudgeUsed: false,
922
+ degradationState: 'none',
923
+ };
924
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
925
+ message: 'why does beam drift happen?',
926
+ draftAnswer: 'Beam drift occurs because clamp relaxation changes the prism angle.',
927
+ knowledgePoints: [point],
928
+ citations: [baseCitation],
929
+ usedScope: scopedWaterglass,
930
+ graphContext: makeGraphContext(),
931
+ ragContextPack,
932
+ ragSufficiencyReview,
933
+ reviewedAt: '2026-07-07T10:11:00.000Z',
934
+ });
935
+ expect(review.decision).toBe('revise');
936
+ expect(review.failedGateIds).toContain('rag_answer_completeness');
937
+ expect(review.publicAnswer).toContain('Beam drift occurs because clamp relaxation changes the prism angle');
938
+ expect(review.publicAnswer).toContain('centroid drift invalidates the calibration reading');
939
+ expect(review.publicAnswer).not.toContain('Mechanism:');
940
+ expect(review.publicAnswer).not.toContain('Downstream consequence:');
941
+ });
942
+ test('filters prompt-style preamble clauses from RAG-grounded revisions', () => {
943
+ const baseCitation = {
944
+ ...makeKnowledgePoint().citation,
945
+ title: '水杯 (water glass)',
946
+ snippet: '此处的“水杯”被定义为一个由特定流体(水)和透明非晶态固体容器(玻璃杯)组成的物理系统。',
947
+ };
948
+ const point = makeKnowledgePoint({
949
+ title: '水杯 (water glass)',
950
+ summary: baseCitation.snippet,
951
+ evidenceSnippet: baseCitation.snippet,
952
+ citation: baseCitation,
953
+ citations: [baseCitation],
954
+ });
955
+ const ragContextPack = {
956
+ query: '什么是water glass',
957
+ generatedAt: '2026-07-05T09:20:00.000Z',
958
+ sourceBoundary: 'full_document',
959
+ budget: {
960
+ maxFragments: 4,
961
+ maxCharsPerFragment: 600,
962
+ maxTotalChars: 1600,
963
+ },
964
+ fragments: [
965
+ {
966
+ fragmentId: 'rag_direct_water_glass_zh',
967
+ role: 'direct_support',
968
+ text: '此处的“水杯”被定义为一个由特定流体(水)和透明非晶态固体容器(玻璃杯)组成的物理系统。',
969
+ atomId: 'atom_water_glass',
970
+ documentId: 'doc_water_glass',
971
+ sourcePath: 'Knowledge_Base/waterglass/water-glass.md',
972
+ title: '水杯 (water glass)',
973
+ headingPath: ['水杯 (water glass)'],
974
+ startLine: 3,
975
+ endLine: 3,
976
+ charCount: 48,
977
+ tokenEstimate: 24,
978
+ truncated: false,
979
+ citationIds: ['citation_water_glass'],
980
+ sourceBoundary: 'direct_span_only',
981
+ },
982
+ {
983
+ fragmentId: 'rag_parent_water_glass_preamble',
984
+ role: 'parent_context',
985
+ text: [
986
+ '好的,遵从您的指示,我将仅基于标题“water glass”创建一份具有科学和数学严谨性的综合技术文档。',
987
+ '所有推理过程以英文进行,最终输出为简体中文。',
988
+ '核心概念及其数学基础 “水杯”系统是多个物理学分支交叉的绝佳范例。',
989
+ ].join('\n\n'),
990
+ atomId: 'atom_water_glass',
991
+ documentId: 'doc_water_glass',
992
+ sourcePath: 'Knowledge_Base/waterglass/water-glass.md',
993
+ title: 'water glass.md preamble',
994
+ headingPath: ['preamble'],
995
+ startLine: 1,
996
+ endLine: 8,
997
+ charCount: 97,
998
+ tokenEstimate: 48,
999
+ truncated: false,
1000
+ citationIds: ['citation_preamble'],
1001
+ sourceBoundary: 'full_document',
1002
+ },
1003
+ ],
1004
+ sourceDecisions: [],
1005
+ totalCharCount: 145,
1006
+ tokenEstimate: 72,
1007
+ };
1008
+ const ragSufficiencyReview = {
1009
+ reviewedAt: '2026-07-05T09:20:00.000Z',
1010
+ status: 'sufficient',
1011
+ score: 0.9,
1012
+ reasons: [],
1013
+ deterministic: true,
1014
+ recoveryAttempted: false,
1015
+ llmJudgeUsed: false,
1016
+ degradationState: 'none',
1017
+ };
1018
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
1019
+ message: '什么是water glass',
1020
+ draftAnswer: 'Grounded by RAG context. 此处的“水杯”被定义为一个由特定流体(水)和透明非晶态固体容器(玻璃杯)组成的物理系统。',
1021
+ knowledgePoints: [point],
1022
+ citations: [baseCitation],
1023
+ usedScope: scopedWaterglass,
1024
+ graphContext: makeGraphContext(),
1025
+ ragContextPack,
1026
+ ragSufficiencyReview,
1027
+ reviewedAt: '2026-07-05T09:21:00.000Z',
1028
+ });
1029
+ expect(review.decision).toBe('revise');
1030
+ expect(review.publicAnswer).toContain('透明非晶态固体容器');
1031
+ expect(review.publicAnswer).toContain('多个物理学分支');
1032
+ expect(review.publicAnswer).not.toContain('遵从您的指示');
1033
+ expect(review.publicAnswer).not.toContain('所有推理过程');
1034
+ expect(review.publicAnswer).not.toContain('最终输出');
1035
+ });
1036
+ test('revises grounded answers when structured numeric facts conflict with support', () => {
1037
+ const densityPoint = makeKnowledgePoint({
1038
+ title: 'Water Density',
1039
+ summary: 'Water density is 999.8 kg/m3 at STP.',
1040
+ evidenceSnippet: 'Water density is 999.8 kg/m3 at STP.',
1041
+ citation: {
1042
+ ...makeKnowledgePoint().citation,
1043
+ title: 'Water Density',
1044
+ snippet: 'Water density is 999.8 kg/m3 at STP.',
1045
+ },
1046
+ citations: [
1047
+ {
1048
+ ...makeKnowledgePoint().citation,
1049
+ title: 'Water Density',
1050
+ snippet: 'Water density is 999.8 kg/m3 at STP.',
1051
+ },
1052
+ ],
1053
+ });
1054
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
1055
+ message: 'what is the density of water',
1056
+ draftAnswer: 'Water density is 875 kg/m3 at STP.',
1057
+ knowledgePoints: [densityPoint],
1058
+ citations: [densityPoint.citation],
1059
+ usedScope: scopedWaterglass,
1060
+ graphContext: makeGraphContext(),
1061
+ reviewedAt: '2026-06-19T00:10:00.000Z',
1062
+ });
1063
+ expect(review.decision).toBe('revise');
1064
+ expect(review.failedGateIds).toContain('claim_structured_consistency');
1065
+ expect(review.publicAnswer).toBe('Water density is 999.8 kg/m3 at STP.');
1066
+ expect(review.gates).toEqual(expect.arrayContaining([
1067
+ expect.objectContaining({
1068
+ gateId: 'claim_structured_consistency',
1069
+ passed: false,
1070
+ }),
1071
+ ]));
1072
+ });
1073
+ test('keeps grounded answers when one of several supported structured values matches', () => {
1074
+ const tablePoint = makeKnowledgePoint({
1075
+ title: 'Water Density Table',
1076
+ summary: 'Reference values: glass density 2500 kg/m3; water density 999.8 kg/m3.',
1077
+ evidenceSnippet: 'Reference values: glass density 2500 kg/m3; water density 999.8 kg/m3.',
1078
+ citation: {
1079
+ ...makeKnowledgePoint().citation,
1080
+ title: 'Water Density Table',
1081
+ snippet: 'Reference values: glass density 2500 kg/m3; water density 999.8 kg/m3.',
1082
+ },
1083
+ citations: [
1084
+ {
1085
+ ...makeKnowledgePoint().citation,
1086
+ title: 'Water Density Table',
1087
+ snippet: 'Reference values: glass density 2500 kg/m3; water density 999.8 kg/m3.',
1088
+ },
1089
+ ],
1090
+ });
1091
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
1092
+ message: 'what is the density of water',
1093
+ draftAnswer: 'Water density is 999.8 kg/m3.',
1094
+ knowledgePoints: [tablePoint],
1095
+ citations: [tablePoint.citation],
1096
+ usedScope: scopedWaterglass,
1097
+ graphContext: makeGraphContext(),
1098
+ reviewedAt: '2026-06-19T00:20:00.000Z',
1099
+ });
1100
+ expect(review.decision).toBe('release');
1101
+ expect(review.failedGateIds).not.toContain('claim_structured_consistency');
1102
+ expect(review.publicAnswer).toBe('Water density is 999.8 kg/m3.');
1103
+ expect(review.gates).toEqual(expect.arrayContaining([
1104
+ expect.objectContaining({
1105
+ gateId: 'claim_structured_consistency',
1106
+ passed: true,
1107
+ }),
1108
+ ]));
1109
+ });
1110
+ test('revises grounded answers when year claims conflict with support', () => {
1111
+ const yearPoint = makeKnowledgePoint({
1112
+ title: 'Glass-Steagall Act',
1113
+ summary: 'The Glass-Steagall Act was enacted in 1933.',
1114
+ evidenceSnippet: 'The Glass-Steagall Act was enacted in 1933.',
1115
+ citation: {
1116
+ ...makeKnowledgePoint().citation,
1117
+ title: 'Glass-Steagall Act',
1118
+ snippet: 'The Glass-Steagall Act was enacted in 1933.',
1119
+ },
1120
+ citations: [
1121
+ {
1122
+ ...makeKnowledgePoint().citation,
1123
+ title: 'Glass-Steagall Act',
1124
+ snippet: 'The Glass-Steagall Act was enacted in 1933.',
1125
+ },
1126
+ ],
1127
+ });
1128
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
1129
+ message: 'when was the Glass-Steagall Act enacted',
1130
+ draftAnswer: 'The Glass-Steagall Act was enacted in 1935.',
1131
+ knowledgePoints: [yearPoint],
1132
+ citations: [yearPoint.citation],
1133
+ usedScope: scopedWaterglass,
1134
+ graphContext: makeGraphContext(),
1135
+ reviewedAt: '2026-06-19T00:30:00.000Z',
1136
+ });
1137
+ expect(review.decision).toBe('revise');
1138
+ expect(review.failedGateIds).toContain('claim_structured_consistency');
1139
+ expect(review.publicAnswer).toBe('The Glass-Steagall Act was enacted in 1933.');
1140
+ });
1141
+ test('revises grounded answers when structured comparison claims invert cited support', () => {
1142
+ const point = makeKnowledgePoint({
1143
+ title: 'Density Comparison',
1144
+ summary: 'Glass density is 2500 kg/m3. Water density is 999.8 kg/m3.',
1145
+ evidenceSnippet: 'Glass density is 2500 kg/m3. Water density is 999.8 kg/m3.',
1146
+ citation: {
1147
+ ...makeKnowledgePoint().citation,
1148
+ title: 'Density Comparison',
1149
+ snippet: 'Glass density is 2500 kg/m3. Water density is 999.8 kg/m3.',
1150
+ },
1151
+ citations: [
1152
+ {
1153
+ ...makeKnowledgePoint().citation,
1154
+ title: 'Density Comparison',
1155
+ snippet: 'Glass density is 2500 kg/m3. Water density is 999.8 kg/m3.',
1156
+ },
1157
+ ],
1158
+ });
1159
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
1160
+ message: 'Which density is higher, glass or water?',
1161
+ draftAnswer: 'Water density is higher than glass density.',
1162
+ knowledgePoints: [point],
1163
+ citations: [point.citation],
1164
+ usedScope: scopedWaterglass,
1165
+ graphContext: makeGraphContext(),
1166
+ reviewedAt: '2026-06-19T01:00:00.000Z',
1167
+ });
1168
+ expect(review.decision).toBe('revise');
1169
+ expect(review.failedGateIds).toContain('claim_structured_comparison_consistency');
1170
+ expect(review.publicAnswer).toBe('Glass density is higher than water density.');
1171
+ expect(review.gates).toEqual(expect.arrayContaining([
1172
+ expect.objectContaining({
1173
+ gateId: 'claim_structured_comparison_consistency',
1174
+ passed: false,
1175
+ }),
1176
+ ]));
1177
+ });
1178
+ test('revises grounded answers when Chinese structured comparison claims invert cited support', () => {
1179
+ const point = makeKnowledgePoint({
1180
+ title: '密度对比',
1181
+ summary: '玻璃密度是2500 kg/m3。水密度是999.8 kg/m3。',
1182
+ evidenceSnippet: '玻璃密度是2500 kg/m3。水密度是999.8 kg/m3。',
1183
+ citation: {
1184
+ ...makeKnowledgePoint().citation,
1185
+ title: '密度对比',
1186
+ snippet: '玻璃密度是2500 kg/m3。水密度是999.8 kg/m3。',
1187
+ },
1188
+ citations: [
1189
+ {
1190
+ ...makeKnowledgePoint().citation,
1191
+ title: '密度对比',
1192
+ snippet: '玻璃密度是2500 kg/m3。水密度是999.8 kg/m3。',
1193
+ },
1194
+ ],
1195
+ });
1196
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
1197
+ message: '玻璃和水哪个密度更高?',
1198
+ draftAnswer: '水密度高于玻璃密度。',
1199
+ knowledgePoints: [point],
1200
+ citations: [point.citation],
1201
+ usedScope: scopedWaterglass,
1202
+ graphContext: makeGraphContext(),
1203
+ reviewedAt: '2026-06-19T01:05:00.000Z',
1204
+ });
1205
+ expect(review.decision).toBe('revise');
1206
+ expect(review.failedGateIds).toContain('claim_structured_comparison_consistency');
1207
+ expect(review.publicAnswer).toBe('玻璃密度高于水密度。');
1208
+ expect(review.gates).toEqual(expect.arrayContaining([
1209
+ expect.objectContaining({
1210
+ gateId: 'claim_structured_comparison_consistency',
1211
+ passed: false,
1212
+ }),
1213
+ ]));
1214
+ });
1215
+ test('does not raise a structured comparison conflict when the comparison stays aligned with support', () => {
1216
+ const point = makeKnowledgePoint({
1217
+ title: 'Density Comparison',
1218
+ summary: 'Glass density is 2500 kg/m3. Water density is 999.8 kg/m3.',
1219
+ evidenceSnippet: 'Glass density is 2500 kg/m3. Water density is 999.8 kg/m3.',
1220
+ citation: {
1221
+ ...makeKnowledgePoint().citation,
1222
+ title: 'Density Comparison',
1223
+ snippet: 'Glass density is 2500 kg/m3. Water density is 999.8 kg/m3.',
1224
+ },
1225
+ citations: [
1226
+ {
1227
+ ...makeKnowledgePoint().citation,
1228
+ title: 'Density Comparison',
1229
+ snippet: 'Glass density is 2500 kg/m3. Water density is 999.8 kg/m3.',
1230
+ },
1231
+ ],
1232
+ });
1233
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
1234
+ message: 'Which density is higher, glass or water?',
1235
+ draftAnswer: 'Glass density is higher than water density.',
1236
+ knowledgePoints: [point],
1237
+ citations: [point.citation],
1238
+ usedScope: scopedWaterglass,
1239
+ graphContext: makeGraphContext(),
1240
+ reviewedAt: '2026-06-19T01:10:00.000Z',
1241
+ });
1242
+ expect(review.decision).toBe('release');
1243
+ expect(review.failedGateIds).not.toContain('claim_structured_comparison_consistency');
1244
+ expect(review.gates).toEqual(expect.arrayContaining([
1245
+ expect.objectContaining({
1246
+ gateId: 'claim_structured_comparison_consistency',
1247
+ passed: true,
1248
+ }),
1249
+ ]));
1250
+ });
1251
+ test('does not raise a structured comparison conflict when the draft compares different structured properties', () => {
1252
+ const point = makeKnowledgePoint({
1253
+ title: 'Mixed Structured Facts',
1254
+ summary: 'Glass density is 2500 kg/m3. Water temperature is 293 K.',
1255
+ evidenceSnippet: 'Glass density is 2500 kg/m3. Water temperature is 293 K.',
1256
+ citation: {
1257
+ ...makeKnowledgePoint().citation,
1258
+ title: 'Mixed Structured Facts',
1259
+ snippet: 'Glass density is 2500 kg/m3. Water temperature is 293 K.',
1260
+ },
1261
+ citations: [
1262
+ {
1263
+ ...makeKnowledgePoint().citation,
1264
+ title: 'Mixed Structured Facts',
1265
+ snippet: 'Glass density is 2500 kg/m3. Water temperature is 293 K.',
1266
+ },
1267
+ ],
1268
+ });
1269
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
1270
+ message: 'compare the reported density and temperature',
1271
+ draftAnswer: 'Glass density is higher than water temperature.',
1272
+ knowledgePoints: [point],
1273
+ citations: [point.citation],
1274
+ usedScope: scopedWaterglass,
1275
+ graphContext: makeGraphContext(),
1276
+ reviewedAt: '2026-06-19T01:15:00.000Z',
1277
+ });
1278
+ expect(review.decision).toBe('release');
1279
+ expect(review.failedGateIds).not.toContain('claim_structured_comparison_consistency');
1280
+ expect(review.gates).toEqual(expect.arrayContaining([
1281
+ expect.objectContaining({
1282
+ gateId: 'claim_structured_comparison_consistency',
1283
+ passed: true,
1284
+ }),
1285
+ ]));
1286
+ });
1287
+ test('revises grounded answers when the subject changes but the supported fact tail stays the same', () => {
1288
+ const point = makeKnowledgePoint({
1289
+ title: 'Water Density',
1290
+ summary: 'Water density is 999.8 kg/m3 at STP.',
1291
+ evidenceSnippet: 'Water density is 999.8 kg/m3 at STP.',
1292
+ citation: {
1293
+ ...makeKnowledgePoint().citation,
1294
+ title: 'Water Density',
1295
+ snippet: 'Water density is 999.8 kg/m3 at STP.',
1296
+ },
1297
+ citations: [
1298
+ {
1299
+ ...makeKnowledgePoint().citation,
1300
+ title: 'Water Density',
1301
+ snippet: 'Water density is 999.8 kg/m3 at STP.',
1302
+ },
1303
+ ],
1304
+ });
1305
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
1306
+ message: 'what is the density of water',
1307
+ draftAnswer: 'Glass density is 999.8 kg/m3 at STP.',
1308
+ knowledgePoints: [point],
1309
+ citations: [point.citation],
1310
+ usedScope: scopedWaterglass,
1311
+ graphContext: makeGraphContext(),
1312
+ reviewedAt: '2026-06-19T02:00:00.000Z',
1313
+ });
1314
+ expect(review.decision).toBe('revise');
1315
+ expect(review.failedGateIds).toContain('claim_subject_consistency');
1316
+ expect(review.publicAnswer).toBe('Water density is 999.8 kg/m3 at STP.');
1317
+ expect(review.gates).toEqual(expect.arrayContaining([
1318
+ expect.objectContaining({
1319
+ gateId: 'claim_subject_consistency',
1320
+ passed: false,
1321
+ }),
1322
+ ]));
1323
+ });
1324
+ test('does not raise a subject conflict when the supported subject and draft subject are still the same entity', () => {
1325
+ const point = makeKnowledgePoint({
1326
+ title: 'Water Glass',
1327
+ summary: 'Water glass is a transparent container filled with water.',
1328
+ evidenceSnippet: 'Water glass is a transparent container filled with water.',
1329
+ citation: {
1330
+ ...makeKnowledgePoint().citation,
1331
+ title: 'Water Glass',
1332
+ snippet: 'Water glass is a transparent container filled with water.',
1333
+ },
1334
+ citations: [
1335
+ {
1336
+ ...makeKnowledgePoint().citation,
1337
+ title: 'Water Glass',
1338
+ snippet: 'Water glass is a transparent container filled with water.',
1339
+ },
1340
+ ],
1341
+ });
1342
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
1343
+ message: 'what is water glass',
1344
+ draftAnswer: 'A water glass is a transparent container filled with water.',
1345
+ knowledgePoints: [point],
1346
+ citations: [point.citation],
1347
+ usedScope: scopedWaterglass,
1348
+ graphContext: makeGraphContext(),
1349
+ reviewedAt: '2026-06-19T02:05:00.000Z',
1350
+ });
1351
+ expect(review.decision).toBe('release');
1352
+ expect(review.failedGateIds).not.toContain('claim_subject_consistency');
1353
+ expect(review.gates).toEqual(expect.arrayContaining([
1354
+ expect.objectContaining({
1355
+ gateId: 'claim_subject_consistency',
1356
+ passed: true,
1357
+ }),
1358
+ ]));
1359
+ });
1360
+ test('revises grounded answers when same-subject attribute claims conflict with cited support', () => {
1361
+ const point = makeKnowledgePoint({
1362
+ title: 'Water Glass',
1363
+ summary: 'Water glass has moderate thermal insulation.',
1364
+ evidenceSnippet: 'Water glass has moderate thermal insulation.',
1365
+ citation: {
1366
+ ...makeKnowledgePoint().citation,
1367
+ title: 'Water Glass',
1368
+ snippet: 'Water glass has moderate thermal insulation.',
1369
+ },
1370
+ citations: [
1371
+ {
1372
+ ...makeKnowledgePoint().citation,
1373
+ title: 'Water Glass',
1374
+ snippet: 'Water glass has moderate thermal insulation.',
1375
+ },
1376
+ ],
1377
+ });
1378
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
1379
+ message: 'What thermal insulation does water glass have?',
1380
+ draftAnswer: 'Water glass has high thermal insulation.',
1381
+ knowledgePoints: [point],
1382
+ citations: [point.citation],
1383
+ usedScope: scopedWaterglass,
1384
+ graphContext: makeGraphContext(),
1385
+ reviewedAt: '2026-06-19T02:20:00.000Z',
1386
+ });
1387
+ expect(review.decision).toBe('revise');
1388
+ expect(review.failedGateIds).toContain('claim_attribute_consistency');
1389
+ expect(review.publicAnswer).toBe('Water glass has moderate thermal insulation.');
1390
+ expect(review.gates).toEqual(expect.arrayContaining([
1391
+ expect.objectContaining({
1392
+ gateId: 'claim_attribute_consistency',
1393
+ passed: false,
1394
+ }),
1395
+ ]));
1396
+ });
1397
+ test('revises grounded answers when Chinese same-subject attribute claims conflict with cited support', () => {
1398
+ const point = makeKnowledgePoint({
1399
+ title: '水杯',
1400
+ summary: '水杯具有中等热绝缘性能。',
1401
+ evidenceSnippet: '水杯具有中等热绝缘性能。',
1402
+ citation: {
1403
+ ...makeKnowledgePoint().citation,
1404
+ title: '水杯',
1405
+ snippet: '水杯具有中等热绝缘性能。',
1406
+ },
1407
+ citations: [
1408
+ {
1409
+ ...makeKnowledgePoint().citation,
1410
+ title: '水杯',
1411
+ snippet: '水杯具有中等热绝缘性能。',
1412
+ },
1413
+ ],
1414
+ });
1415
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
1416
+ message: '水杯有什么热绝缘性能?',
1417
+ draftAnswer: '水杯具有高热绝缘性能。',
1418
+ knowledgePoints: [point],
1419
+ citations: [point.citation],
1420
+ usedScope: scopedWaterglass,
1421
+ graphContext: makeGraphContext(),
1422
+ reviewedAt: '2026-06-19T02:25:00.000Z',
1423
+ });
1424
+ expect(review.decision).toBe('revise');
1425
+ expect(review.failedGateIds).toContain('claim_attribute_consistency');
1426
+ expect(review.publicAnswer).toBe('水杯具有中等热绝缘性能。');
1427
+ expect(review.gates).toEqual(expect.arrayContaining([
1428
+ expect.objectContaining({
1429
+ gateId: 'claim_attribute_consistency',
1430
+ passed: false,
1431
+ }),
1432
+ ]));
1433
+ });
1434
+ test('does not raise an attribute conflict when the draft is a compatible refinement of the supported attribute', () => {
1435
+ const point = makeKnowledgePoint({
1436
+ title: 'Water Glass',
1437
+ summary: 'Water glass has a transparent glass wall.',
1438
+ evidenceSnippet: 'Water glass has a transparent glass wall.',
1439
+ citation: {
1440
+ ...makeKnowledgePoint().citation,
1441
+ title: 'Water Glass',
1442
+ snippet: 'Water glass has a transparent glass wall.',
1443
+ },
1444
+ citations: [
1445
+ {
1446
+ ...makeKnowledgePoint().citation,
1447
+ title: 'Water Glass',
1448
+ snippet: 'Water glass has a transparent glass wall.',
1449
+ },
1450
+ ],
1451
+ });
1452
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
1453
+ message: 'What wall does the water glass have?',
1454
+ draftAnswer: 'Water glass has a transparent wall.',
1455
+ knowledgePoints: [point],
1456
+ citations: [point.citation],
1457
+ usedScope: scopedWaterglass,
1458
+ graphContext: makeGraphContext(),
1459
+ reviewedAt: '2026-06-19T02:30:00.000Z',
1460
+ });
1461
+ expect(review.decision).toBe('release');
1462
+ expect(review.failedGateIds).not.toContain('claim_attribute_consistency');
1463
+ expect(review.gates).toEqual(expect.arrayContaining([
1464
+ expect.objectContaining({
1465
+ gateId: 'claim_attribute_consistency',
1466
+ passed: true,
1467
+ }),
1468
+ ]));
1469
+ });
1470
+ test('revises grounded answers when containment relations conflict with cited support', () => {
1471
+ const point = makeKnowledgePoint({
1472
+ title: 'Water Glass Contents',
1473
+ summary: 'Water glass contains water during the example setup.',
1474
+ evidenceSnippet: 'Water glass contains water during the example setup.',
1475
+ citation: {
1476
+ ...makeKnowledgePoint().citation,
1477
+ title: 'Water Glass Contents',
1478
+ snippet: 'Water glass contains water during the example setup.',
1479
+ },
1480
+ citations: [
1481
+ {
1482
+ ...makeKnowledgePoint().citation,
1483
+ title: 'Water Glass Contents',
1484
+ snippet: 'Water glass contains water during the example setup.',
1485
+ },
1486
+ ],
1487
+ });
1488
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
1489
+ message: 'what does the example water glass contain',
1490
+ draftAnswer: 'Water glass contains oil during the example setup.',
1491
+ knowledgePoints: [point],
1492
+ citations: [point.citation],
1493
+ usedScope: scopedWaterglass,
1494
+ graphContext: makeGraphContext(),
1495
+ reviewedAt: '2026-06-19T03:00:00.000Z',
1496
+ });
1497
+ expect(review.decision).toBe('revise');
1498
+ expect(review.failedGateIds).toContain('claim_containment_consistency');
1499
+ expect(review.publicAnswer).toBe('Water Glass Contents: Water glass contains water during the example setup.');
1500
+ expect(review.gates).toEqual(expect.arrayContaining([
1501
+ expect.objectContaining({
1502
+ gateId: 'claim_containment_consistency',
1503
+ passed: false,
1504
+ }),
1505
+ ]));
1506
+ });
1507
+ test('revises grounded answers when Chinese containment relations conflict with cited support', () => {
1508
+ const point = makeKnowledgePoint({
1509
+ title: '水杯内容物',
1510
+ summary: '水杯盛有清水。',
1511
+ evidenceSnippet: '水杯盛有清水。',
1512
+ citation: {
1513
+ ...makeKnowledgePoint().citation,
1514
+ title: '水杯内容物',
1515
+ snippet: '水杯盛有清水。',
1516
+ },
1517
+ citations: [
1518
+ {
1519
+ ...makeKnowledgePoint().citation,
1520
+ title: '水杯内容物',
1521
+ snippet: '水杯盛有清水。',
1522
+ },
1523
+ ],
1524
+ });
1525
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
1526
+ message: '水杯里装的是什么?',
1527
+ draftAnswer: '水杯盛有机油。',
1528
+ knowledgePoints: [point],
1529
+ citations: [point.citation],
1530
+ usedScope: scopedWaterglass,
1531
+ graphContext: makeGraphContext(),
1532
+ reviewedAt: '2026-06-19T03:05:00.000Z',
1533
+ });
1534
+ expect(review.decision).toBe('revise');
1535
+ expect(review.failedGateIds).toContain('claim_containment_consistency');
1536
+ expect(review.publicAnswer).toBe('水杯内容物: 水杯盛有清水。');
1537
+ expect(review.gates).toEqual(expect.arrayContaining([
1538
+ expect.objectContaining({
1539
+ gateId: 'claim_containment_consistency',
1540
+ passed: false,
1541
+ }),
1542
+ ]));
1543
+ });
1544
+ test('does not raise a containment conflict when the draft is a compatible refinement of the supported content', () => {
1545
+ const point = makeKnowledgePoint({
1546
+ title: 'Water Glass Contents',
1547
+ summary: 'Water glass contains water.',
1548
+ evidenceSnippet: 'Water glass contains water.',
1549
+ citation: {
1550
+ ...makeKnowledgePoint().citation,
1551
+ title: 'Water Glass Contents',
1552
+ snippet: 'Water glass contains water.',
1553
+ },
1554
+ citations: [
1555
+ {
1556
+ ...makeKnowledgePoint().citation,
1557
+ title: 'Water Glass Contents',
1558
+ snippet: 'Water glass contains water.',
1559
+ },
1560
+ ],
1561
+ });
1562
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
1563
+ message: 'what does water glass contain',
1564
+ draftAnswer: 'Water glass contains cold water.',
1565
+ knowledgePoints: [point],
1566
+ citations: [point.citation],
1567
+ usedScope: scopedWaterglass,
1568
+ graphContext: makeGraphContext(),
1569
+ reviewedAt: '2026-06-19T03:10:00.000Z',
1570
+ });
1571
+ expect(review.decision).toBe('release');
1572
+ expect(review.failedGateIds).not.toContain('claim_containment_consistency');
1573
+ expect(review.gates).toEqual(expect.arrayContaining([
1574
+ expect.objectContaining({
1575
+ gateId: 'claim_containment_consistency',
1576
+ passed: true,
1577
+ }),
1578
+ ]));
1579
+ });
1580
+ test('revises grounded answers when composition claims conflict with cited support', () => {
1581
+ const point = makeKnowledgePoint({
1582
+ title: 'Water Glass Composition',
1583
+ summary: 'Water glass is composed of water and a transparent glass cup.',
1584
+ evidenceSnippet: 'Water glass is composed of water and a transparent glass cup.',
1585
+ citation: {
1586
+ ...makeKnowledgePoint().citation,
1587
+ title: 'Water Glass Composition',
1588
+ snippet: 'Water glass is composed of water and a transparent glass cup.',
1589
+ },
1590
+ citations: [
1591
+ {
1592
+ ...makeKnowledgePoint().citation,
1593
+ title: 'Water Glass Composition',
1594
+ snippet: 'Water glass is composed of water and a transparent glass cup.',
1595
+ },
1596
+ ],
1597
+ });
1598
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
1599
+ message: 'what is water glass made of',
1600
+ draftAnswer: 'Water glass is composed of oil and a plastic cup.',
1601
+ knowledgePoints: [point],
1602
+ citations: [point.citation],
1603
+ usedScope: scopedWaterglass,
1604
+ graphContext: makeGraphContext(),
1605
+ reviewedAt: '2026-06-19T04:00:00.000Z',
1606
+ });
1607
+ expect(review.decision).toBe('revise');
1608
+ expect(review.failedGateIds).toContain('claim_composition_consistency');
1609
+ expect(review.publicAnswer).toBe('Water Glass Composition: Water glass is composed of water and a transparent glass cup.');
1610
+ expect(review.gates).toEqual(expect.arrayContaining([
1611
+ expect.objectContaining({
1612
+ gateId: 'claim_composition_consistency',
1613
+ passed: false,
1614
+ }),
1615
+ ]));
1616
+ });
1617
+ test('revises grounded answers when Chinese composition claims conflict with cited support', () => {
1618
+ const point = makeKnowledgePoint({
1619
+ title: '水杯组成',
1620
+ summary: '水杯由水和玻璃杯组成。',
1621
+ evidenceSnippet: '水杯由水和玻璃杯组成。',
1622
+ citation: {
1623
+ ...makeKnowledgePoint().citation,
1624
+ title: '水杯组成',
1625
+ snippet: '水杯由水和玻璃杯组成。',
1626
+ },
1627
+ citations: [
1628
+ {
1629
+ ...makeKnowledgePoint().citation,
1630
+ title: '水杯组成',
1631
+ snippet: '水杯由水和玻璃杯组成。',
1632
+ },
1633
+ ],
1634
+ });
1635
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
1636
+ message: '水杯由什么组成?',
1637
+ draftAnswer: '水杯由机油和塑料杯组成。',
1638
+ knowledgePoints: [point],
1639
+ citations: [point.citation],
1640
+ usedScope: scopedWaterglass,
1641
+ graphContext: makeGraphContext(),
1642
+ reviewedAt: '2026-06-19T04:05:00.000Z',
1643
+ });
1644
+ expect(review.decision).toBe('revise');
1645
+ expect(review.failedGateIds).toContain('claim_composition_consistency');
1646
+ expect(review.publicAnswer).toBe('水杯组成: 水杯由水和玻璃杯组成。');
1647
+ expect(review.gates).toEqual(expect.arrayContaining([
1648
+ expect.objectContaining({
1649
+ gateId: 'claim_composition_consistency',
1650
+ passed: false,
1651
+ }),
1652
+ ]));
1653
+ });
1654
+ test('does not raise a composition conflict when the draft keeps the supported components in a compatible order', () => {
1655
+ const point = makeKnowledgePoint({
1656
+ title: 'Water Glass Composition',
1657
+ summary: 'Water glass consists of water and a transparent glass cup.',
1658
+ evidenceSnippet: 'Water glass consists of water and a transparent glass cup.',
1659
+ citation: {
1660
+ ...makeKnowledgePoint().citation,
1661
+ title: 'Water Glass Composition',
1662
+ snippet: 'Water glass consists of water and a transparent glass cup.',
1663
+ },
1664
+ citations: [
1665
+ {
1666
+ ...makeKnowledgePoint().citation,
1667
+ title: 'Water Glass Composition',
1668
+ snippet: 'Water glass consists of water and a transparent glass cup.',
1669
+ },
1670
+ ],
1671
+ });
1672
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
1673
+ message: 'what is water glass composed of',
1674
+ draftAnswer: 'Water glass is composed of a glass cup and water.',
1675
+ knowledgePoints: [point],
1676
+ citations: [point.citation],
1677
+ usedScope: scopedWaterglass,
1678
+ graphContext: makeGraphContext(),
1679
+ reviewedAt: '2026-06-19T04:10:00.000Z',
1680
+ });
1681
+ expect(review.decision).toBe('release');
1682
+ expect(review.failedGateIds).not.toContain('claim_composition_consistency');
1683
+ expect(review.gates).toEqual(expect.arrayContaining([
1684
+ expect.objectContaining({
1685
+ gateId: 'claim_composition_consistency',
1686
+ passed: true,
1687
+ }),
1688
+ ]));
1689
+ });
1690
+ test('revises grounded answers when purpose claims conflict with cited support', () => {
1691
+ const point = makeKnowledgePoint({
1692
+ title: 'Water Glass Purpose',
1693
+ summary: 'Water glass is used for drinking water and serving cold water.',
1694
+ evidenceSnippet: 'Water glass is used for drinking water and serving cold water.',
1695
+ citation: {
1696
+ ...makeKnowledgePoint().citation,
1697
+ title: 'Water Glass Purpose',
1698
+ snippet: 'Water glass is used for drinking water and serving cold water.',
1699
+ },
1700
+ citations: [
1701
+ {
1702
+ ...makeKnowledgePoint().citation,
1703
+ title: 'Water Glass Purpose',
1704
+ snippet: 'Water glass is used for drinking water and serving cold water.',
1705
+ },
1706
+ ],
1707
+ });
1708
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
1709
+ message: 'what is water glass used for',
1710
+ draftAnswer: 'Water glass is used for storing motor oil.',
1711
+ knowledgePoints: [point],
1712
+ citations: [point.citation],
1713
+ usedScope: scopedWaterglass,
1714
+ graphContext: makeGraphContext(),
1715
+ reviewedAt: '2026-06-19T04:15:00.000Z',
1716
+ });
1717
+ expect(review.decision).toBe('revise');
1718
+ expect(review.failedGateIds).toContain('claim_purpose_consistency');
1719
+ expect(review.publicAnswer).toBe('Water Glass Purpose: Water glass is used for drinking water and serving cold water.');
1720
+ expect(review.gates).toEqual(expect.arrayContaining([
1721
+ expect.objectContaining({
1722
+ gateId: 'claim_purpose_consistency',
1723
+ passed: false,
1724
+ }),
1725
+ ]));
1726
+ });
1727
+ test('revises grounded answers when Chinese purpose claims conflict with cited support', () => {
1728
+ const point = makeKnowledgePoint({
1729
+ title: '水杯用途',
1730
+ summary: '水杯用于饮水和盛放冷水。',
1731
+ evidenceSnippet: '水杯用于饮水和盛放冷水。',
1732
+ citation: {
1733
+ ...makeKnowledgePoint().citation,
1734
+ title: '水杯用途',
1735
+ snippet: '水杯用于饮水和盛放冷水。',
1736
+ },
1737
+ citations: [
1738
+ {
1739
+ ...makeKnowledgePoint().citation,
1740
+ title: '水杯用途',
1741
+ snippet: '水杯用于饮水和盛放冷水。',
1742
+ },
1743
+ ],
1744
+ });
1745
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
1746
+ message: '水杯有什么用途?',
1747
+ draftAnswer: '水杯用于储存机油。',
1748
+ knowledgePoints: [point],
1749
+ citations: [point.citation],
1750
+ usedScope: scopedWaterglass,
1751
+ graphContext: makeGraphContext(),
1752
+ reviewedAt: '2026-06-19T04:20:00.000Z',
1753
+ });
1754
+ expect(review.decision).toBe('revise');
1755
+ expect(review.failedGateIds).toContain('claim_purpose_consistency');
1756
+ expect(review.publicAnswer).toBe('水杯用途: 水杯用于饮水和盛放冷水。');
1757
+ expect(review.gates).toEqual(expect.arrayContaining([
1758
+ expect.objectContaining({
1759
+ gateId: 'claim_purpose_consistency',
1760
+ passed: false,
1761
+ }),
1762
+ ]));
1763
+ });
1764
+ test('does not raise a purpose conflict when the draft keeps a supported purpose refinement', () => {
1765
+ const point = makeKnowledgePoint({
1766
+ title: 'Water Glass Purpose',
1767
+ summary: 'Water glass is used for drinking water and serving cold water.',
1768
+ evidenceSnippet: 'Water glass is used for drinking water and serving cold water.',
1769
+ citation: {
1770
+ ...makeKnowledgePoint().citation,
1771
+ title: 'Water Glass Purpose',
1772
+ snippet: 'Water glass is used for drinking water and serving cold water.',
1773
+ },
1774
+ citations: [
1775
+ {
1776
+ ...makeKnowledgePoint().citation,
1777
+ title: 'Water Glass Purpose',
1778
+ snippet: 'Water glass is used for drinking water and serving cold water.',
1779
+ },
1780
+ ],
1781
+ });
1782
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
1783
+ message: 'what is water glass used for',
1784
+ draftAnswer: 'Water glass is used for serving cold water.',
1785
+ knowledgePoints: [point],
1786
+ citations: [point.citation],
1787
+ usedScope: scopedWaterglass,
1788
+ graphContext: makeGraphContext(),
1789
+ reviewedAt: '2026-06-19T04:25:00.000Z',
1790
+ });
1791
+ expect(review.decision).toBe('release');
1792
+ expect(review.failedGateIds).not.toContain('claim_purpose_consistency');
1793
+ expect(review.gates).toEqual(expect.arrayContaining([
1794
+ expect.objectContaining({
1795
+ gateId: 'claim_purpose_consistency',
1796
+ passed: true,
1797
+ }),
1798
+ ]));
1799
+ });
1800
+ test('revises grounded answers when dependency claims conflict with cited support', () => {
1801
+ const point = makeKnowledgePoint({
1802
+ title: 'Response Validation Dependencies',
1803
+ summary: 'Response Validation depends on Baseline Measurement and Sensor Calibration.',
1804
+ evidenceSnippet: 'Response Validation depends on Baseline Measurement and Sensor Calibration.',
1805
+ citation: {
1806
+ ...makeKnowledgePoint().citation,
1807
+ title: 'Response Validation Dependencies',
1808
+ snippet: 'Response Validation depends on Baseline Measurement and Sensor Calibration.',
1809
+ },
1810
+ citations: [
1811
+ {
1812
+ ...makeKnowledgePoint().citation,
1813
+ title: 'Response Validation Dependencies',
1814
+ snippet: 'Response Validation depends on Baseline Measurement and Sensor Calibration.',
1815
+ },
1816
+ ],
1817
+ });
1818
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
1819
+ message: 'What does response validation depend on?',
1820
+ draftAnswer: 'Response Validation depends on Final Reporting.',
1821
+ knowledgePoints: [point],
1822
+ citations: [point.citation],
1823
+ usedScope: scopedWaterglass,
1824
+ graphContext: makeGraphContext(),
1825
+ reviewedAt: '2026-06-19T04:30:00.000Z',
1826
+ });
1827
+ expect(review.decision).toBe('revise');
1828
+ expect(review.failedGateIds).toContain('claim_dependency_consistency');
1829
+ expect(review.publicAnswer).toBe('Response Validation Dependencies: Response Validation depends on Baseline Measurement and Sensor Calibration.');
1830
+ expect(review.gates).toEqual(expect.arrayContaining([
1831
+ expect.objectContaining({
1832
+ gateId: 'claim_dependency_consistency',
1833
+ passed: false,
1834
+ }),
1835
+ ]));
1836
+ });
1837
+ test('revises grounded answers when Chinese dependency claims conflict with cited support', () => {
1838
+ const point = makeKnowledgePoint({
1839
+ title: '响应验证依赖项',
1840
+ summary: '响应验证依赖基线测量和传感器校准。',
1841
+ evidenceSnippet: '响应验证依赖基线测量和传感器校准。',
1842
+ citation: {
1843
+ ...makeKnowledgePoint().citation,
1844
+ title: '响应验证依赖项',
1845
+ snippet: '响应验证依赖基线测量和传感器校准。',
1846
+ },
1847
+ citations: [
1848
+ {
1849
+ ...makeKnowledgePoint().citation,
1850
+ title: '响应验证依赖项',
1851
+ snippet: '响应验证依赖基线测量和传感器校准。',
1852
+ },
1853
+ ],
1854
+ });
1855
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
1856
+ message: '响应验证依赖什么?',
1857
+ draftAnswer: '响应验证依赖最终报告。',
1858
+ knowledgePoints: [point],
1859
+ citations: [point.citation],
1860
+ usedScope: scopedWaterglass,
1861
+ graphContext: makeGraphContext(),
1862
+ reviewedAt: '2026-06-19T04:35:00.000Z',
1863
+ });
1864
+ expect(review.decision).toBe('revise');
1865
+ expect(review.failedGateIds).toContain('claim_dependency_consistency');
1866
+ expect(review.publicAnswer).toBe('响应验证依赖项: 响应验证依赖基线测量和传感器校准。');
1867
+ expect(review.gates).toEqual(expect.arrayContaining([
1868
+ expect.objectContaining({
1869
+ gateId: 'claim_dependency_consistency',
1870
+ passed: false,
1871
+ }),
1872
+ ]));
1873
+ });
1874
+ test('does not raise a dependency conflict when the draft keeps a supported prerequisite subset', () => {
1875
+ const point = makeKnowledgePoint({
1876
+ title: 'Response Validation Dependencies',
1877
+ summary: 'Response Validation depends on Baseline Measurement and Sensor Calibration.',
1878
+ evidenceSnippet: 'Response Validation depends on Baseline Measurement and Sensor Calibration.',
1879
+ citation: {
1880
+ ...makeKnowledgePoint().citation,
1881
+ title: 'Response Validation Dependencies',
1882
+ snippet: 'Response Validation depends on Baseline Measurement and Sensor Calibration.',
1883
+ },
1884
+ citations: [
1885
+ {
1886
+ ...makeKnowledgePoint().citation,
1887
+ title: 'Response Validation Dependencies',
1888
+ snippet: 'Response Validation depends on Baseline Measurement and Sensor Calibration.',
1889
+ },
1890
+ ],
1891
+ });
1892
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
1893
+ message: 'What is a prerequisite for response validation?',
1894
+ draftAnswer: 'Baseline Measurement is a prerequisite for Response Validation.',
1895
+ knowledgePoints: [point],
1896
+ citations: [point.citation],
1897
+ usedScope: scopedWaterglass,
1898
+ graphContext: makeGraphContext(),
1899
+ reviewedAt: '2026-06-19T04:40:00.000Z',
1900
+ });
1901
+ expect(review.decision).toBe('release');
1902
+ expect(review.failedGateIds).not.toContain('claim_dependency_consistency');
1903
+ expect(review.gates).toEqual(expect.arrayContaining([
1904
+ expect.objectContaining({
1905
+ gateId: 'claim_dependency_consistency',
1906
+ passed: true,
1907
+ }),
1908
+ ]));
1909
+ });
1910
+ test('revises grounded answers when location claims conflict with cited support', () => {
1911
+ const point = makeKnowledgePoint({
1912
+ title: 'Control Module',
1913
+ summary: 'Control module is located in the main chamber.',
1914
+ evidenceSnippet: 'Control module is located in the main chamber.',
1915
+ citation: {
1916
+ ...makeKnowledgePoint().citation,
1917
+ title: 'Control Module',
1918
+ snippet: 'Control module is located in the main chamber.',
1919
+ },
1920
+ citations: [
1921
+ {
1922
+ ...makeKnowledgePoint().citation,
1923
+ title: 'Control Module',
1924
+ snippet: 'Control module is located in the main chamber.',
1925
+ },
1926
+ ],
1927
+ });
1928
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
1929
+ message: 'Where is the control module located?',
1930
+ draftAnswer: 'Control module is located in the auxiliary chamber.',
1931
+ knowledgePoints: [point],
1932
+ citations: [point.citation],
1933
+ usedScope: scopedWaterglass,
1934
+ graphContext: makeGraphContext({
1935
+ anchorTitle: 'Control Module',
1936
+ }),
1937
+ reviewedAt: '2026-06-19T04:45:00.000Z',
1938
+ });
1939
+ expect(review.decision).toBe('revise');
1940
+ expect(review.failedGateIds).toContain('claim_location_consistency');
1941
+ expect(review.publicAnswer).toBe('Control module is located in the main chamber.');
1942
+ expect(review.gates).toEqual(expect.arrayContaining([
1943
+ expect.objectContaining({
1944
+ gateId: 'claim_location_consistency',
1945
+ passed: false,
1946
+ }),
1947
+ ]));
1948
+ });
1949
+ test('revises grounded answers when Chinese location claims conflict with cited support', () => {
1950
+ const point = makeKnowledgePoint({
1951
+ title: '控制模块',
1952
+ summary: '控制模块位于主舱室。',
1953
+ evidenceSnippet: '控制模块位于主舱室。',
1954
+ citation: {
1955
+ ...makeKnowledgePoint().citation,
1956
+ title: '控制模块',
1957
+ snippet: '控制模块位于主舱室。',
1958
+ },
1959
+ citations: [
1960
+ {
1961
+ ...makeKnowledgePoint().citation,
1962
+ title: '控制模块',
1963
+ snippet: '控制模块位于主舱室。',
1964
+ },
1965
+ ],
1966
+ });
1967
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
1968
+ message: '控制模块位于哪里?',
1969
+ draftAnswer: '控制模块位于辅助舱室。',
1970
+ knowledgePoints: [point],
1971
+ citations: [point.citation],
1972
+ usedScope: scopedWaterglass,
1973
+ graphContext: makeGraphContext({
1974
+ anchorTitle: '控制模块',
1975
+ }),
1976
+ reviewedAt: '2026-06-19T04:50:00.000Z',
1977
+ });
1978
+ expect(review.decision).toBe('revise');
1979
+ expect(review.failedGateIds).toContain('claim_location_consistency');
1980
+ expect(review.publicAnswer).toBe('控制模块位于主舱室。');
1981
+ });
1982
+ test('does not raise a location conflict when the draft keeps a supported broader location', () => {
1983
+ const point = makeKnowledgePoint({
1984
+ title: 'Control Module',
1985
+ summary: 'Control module is located in the eastern experimental hall.',
1986
+ evidenceSnippet: 'Control module is located in the eastern experimental hall.',
1987
+ citation: {
1988
+ ...makeKnowledgePoint().citation,
1989
+ title: 'Control Module',
1990
+ snippet: 'Control module is located in the eastern experimental hall.',
1991
+ },
1992
+ citations: [
1993
+ {
1994
+ ...makeKnowledgePoint().citation,
1995
+ title: 'Control Module',
1996
+ snippet: 'Control module is located in the eastern experimental hall.',
1997
+ },
1998
+ ],
1999
+ });
2000
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
2001
+ message: 'Where is the control module located?',
2002
+ draftAnswer: 'Control module is located in the experimental hall.',
2003
+ knowledgePoints: [point],
2004
+ citations: [point.citation],
2005
+ usedScope: scopedWaterglass,
2006
+ graphContext: makeGraphContext({
2007
+ anchorTitle: 'Control Module',
2008
+ }),
2009
+ reviewedAt: '2026-06-19T04:55:00.000Z',
2010
+ });
2011
+ expect(review.decision).toBe('release');
2012
+ expect(review.failedGateIds).not.toContain('claim_location_consistency');
2013
+ expect(review.gates).toEqual(expect.arrayContaining([
2014
+ expect.objectContaining({
2015
+ gateId: 'claim_location_consistency',
2016
+ passed: true,
2017
+ }),
2018
+ ]));
2019
+ });
2020
+ test('revises grounded answers when same-subject state conflicts with cited support', () => {
2021
+ const point = makeKnowledgePoint({
2022
+ title: 'Water Glass Thermodynamics',
2023
+ summary: 'Water glass is an open system during the example setup.',
2024
+ evidenceSnippet: 'Water glass is an open system during the example setup.',
2025
+ citation: {
2026
+ ...makeKnowledgePoint().citation,
2027
+ title: 'Water Glass Thermodynamics',
2028
+ snippet: 'Water glass is an open system during the example setup.',
2029
+ },
2030
+ citations: [
2031
+ {
2032
+ ...makeKnowledgePoint().citation,
2033
+ title: 'Water Glass Thermodynamics',
2034
+ snippet: 'Water glass is an open system during the example setup.',
2035
+ },
2036
+ ],
2037
+ });
2038
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
2039
+ message: 'Is the example water glass open or closed?',
2040
+ draftAnswer: 'Water glass is a closed system during the example setup.',
2041
+ knowledgePoints: [point],
2042
+ citations: [point.citation],
2043
+ usedScope: scopedWaterglass,
2044
+ graphContext: makeGraphContext(),
2045
+ reviewedAt: '2026-06-19T00:40:00.000Z',
2046
+ });
2047
+ expect(review.decision).toBe('revise');
2048
+ expect(review.failedGateIds).toContain('claim_state_consistency');
2049
+ expect(review.publicAnswer).toBe('Water Glass Thermodynamics: Water glass is an open system during the example setup.');
2050
+ expect(review.gates).toEqual(expect.arrayContaining([
2051
+ expect.objectContaining({
2052
+ gateId: 'claim_state_consistency',
2053
+ passed: false,
2054
+ }),
2055
+ ]));
2056
+ });
2057
+ test('does not raise a state conflict when the draft is a compatible refinement', () => {
2058
+ const point = makeKnowledgePoint({
2059
+ title: 'Water Glass',
2060
+ summary: 'Water glass is a transparent container.',
2061
+ evidenceSnippet: 'Water glass is a transparent container.',
2062
+ citation: {
2063
+ ...makeKnowledgePoint().citation,
2064
+ title: 'Water Glass',
2065
+ snippet: 'Water glass is a transparent container.',
2066
+ },
2067
+ citations: [
2068
+ {
2069
+ ...makeKnowledgePoint().citation,
2070
+ title: 'Water Glass',
2071
+ snippet: 'Water glass is a transparent container.',
2072
+ },
2073
+ ],
2074
+ });
2075
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
2076
+ message: 'what is water glass',
2077
+ draftAnswer: 'Water glass is a transparent container filled with water.',
2078
+ knowledgePoints: [point],
2079
+ citations: [point.citation],
2080
+ usedScope: scopedWaterglass,
2081
+ graphContext: makeGraphContext(),
2082
+ reviewedAt: '2026-06-19T00:50:00.000Z',
2083
+ });
2084
+ expect(review.decision).toBe('release');
2085
+ expect(review.failedGateIds).not.toContain('claim_state_consistency');
2086
+ expect(review.gates).toEqual(expect.arrayContaining([
2087
+ expect.objectContaining({
2088
+ gateId: 'claim_state_consistency',
2089
+ passed: true,
2090
+ }),
2091
+ ]));
2092
+ });
2093
+ test('revises grounded answers when draft polarity conflicts with cited support', () => {
2094
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
2095
+ message: 'what is water glass',
2096
+ draftAnswer: 'Water glass is not a transparent container filled with water.',
2097
+ knowledgePoints: [makeKnowledgePoint()],
2098
+ citations: [makeKnowledgePoint().citation],
2099
+ usedScope: scopedWaterglass,
2100
+ graphContext: makeGraphContext(),
2101
+ reviewedAt: '2026-06-19T01:00:00.000Z',
2102
+ });
2103
+ expect(review.decision).toBe('revise');
2104
+ expect(review.failedGateIds).toContain('claim_polarity_consistency');
2105
+ expect(review.publicAnswer).toBe('Water glass is a transparent container filled with water.');
2106
+ expect(review.gates).toEqual(expect.arrayContaining([
2107
+ expect.objectContaining({
2108
+ gateId: 'claim_polarity_consistency',
2109
+ passed: false,
2110
+ }),
2111
+ ]));
2112
+ });
2113
+ test('does not raise a polarity conflict when support includes an unrelated negative sentence', () => {
2114
+ const point = makeKnowledgePoint({
2115
+ summary: 'Water glass is not plastic. Water glass is a transparent container filled with water.',
2116
+ evidenceSnippet: 'Water glass is not plastic. Water glass is a transparent container filled with water.',
2117
+ citation: {
2118
+ ...makeKnowledgePoint().citation,
2119
+ snippet: 'Water glass is not plastic. Water glass is a transparent container filled with water.',
2120
+ },
2121
+ citations: [
2122
+ {
2123
+ ...makeKnowledgePoint().citation,
2124
+ snippet: 'Water glass is not plastic. Water glass is a transparent container filled with water.',
2125
+ },
2126
+ ],
2127
+ });
2128
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
2129
+ message: 'what is water glass',
2130
+ draftAnswer: 'Water glass is a transparent container filled with water.',
2131
+ knowledgePoints: [point],
2132
+ citations: [point.citation],
2133
+ usedScope: scopedWaterglass,
2134
+ graphContext: makeGraphContext(),
2135
+ reviewedAt: '2026-06-19T01:10:00.000Z',
2136
+ });
2137
+ expect(review.decision).toBe('release');
2138
+ expect(review.failedGateIds).not.toContain('claim_polarity_consistency');
2139
+ expect(review.gates).toEqual(expect.arrayContaining([
2140
+ expect.objectContaining({
2141
+ gateId: 'claim_polarity_consistency',
2142
+ passed: true,
2143
+ }),
2144
+ ]));
2145
+ });
2146
+ test('revises grounded answers when Chinese polarity conflicts with cited support', () => {
2147
+ const point = makeKnowledgePoint({
2148
+ title: '水杯',
2149
+ summary: '水杯是透明的盛水容器。',
2150
+ evidenceSnippet: '水杯是透明的盛水容器。',
2151
+ citation: {
2152
+ ...makeKnowledgePoint().citation,
2153
+ title: '水杯',
2154
+ snippet: '水杯是透明的盛水容器。',
2155
+ },
2156
+ citations: [
2157
+ {
2158
+ ...makeKnowledgePoint().citation,
2159
+ title: '水杯',
2160
+ snippet: '水杯是透明的盛水容器。',
2161
+ },
2162
+ ],
2163
+ });
2164
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
2165
+ message: '什么是水杯',
2166
+ draftAnswer: '水杯不是透明的盛水容器。',
2167
+ knowledgePoints: [point],
2168
+ citations: [point.citation],
2169
+ usedScope: scopedWaterglass,
2170
+ graphContext: makeGraphContext(),
2171
+ reviewedAt: '2026-06-19T01:20:00.000Z',
2172
+ });
2173
+ expect(review.decision).toBe('revise');
2174
+ expect(review.failedGateIds).toContain('claim_polarity_consistency');
2175
+ expect(review.publicAnswer).toBe('水杯是透明的盛水容器。');
2176
+ });
2177
+ test('revises grounded answers when Chinese same-subject state conflicts with cited support', () => {
2178
+ const point = makeKnowledgePoint({
2179
+ title: '水杯热力学',
2180
+ summary: '水杯是开放系统。',
2181
+ evidenceSnippet: '水杯是开放系统。',
2182
+ citation: {
2183
+ ...makeKnowledgePoint().citation,
2184
+ title: '水杯热力学',
2185
+ snippet: '水杯是开放系统。',
2186
+ },
2187
+ citations: [
2188
+ {
2189
+ ...makeKnowledgePoint().citation,
2190
+ title: '水杯热力学',
2191
+ snippet: '水杯是开放系统。',
2192
+ },
2193
+ ],
2194
+ });
2195
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
2196
+ message: '水杯是开放系统还是封闭系统?',
2197
+ draftAnswer: '水杯是封闭系统。',
2198
+ knowledgePoints: [point],
2199
+ citations: [point.citation],
2200
+ usedScope: scopedWaterglass,
2201
+ graphContext: makeGraphContext(),
2202
+ reviewedAt: '2026-06-19T01:25:00.000Z',
2203
+ });
2204
+ expect(review.decision).toBe('revise');
2205
+ expect(review.failedGateIds).toContain('claim_state_consistency');
2206
+ expect(review.publicAnswer).toBe('水杯热力学: 水杯是开放系统。');
2207
+ });
2208
+ test('revises grounded answers when prerequisite order is reversed against the DAG', () => {
2209
+ const point = makeKnowledgePoint({
2210
+ atomId: 'atom_ground_state',
2211
+ atomIds: ['atom_ground_state'],
2212
+ title: 'Ground State',
2213
+ summary: 'Ground State is stabilized after Bridge Layer is established.',
2214
+ evidenceSnippet: 'Ground State is stabilized after Bridge Layer is established.',
2215
+ citation: {
2216
+ ...makeKnowledgePoint().citation,
2217
+ atomId: 'atom_ground_state',
2218
+ title: 'Ground State',
2219
+ snippet: 'Ground State is stabilized after Bridge Layer is established.',
2220
+ },
2221
+ citations: [
2222
+ {
2223
+ ...makeKnowledgePoint().citation,
2224
+ atomId: 'atom_ground_state',
2225
+ title: 'Ground State',
2226
+ snippet: 'Ground State is stabilized after Bridge Layer is established.',
2227
+ },
2228
+ ],
2229
+ });
2230
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
2231
+ message: 'How are Bridge Layer and Ground State ordered?',
2232
+ draftAnswer: 'Ground State is a prerequisite for Bridge Layer.',
2233
+ knowledgePoints: [point],
2234
+ citations: [point.citation],
2235
+ usedScope: scopedWaterglass,
2236
+ graphContext: makeOrderedGraphContext(),
2237
+ reviewedAt: '2026-06-19T02:05:00.000Z',
2238
+ });
2239
+ expect(review.decision).toBe('revise');
2240
+ expect(review.failedGateIds).toContain('claim_graph_order_consistency');
2241
+ expect(review.publicAnswer).toBe('Bridge Layer is a prerequisite for Ground State.');
2242
+ expect(review.gates).toEqual(expect.arrayContaining([
2243
+ expect.objectContaining({
2244
+ gateId: 'claim_graph_order_consistency',
2245
+ passed: false,
2246
+ }),
2247
+ ]));
2248
+ });
2249
+ test('keeps grounded answers when prerequisite order matches the DAG', () => {
2250
+ const point = makeKnowledgePoint({
2251
+ atomId: 'atom_ground_state',
2252
+ atomIds: ['atom_ground_state'],
2253
+ title: 'Ground State',
2254
+ summary: 'Ground State is stabilized after Bridge Layer is established.',
2255
+ evidenceSnippet: 'Ground State is stabilized after Bridge Layer is established.',
2256
+ citation: {
2257
+ ...makeKnowledgePoint().citation,
2258
+ atomId: 'atom_ground_state',
2259
+ title: 'Ground State',
2260
+ snippet: 'Ground State is stabilized after Bridge Layer is established.',
2261
+ },
2262
+ citations: [
2263
+ {
2264
+ ...makeKnowledgePoint().citation,
2265
+ atomId: 'atom_ground_state',
2266
+ title: 'Ground State',
2267
+ snippet: 'Ground State is stabilized after Bridge Layer is established.',
2268
+ },
2269
+ ],
2270
+ });
2271
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
2272
+ message: 'How are Bridge Layer and Ground State ordered?',
2273
+ draftAnswer: 'Bridge Layer is a prerequisite for Ground State.',
2274
+ knowledgePoints: [point],
2275
+ citations: [point.citation],
2276
+ usedScope: scopedWaterglass,
2277
+ graphContext: makeOrderedGraphContext(),
2278
+ reviewedAt: '2026-06-19T02:10:00.000Z',
2279
+ });
2280
+ expect(review.decision).toBe('release');
2281
+ expect(review.failedGateIds).not.toContain('claim_graph_order_consistency');
2282
+ expect(review.publicAnswer).toBe('Bridge Layer is a prerequisite for Ground State.');
2283
+ expect(review.gates).toEqual(expect.arrayContaining([
2284
+ expect.objectContaining({
2285
+ gateId: 'claim_graph_order_consistency',
2286
+ passed: true,
2287
+ }),
2288
+ ]));
2289
+ });
2290
+ test('revises grounded answers when sequence order is reversed against the DAG', () => {
2291
+ const point = makeKnowledgePoint({
2292
+ atomId: 'atom_response_validation',
2293
+ atomIds: ['atom_response_validation'],
2294
+ title: 'Response Validation',
2295
+ summary: 'Response Validation runs after Baseline Measurement.',
2296
+ evidenceSnippet: 'Response Validation runs after Baseline Measurement.',
2297
+ citation: {
2298
+ ...makeKnowledgePoint().citation,
2299
+ atomId: 'atom_response_validation',
2300
+ title: 'Response Validation',
2301
+ snippet: 'Response Validation runs after Baseline Measurement.',
2302
+ },
2303
+ citations: [
2304
+ {
2305
+ ...makeKnowledgePoint().citation,
2306
+ atomId: 'atom_response_validation',
2307
+ title: 'Response Validation',
2308
+ snippet: 'Response Validation runs after Baseline Measurement.',
2309
+ },
2310
+ ],
2311
+ });
2312
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
2313
+ message: 'Which step comes first?',
2314
+ draftAnswer: 'Response Validation comes before Baseline Measurement.',
2315
+ knowledgePoints: [point],
2316
+ citations: [point.citation],
2317
+ usedScope: scopedWaterglass,
2318
+ graphContext: makeOrderedGraphContext({
2319
+ anchorAtomId: 'atom_response_validation',
2320
+ anchorTitle: 'Response Validation',
2321
+ supportingAtomIds: ['atom_baseline_measurement'],
2322
+ supportingTitles: ['Baseline Measurement'],
2323
+ relationKinds: ['sequence'],
2324
+ relationSummaries: [
2325
+ {
2326
+ relationKind: 'sequence',
2327
+ edgeIds: ['edge_baseline_to_validation'],
2328
+ sourceAtomIds: ['atom_baseline_measurement'],
2329
+ targetAtomIds: ['atom_response_validation'],
2330
+ averageConfidence: 0.91,
2331
+ },
2332
+ ],
2333
+ connectionPaths: [
2334
+ {
2335
+ sourceAtomId: 'atom_baseline_measurement',
2336
+ sourceTitle: 'Baseline Measurement',
2337
+ targetAtomId: 'atom_response_validation',
2338
+ targetTitle: 'Response Validation',
2339
+ pathAtomIds: ['atom_baseline_measurement', 'atom_response_validation'],
2340
+ pathTitles: ['Baseline Measurement', 'Response Validation'],
2341
+ pathEdges: [
2342
+ {
2343
+ fromAtomId: 'atom_baseline_measurement',
2344
+ toAtomId: 'atom_response_validation',
2345
+ relationKind: 'sequence',
2346
+ },
2347
+ ],
2348
+ length: 1,
2349
+ },
2350
+ ],
2351
+ predecessorWindow: [
2352
+ {
2353
+ atomId: 'atom_baseline_measurement',
2354
+ title: 'Baseline Measurement',
2355
+ relationKind: 'sequence',
2356
+ confidence: 0.91,
2357
+ },
2358
+ ],
2359
+ }),
2360
+ reviewedAt: '2026-06-19T02:20:00.000Z',
2361
+ });
2362
+ expect(review.decision).toBe('revise');
2363
+ expect(review.failedGateIds).toContain('claim_graph_order_consistency');
2364
+ expect(review.publicAnswer).toBe('Baseline Measurement comes before Response Validation.');
2365
+ });
2366
+ test('revises grounded answers when causal direction is reversed against the DAG', () => {
2367
+ const point = makeKnowledgePoint({
2368
+ atomId: 'atom_pressure_rise',
2369
+ atomIds: ['atom_pressure_rise'],
2370
+ title: 'Pressure Rise',
2371
+ summary: 'Pressure Rise follows thermal expansion in the sealed example.',
2372
+ evidenceSnippet: 'Pressure Rise follows thermal expansion in the sealed example.',
2373
+ citation: {
2374
+ ...makeKnowledgePoint().citation,
2375
+ atomId: 'atom_pressure_rise',
2376
+ title: 'Pressure Rise',
2377
+ snippet: 'Pressure Rise follows thermal expansion in the sealed example.',
2378
+ },
2379
+ citations: [
2380
+ {
2381
+ ...makeKnowledgePoint().citation,
2382
+ atomId: 'atom_pressure_rise',
2383
+ title: 'Pressure Rise',
2384
+ snippet: 'Pressure Rise follows thermal expansion in the sealed example.',
2385
+ },
2386
+ ],
2387
+ });
2388
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
2389
+ message: 'What causes the pressure rise?',
2390
+ draftAnswer: 'Pressure Rise causes Thermal Expansion.',
2391
+ knowledgePoints: [point],
2392
+ citations: [point.citation],
2393
+ usedScope: scopedWaterglass,
2394
+ graphContext: makeOrderedGraphContext({
2395
+ anchorAtomId: 'atom_pressure_rise',
2396
+ anchorTitle: 'Pressure Rise',
2397
+ supportingAtomIds: ['atom_thermal_expansion'],
2398
+ supportingTitles: ['Thermal Expansion'],
2399
+ relationKinds: ['causal'],
2400
+ relationSummaries: [
2401
+ {
2402
+ relationKind: 'causal',
2403
+ edgeIds: ['edge_expansion_to_pressure'],
2404
+ sourceAtomIds: ['atom_thermal_expansion'],
2405
+ targetAtomIds: ['atom_pressure_rise'],
2406
+ averageConfidence: 0.94,
2407
+ },
2408
+ ],
2409
+ connectionPaths: [
2410
+ {
2411
+ sourceAtomId: 'atom_thermal_expansion',
2412
+ sourceTitle: 'Thermal Expansion',
2413
+ targetAtomId: 'atom_pressure_rise',
2414
+ targetTitle: 'Pressure Rise',
2415
+ pathAtomIds: ['atom_thermal_expansion', 'atom_pressure_rise'],
2416
+ pathTitles: ['Thermal Expansion', 'Pressure Rise'],
2417
+ pathEdges: [
2418
+ {
2419
+ fromAtomId: 'atom_thermal_expansion',
2420
+ toAtomId: 'atom_pressure_rise',
2421
+ relationKind: 'causal',
2422
+ },
2423
+ ],
2424
+ length: 1,
2425
+ },
2426
+ ],
2427
+ predecessorWindow: [
2428
+ {
2429
+ atomId: 'atom_thermal_expansion',
2430
+ title: 'Thermal Expansion',
2431
+ relationKind: 'causal',
2432
+ confidence: 0.94,
2433
+ },
2434
+ ],
2435
+ }),
2436
+ reviewedAt: '2026-06-19T04:00:00.000Z',
2437
+ });
2438
+ expect(review.decision).toBe('revise');
2439
+ expect(review.failedGateIds).toContain('claim_graph_causal_consistency');
2440
+ expect(review.publicAnswer).toBe('Thermal Expansion causes Pressure Rise.');
2441
+ expect(review.gates).toEqual(expect.arrayContaining([
2442
+ expect.objectContaining({
2443
+ gateId: 'claim_graph_causal_consistency',
2444
+ passed: false,
2445
+ }),
2446
+ ]));
2447
+ });
2448
+ test('keeps grounded answers when causal direction matches the DAG', () => {
2449
+ const point = makeKnowledgePoint({
2450
+ atomId: 'atom_pressure_rise',
2451
+ atomIds: ['atom_pressure_rise'],
2452
+ title: 'Pressure Rise',
2453
+ summary: 'Pressure Rise follows thermal expansion in the sealed example.',
2454
+ evidenceSnippet: 'Pressure Rise follows thermal expansion in the sealed example.',
2455
+ citation: {
2456
+ ...makeKnowledgePoint().citation,
2457
+ atomId: 'atom_pressure_rise',
2458
+ title: 'Pressure Rise',
2459
+ snippet: 'Pressure Rise follows thermal expansion in the sealed example.',
2460
+ },
2461
+ citations: [
2462
+ {
2463
+ ...makeKnowledgePoint().citation,
2464
+ atomId: 'atom_pressure_rise',
2465
+ title: 'Pressure Rise',
2466
+ snippet: 'Pressure Rise follows thermal expansion in the sealed example.',
2467
+ },
2468
+ ],
2469
+ });
2470
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
2471
+ message: 'What causes the pressure rise?',
2472
+ draftAnswer: 'Thermal Expansion causes Pressure Rise.',
2473
+ knowledgePoints: [point],
2474
+ citations: [point.citation],
2475
+ usedScope: scopedWaterglass,
2476
+ graphContext: makeOrderedGraphContext({
2477
+ anchorAtomId: 'atom_pressure_rise',
2478
+ anchorTitle: 'Pressure Rise',
2479
+ supportingAtomIds: ['atom_thermal_expansion'],
2480
+ supportingTitles: ['Thermal Expansion'],
2481
+ relationKinds: ['causal'],
2482
+ relationSummaries: [
2483
+ {
2484
+ relationKind: 'causal',
2485
+ edgeIds: ['edge_expansion_to_pressure'],
2486
+ sourceAtomIds: ['atom_thermal_expansion'],
2487
+ targetAtomIds: ['atom_pressure_rise'],
2488
+ averageConfidence: 0.94,
2489
+ },
2490
+ ],
2491
+ connectionPaths: [
2492
+ {
2493
+ sourceAtomId: 'atom_thermal_expansion',
2494
+ sourceTitle: 'Thermal Expansion',
2495
+ targetAtomId: 'atom_pressure_rise',
2496
+ targetTitle: 'Pressure Rise',
2497
+ pathAtomIds: ['atom_thermal_expansion', 'atom_pressure_rise'],
2498
+ pathTitles: ['Thermal Expansion', 'Pressure Rise'],
2499
+ pathEdges: [
2500
+ {
2501
+ fromAtomId: 'atom_thermal_expansion',
2502
+ toAtomId: 'atom_pressure_rise',
2503
+ relationKind: 'causal',
2504
+ },
2505
+ ],
2506
+ length: 1,
2507
+ },
2508
+ ],
2509
+ predecessorWindow: [
2510
+ {
2511
+ atomId: 'atom_thermal_expansion',
2512
+ title: 'Thermal Expansion',
2513
+ relationKind: 'causal',
2514
+ confidence: 0.94,
2515
+ },
2516
+ ],
2517
+ }),
2518
+ reviewedAt: '2026-06-19T04:05:00.000Z',
2519
+ });
2520
+ expect(review.decision).toBe('release');
2521
+ expect(review.failedGateIds).not.toContain('claim_graph_causal_consistency');
2522
+ expect(review.publicAnswer).toBe('Thermal Expansion causes Pressure Rise.');
2523
+ expect(review.gates).toEqual(expect.arrayContaining([
2524
+ expect.objectContaining({
2525
+ gateId: 'claim_graph_causal_consistency',
2526
+ passed: true,
2527
+ }),
2528
+ ]));
2529
+ });
2530
+ test('revises grounded answers when Chinese causal direction is reversed against the DAG', () => {
2531
+ const point = makeKnowledgePoint({
2532
+ atomId: 'atom_pressure_rise_zh',
2533
+ atomIds: ['atom_pressure_rise_zh'],
2534
+ title: '压力升高',
2535
+ summary: '密闭示例中,热膨胀会导致压力升高。',
2536
+ evidenceSnippet: '密闭示例中,热膨胀会导致压力升高。',
2537
+ citation: {
2538
+ ...makeKnowledgePoint().citation,
2539
+ atomId: 'atom_pressure_rise_zh',
2540
+ title: '压力升高',
2541
+ snippet: '密闭示例中,热膨胀会导致压力升高。',
2542
+ },
2543
+ citations: [
2544
+ {
2545
+ ...makeKnowledgePoint().citation,
2546
+ atomId: 'atom_pressure_rise_zh',
2547
+ title: '压力升高',
2548
+ snippet: '密闭示例中,热膨胀会导致压力升高。',
2549
+ },
2550
+ ],
2551
+ });
2552
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
2553
+ message: '是什么导致压力升高?',
2554
+ draftAnswer: '压力升高导致热膨胀。',
2555
+ knowledgePoints: [point],
2556
+ citations: [point.citation],
2557
+ usedScope: scopedWaterglass,
2558
+ graphContext: makeOrderedGraphContext({
2559
+ anchorAtomId: 'atom_pressure_rise_zh',
2560
+ anchorTitle: '压力升高',
2561
+ supportingAtomIds: ['atom_thermal_expansion_zh'],
2562
+ supportingTitles: ['热膨胀'],
2563
+ relationKinds: ['causal'],
2564
+ relationSummaries: [
2565
+ {
2566
+ relationKind: 'causal',
2567
+ edgeIds: ['edge_expansion_to_pressure_zh'],
2568
+ sourceAtomIds: ['atom_thermal_expansion_zh'],
2569
+ targetAtomIds: ['atom_pressure_rise_zh'],
2570
+ averageConfidence: 0.94,
2571
+ },
2572
+ ],
2573
+ connectionPaths: [
2574
+ {
2575
+ sourceAtomId: 'atom_thermal_expansion_zh',
2576
+ sourceTitle: '热膨胀',
2577
+ targetAtomId: 'atom_pressure_rise_zh',
2578
+ targetTitle: '压力升高',
2579
+ pathAtomIds: ['atom_thermal_expansion_zh', 'atom_pressure_rise_zh'],
2580
+ pathTitles: ['热膨胀', '压力升高'],
2581
+ pathEdges: [
2582
+ {
2583
+ fromAtomId: 'atom_thermal_expansion_zh',
2584
+ toAtomId: 'atom_pressure_rise_zh',
2585
+ relationKind: 'causal',
2586
+ },
2587
+ ],
2588
+ length: 1,
2589
+ },
2590
+ ],
2591
+ predecessorWindow: [
2592
+ {
2593
+ atomId: 'atom_thermal_expansion_zh',
2594
+ title: '热膨胀',
2595
+ relationKind: 'causal',
2596
+ confidence: 0.94,
2597
+ },
2598
+ ],
2599
+ }),
2600
+ reviewedAt: '2026-06-19T04:10:00.000Z',
2601
+ });
2602
+ expect(review.decision).toBe('revise');
2603
+ expect(review.failedGateIds).toContain('claim_graph_causal_consistency');
2604
+ expect(review.publicAnswer).toBe('热膨胀导致压力升高。');
2605
+ });
2606
+ test('revises grounded answers when temporally flagged evidence is released as a current answer', () => {
2607
+ const point = makeKnowledgePoint({
2608
+ temporalValidity: {
2609
+ isValid: false,
2610
+ checkedAt: '2026-06-19T05:20:00.000Z',
2611
+ reasons: ['temporal_edge_expired'],
2612
+ details: [],
2613
+ },
2614
+ });
2615
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
2616
+ message: 'what is water glass',
2617
+ draftAnswer: 'Water glass is a transparent container filled with water.',
2618
+ knowledgePoints: [point],
2619
+ citations: [point.citation],
2620
+ usedScope: scopedWaterglass,
2621
+ graphContext: makeGraphContext({
2622
+ temporalValidity: {
2623
+ checkedAt: '2026-06-19T05:20:00.000Z',
2624
+ allPointsValid: false,
2625
+ warningReasons: ['temporal_edge_expired'],
2626
+ invalidKnowledgePointTitles: ['Water Glass'],
2627
+ edgeKinds: ['validity_window'],
2628
+ details: [],
2629
+ },
2630
+ }),
2631
+ reviewedAt: '2026-06-19T05:20:00.000Z',
2632
+ });
2633
+ expect(review.decision).toBe('revise');
2634
+ expect(review.failedGateIds).toContain('claim_temporal_validity_consistency');
2635
+ expect(review.publicAnswer).toBe('The retrieved evidence for Water Glass carries temporal warnings, so I cannot safely present it as the current answer.');
2636
+ expect(review.gates).toEqual(expect.arrayContaining([
2637
+ expect.objectContaining({
2638
+ gateId: 'claim_temporal_validity_consistency',
2639
+ passed: false,
2640
+ }),
2641
+ ]));
2642
+ });
2643
+ test('keeps grounded answers when temporally flagged evidence is explicitly time-qualified in the public answer', () => {
2644
+ const point = makeKnowledgePoint({
2645
+ summary: 'As of 2024, Water Glass was a transparent container filled with water.',
2646
+ evidenceSnippet: 'As of 2024, Water Glass was a transparent container filled with water.',
2647
+ citation: {
2648
+ ...makeKnowledgePoint().citation,
2649
+ snippet: 'As of 2024, Water Glass was a transparent container filled with water.',
2650
+ },
2651
+ citations: [
2652
+ {
2653
+ ...makeKnowledgePoint().citation,
2654
+ snippet: 'As of 2024, Water Glass was a transparent container filled with water.',
2655
+ },
2656
+ ],
2657
+ temporalValidity: {
2658
+ isValid: false,
2659
+ checkedAt: '2026-06-19T05:25:00.000Z',
2660
+ reasons: ['temporal_edge_expired'],
2661
+ details: [],
2662
+ },
2663
+ });
2664
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
2665
+ message: 'what did water glass mean historically',
2666
+ draftAnswer: 'As of 2024, Water Glass was a transparent container filled with water.',
2667
+ knowledgePoints: [point],
2668
+ citations: [point.citation],
2669
+ usedScope: scopedWaterglass,
2670
+ graphContext: makeGraphContext({
2671
+ temporalValidity: {
2672
+ checkedAt: '2026-06-19T05:25:00.000Z',
2673
+ allPointsValid: false,
2674
+ warningReasons: ['temporal_edge_expired'],
2675
+ invalidKnowledgePointTitles: ['Water Glass'],
2676
+ edgeKinds: ['validity_window'],
2677
+ details: [],
2678
+ },
2679
+ }),
2680
+ reviewedAt: '2026-06-19T05:25:00.000Z',
2681
+ });
2682
+ expect(review.decision).toBe('release');
2683
+ expect(review.failedGateIds).not.toContain('claim_temporal_validity_consistency');
2684
+ expect(review.publicAnswer).toBe('As of 2024, Water Glass was a transparent container filled with water.');
2685
+ expect(review.gates).toEqual(expect.arrayContaining([
2686
+ expect.objectContaining({
2687
+ gateId: 'claim_temporal_validity_consistency',
2688
+ passed: true,
2689
+ }),
2690
+ ]));
2691
+ });
2692
+ test('revises grounded answers when Chinese temporally flagged evidence is released as a current answer', () => {
2693
+ const point = makeKnowledgePoint({
2694
+ atomId: 'atom_water_glass_zh',
2695
+ atomIds: ['atom_water_glass_zh'],
2696
+ title: '水杯',
2697
+ summary: '水杯是一个装有水的透明容器。',
2698
+ evidenceSnippet: '水杯是一个装有水的透明容器。',
2699
+ citation: {
2700
+ ...makeKnowledgePoint().citation,
2701
+ atomId: 'atom_water_glass_zh',
2702
+ title: '水杯',
2703
+ snippet: '水杯是一个装有水的透明容器。',
2704
+ },
2705
+ citations: [
2706
+ {
2707
+ ...makeKnowledgePoint().citation,
2708
+ atomId: 'atom_water_glass_zh',
2709
+ title: '水杯',
2710
+ snippet: '水杯是一个装有水的透明容器。',
2711
+ },
2712
+ ],
2713
+ temporalValidity: {
2714
+ isValid: false,
2715
+ checkedAt: '2026-06-19T05:30:00.000Z',
2716
+ reasons: ['temporal_edge_expired'],
2717
+ details: [],
2718
+ },
2719
+ });
2720
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
2721
+ message: '什么是水杯?',
2722
+ draftAnswer: '水杯是一个装有水的透明容器。',
2723
+ knowledgePoints: [point],
2724
+ citations: [point.citation],
2725
+ usedScope: scopedWaterglass,
2726
+ graphContext: makeGraphContext({
2727
+ anchorAtomId: 'atom_water_glass_zh',
2728
+ anchorTitle: '水杯',
2729
+ temporalValidity: {
2730
+ checkedAt: '2026-06-19T05:30:00.000Z',
2731
+ allPointsValid: false,
2732
+ warningReasons: ['temporal_edge_expired'],
2733
+ invalidKnowledgePointTitles: ['水杯'],
2734
+ edgeKinds: ['validity_window'],
2735
+ details: [],
2736
+ },
2737
+ }),
2738
+ reviewedAt: '2026-06-19T05:30:00.000Z',
2739
+ });
2740
+ expect(review.decision).toBe('revise');
2741
+ expect(review.failedGateIds).toContain('claim_temporal_validity_consistency');
2742
+ expect(review.publicAnswer).toBe('关于水杯的当前命中证据带有时序警告,我不能把它直接当作当前结论发布。');
2743
+ });
2744
+ test('keeps grounded answers when the graph only records superseded lineage but the current anchor is still temporally valid', () => {
2745
+ const point = makeKnowledgePoint();
2746
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
2747
+ message: 'what is water glass',
2748
+ draftAnswer: 'Water glass is a transparent container filled with water.',
2749
+ knowledgePoints: [point],
2750
+ citations: [point.citation],
2751
+ usedScope: scopedWaterglass,
2752
+ graphContext: makeGraphContext({
2753
+ temporalValidity: {
2754
+ checkedAt: '2026-06-19T05:35:00.000Z',
2755
+ allPointsValid: true,
2756
+ warningReasons: [],
2757
+ invalidKnowledgePointTitles: [],
2758
+ edgeKinds: ['supersedes'],
2759
+ details: [
2760
+ {
2761
+ edgeId: 'temporal_support_supersedes',
2762
+ edgeKind: 'supersedes',
2763
+ sourceAtomId: 'atom_water_glass',
2764
+ targetAtomId: 'atom_water_glass_v0',
2765
+ validFrom: '2024-01-01T00:00:00.000Z',
2766
+ validTo: '2025-01-01T00:00:00.000Z',
2767
+ isActive: true,
2768
+ },
2769
+ ],
2770
+ },
2771
+ }),
2772
+ reviewedAt: '2026-06-19T05:35:00.000Z',
2773
+ });
2774
+ expect(review.decision).toBe('release');
2775
+ expect(review.failedGateIds).not.toContain('claim_temporal_validity_consistency');
2776
+ expect(review.gates).toEqual(expect.arrayContaining([
2777
+ expect.objectContaining({
2778
+ gateId: 'claim_temporal_validity_consistency',
2779
+ passed: true,
2780
+ }),
2781
+ ]));
2782
+ });
2783
+ test('revises grounded answers when a DAG contrast pair is incorrectly released as an analogy claim', () => {
2784
+ const point = makeKnowledgePoint({
2785
+ title: 'Plastic Cup',
2786
+ summary: 'Plastic Cup contrasts with Metal Cup in the current comparison context.',
2787
+ evidenceSnippet: 'Plastic Cup contrasts with Metal Cup in the current comparison context.',
2788
+ citation: {
2789
+ ...makeKnowledgePoint().citation,
2790
+ title: 'Plastic Cup',
2791
+ snippet: 'Plastic Cup contrasts with Metal Cup in the current comparison context.',
2792
+ },
2793
+ citations: [
2794
+ {
2795
+ ...makeKnowledgePoint().citation,
2796
+ title: 'Plastic Cup',
2797
+ snippet: 'Plastic Cup contrasts with Metal Cup in the current comparison context.',
2798
+ },
2799
+ ],
2800
+ });
2801
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
2802
+ message: 'compare plastic cup and metal cup',
2803
+ draftAnswer: 'Plastic Cup is similar to Metal Cup.',
2804
+ knowledgePoints: [point],
2805
+ citations: [point.citation],
2806
+ usedScope: scopedWaterglass,
2807
+ graphContext: makeComparisonGraphContext('contrast', 'Plastic Cup', 'Metal Cup'),
2808
+ reviewedAt: '2026-06-19T03:45:00.000Z',
2809
+ });
2810
+ expect(review.decision).toBe('revise');
2811
+ expect(review.failedGateIds).toContain('claim_graph_comparison_consistency');
2812
+ expect(review.publicAnswer).toBe('Plastic Cup contrasts with Metal Cup.');
2813
+ expect(review.gates).toEqual(expect.arrayContaining([
2814
+ expect.objectContaining({
2815
+ gateId: 'claim_graph_comparison_consistency',
2816
+ passed: false,
2817
+ }),
2818
+ ]));
2819
+ });
2820
+ test('keeps grounded answers when the DAG contrast claim already matches the public answer', () => {
2821
+ const point = makeKnowledgePoint({
2822
+ title: 'Plastic Cup',
2823
+ summary: 'Plastic Cup contrasts with Metal Cup in the current comparison context.',
2824
+ evidenceSnippet: 'Plastic Cup contrasts with Metal Cup in the current comparison context.',
2825
+ citation: {
2826
+ ...makeKnowledgePoint().citation,
2827
+ title: 'Plastic Cup',
2828
+ snippet: 'Plastic Cup contrasts with Metal Cup in the current comparison context.',
2829
+ },
2830
+ citations: [
2831
+ {
2832
+ ...makeKnowledgePoint().citation,
2833
+ title: 'Plastic Cup',
2834
+ snippet: 'Plastic Cup contrasts with Metal Cup in the current comparison context.',
2835
+ },
2836
+ ],
2837
+ });
2838
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
2839
+ message: 'compare plastic cup and metal cup',
2840
+ draftAnswer: 'Plastic Cup contrasts with Metal Cup.',
2841
+ knowledgePoints: [point],
2842
+ citations: [point.citation],
2843
+ usedScope: scopedWaterglass,
2844
+ graphContext: makeComparisonGraphContext('contrast', 'Plastic Cup', 'Metal Cup'),
2845
+ reviewedAt: '2026-06-19T03:50:00.000Z',
2846
+ });
2847
+ expect(review.decision).toBe('release');
2848
+ expect(review.failedGateIds).not.toContain('claim_graph_comparison_consistency');
2849
+ expect(review.publicAnswer).toBe('Plastic Cup contrasts with Metal Cup.');
2850
+ expect(review.gates).toEqual(expect.arrayContaining([
2851
+ expect.objectContaining({
2852
+ gateId: 'claim_graph_comparison_consistency',
2853
+ passed: true,
2854
+ }),
2855
+ ]));
2856
+ });
2857
+ test('revises grounded answers when a DAG analogy pair is incorrectly released as a Chinese contrast claim', () => {
2858
+ const point = makeKnowledgePoint({
2859
+ title: '层流',
2860
+ summary: '层流与电路电流类似。',
2861
+ evidenceSnippet: '层流与电路电流类似。',
2862
+ citation: {
2863
+ ...makeKnowledgePoint().citation,
2864
+ title: '层流',
2865
+ snippet: '层流与电路电流类似。',
2866
+ },
2867
+ citations: [
2868
+ {
2869
+ ...makeKnowledgePoint().citation,
2870
+ title: '层流',
2871
+ snippet: '层流与电路电流类似。',
2872
+ },
2873
+ ],
2874
+ });
2875
+ const review = (0, answerReleaseReview_1.reviewAnswerRelease)({
2876
+ message: '对比层流和电路电流',
2877
+ draftAnswer: '层流与电路电流不同。',
2878
+ knowledgePoints: [point],
2879
+ citations: [point.citation],
2880
+ usedScope: scopedWaterglass,
2881
+ graphContext: makeComparisonGraphContext('analogy', '层流', '电路电流'),
2882
+ reviewedAt: '2026-06-19T03:55:00.000Z',
2883
+ });
2884
+ expect(review.decision).toBe('revise');
2885
+ expect(review.failedGateIds).toContain('claim_graph_comparison_consistency');
2886
+ expect(review.publicAnswer).toBe('层流与电路电流类似。');
2887
+ });
2888
+ });