noteconnection 1.8.0 → 1.9.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 (195) hide show
  1. package/README.md +257 -1803
  2. package/dist/src/agent_workspace.contract.parity.test.js +105 -421
  3. package/dist/src/agent_workspace.frontend.test.js +410 -11
  4. package/dist/src/android.knowledgebase.picker.contract.test.js +102 -0
  5. package/dist/src/appimage.patchelf.test.js +113 -0
  6. package/dist/src/appimage.portability.test.js +156 -0
  7. package/dist/src/backend/FileLoader.js +18 -4
  8. package/dist/src/backend/FileLoader.test.js +125 -0
  9. package/dist/src/backend/GraphBuilder.identity.test.js +86 -0
  10. package/dist/src/backend/GraphBuilder.js +66 -18
  11. package/dist/src/backend/GraphBuilder.matching.test.js +127 -0
  12. package/dist/src/backend/GraphMetrics.js +0 -3
  13. package/dist/src/backend/ResourceIdentity.js +155 -0
  14. package/dist/src/backend/ResourceIdentity.test.js +113 -0
  15. package/dist/src/backend/algorithms/CycleDetection.js +0 -1
  16. package/dist/src/backend/algorithms/HybridEngine.js +0 -3
  17. package/dist/src/backend/algorithms/LayoutEngine.js +0 -1
  18. package/dist/src/backend/algorithms/StatisticalAnalyzer.js +0 -4
  19. package/dist/src/backend/algorithms/TopologicalSort.js +0 -2
  20. package/dist/src/backend/build_dag.js +0 -1
  21. package/dist/src/capacitor.bridge.serialization.contract.test.js +3 -0
  22. package/dist/src/capacitor.runtime.contract.test.js +11 -2
  23. package/dist/src/core/Graph.js +138 -42
  24. package/dist/src/core/Graph.test.js +74 -0
  25. package/dist/src/core/NoteConnection.js +2 -2
  26. package/dist/src/core/PathBridge.js +265 -2
  27. package/dist/src/core/ResourceReference.js +17 -0
  28. package/dist/src/desktop.godot.pack.test.js +142 -0
  29. package/dist/src/desktop.runtime.graph.test.js +66 -0
  30. package/dist/src/export/WorkspaceExportBundle.js +44 -0
  31. package/dist/src/export/WorkspaceExportBundle.test.js +61 -0
  32. package/dist/src/foundation.release.evidence.contract.test.js +127 -9
  33. package/dist/src/foundation.sqlite.runtime.contract.test.js +6 -0
  34. package/dist/src/frontend/README.md +257 -1803
  35. package/dist/src/frontend/agent_workspace.js +665 -7
  36. package/dist/src/frontend/index.html +31 -2
  37. package/dist/src/frontend/knowledge_projection_contract.js +230 -0
  38. package/dist/src/frontend/knowledge_projection_store.js +214 -0
  39. package/dist/src/frontend/libs/path_core.js +158 -42
  40. package/dist/src/frontend/locales/en.json +32 -0
  41. package/dist/src/frontend/locales/zh.json +32 -0
  42. package/dist/src/frontend/mobile_budget_runtime.js +33 -0
  43. package/dist/src/frontend/mobile_exact_analyzer.js +393 -0
  44. package/dist/src/frontend/mobile_identity_contract.js +221 -0
  45. package/dist/src/frontend/mobile_semantic_comparator.js +264 -0
  46. package/dist/src/frontend/path.html +6 -2
  47. package/dist/src/frontend/simulationWorker.js +1 -1
  48. package/dist/src/frontend/source_manager.js +110 -4
  49. package/dist/src/frontend/storage_provider.js +661 -71
  50. package/dist/src/frontend/styles.css +107 -13
  51. package/dist/src/frontend/workspace_panes.js +111 -7
  52. package/dist/src/frontend.runtime.assets.contract.test.js +81 -0
  53. package/dist/src/graph.answer.operator.contract.test.js +72 -0
  54. package/dist/src/index.js +6 -2
  55. package/dist/src/index.knowledge-root.test.js +118 -0
  56. package/dist/src/indexing/IndexLifecycle.js +22 -0
  57. package/dist/src/indexing/IndexLifecycle.test.js +24 -0
  58. package/dist/src/knowledge.api.contract.test.js +4 -2
  59. package/dist/src/knowledge_projection.contract.test.js +134 -0
  60. package/dist/src/knowledge_projection_store.test.js +200 -0
  61. package/dist/src/learning/AnswerQualityEvaluation.js +153 -0
  62. package/dist/src/learning/AnswerQualityEvaluation.test.js +118 -0
  63. package/dist/src/learning/KnowledgeLearningPlatform.js +4687 -3892
  64. package/dist/src/learning/KnowledgeLearningPlatform.persistence.test.js +418 -0
  65. package/dist/src/learning/KnowledgeLearningPlatform.test.js +243 -6
  66. package/dist/src/learning/KnowledgeWorkspaceConversationRegression.js +25 -1
  67. package/dist/src/learning/KnowledgeWorkspaceConversationRegression.test.js +13 -5
  68. package/dist/src/learning/MeasurementEvidenceConflicts.test.js +78 -0
  69. package/dist/src/learning/PublicAnswerContract.test.js +85 -0
  70. package/dist/src/learning/PublicEvidenceSelection.test.js +94 -0
  71. package/dist/src/learning/PublicMeasurementDisclosure.test.js +54 -0
  72. package/dist/src/learning/agentConversationExecution.js +211 -0
  73. package/dist/src/learning/agentConversationExecution.test.js +209 -0
  74. package/dist/src/learning/agentConversationSerialization.js +266 -0
  75. package/dist/src/learning/agentConversationSerialization.test.js +165 -0
  76. package/dist/src/learning/agentResponseBudget.js +179 -0
  77. package/dist/src/learning/agentResponseBudget.test.js +97 -0
  78. package/dist/src/learning/agentWorkspaceCapabilityContracts.js +65 -0
  79. package/dist/src/learning/answerReleaseReview.js +1191 -250
  80. package/dist/src/learning/answerReleaseReview.test.js +1293 -7
  81. package/dist/src/learning/answerTaskPlan.js +473 -0
  82. package/dist/src/learning/conversationComposer.js +627 -386
  83. package/dist/src/learning/conversationComposer.test.js +1403 -0
  84. package/dist/src/learning/evidenceContextAssembler.js +312 -120
  85. package/dist/src/learning/graphAnswerCoverage.js +37 -0
  86. package/dist/src/learning/graphAnswerCoverage.test.js +93 -0
  87. package/dist/src/learning/graphAnswerCoverageCalibration.js +59 -0
  88. package/dist/src/learning/graphAnswerCoverageCalibration.test.js +46 -0
  89. package/dist/src/learning/graphAnswerFacts.js +94 -0
  90. package/dist/src/learning/graphAnswerFacts.test.js +39 -0
  91. package/dist/src/learning/graphAnswerPlan.js +908 -0
  92. package/dist/src/learning/graphAnswerPlan.test.js +907 -0
  93. package/dist/src/learning/graphAnswerQualityCalibration.js +255 -0
  94. package/dist/src/learning/graphAnswerQualityCalibration.test.js +72 -0
  95. package/dist/src/learning/graphAnswerQualityPolicy.js +30 -0
  96. package/dist/src/learning/graphClaimMatcher.js +104 -0
  97. package/dist/src/learning/graphConditionedContext.js +109 -0
  98. package/dist/src/learning/graphConditionedContext.test.js +99 -0
  99. package/dist/src/learning/graphContextAssembler.js +3 -13
  100. package/dist/src/learning/graphContextAssembler.test.js +2 -2
  101. package/dist/src/learning/graphExpansionPolicy.js +22 -0
  102. package/dist/src/learning/graphExpansionPolicy.test.js +33 -0
  103. package/dist/src/learning/identityMetadata.test.js +26 -0
  104. package/dist/src/learning/index.js +1 -0
  105. package/dist/src/learning/mobileAnswerProjection.js +104 -0
  106. package/dist/src/learning/mobileAnswerProjection.test.js +102 -0
  107. package/dist/src/learning/queryBackend.js +34 -10
  108. package/dist/src/learning/queryBackend.test.js +21 -0
  109. package/dist/src/learning/ragContextPack.js +115 -16
  110. package/dist/src/learning/ragContextPack.test.js +151 -0
  111. package/dist/src/learning/ragEvidenceQuality.js +96 -0
  112. package/dist/src/learning/ragEvidenceQuality.test.js +29 -0
  113. package/dist/src/learning/ragPublicText.js +67 -4
  114. package/dist/src/learning/ragPublicText.test.js +57 -0
  115. package/dist/src/learning/ragSufficiencyJudge.js +11 -1
  116. package/dist/src/learning/ragSufficiencyProviderJudge.js +16 -26
  117. package/dist/src/learning/ragSufficiencyProviderJudge.test.js +51 -0
  118. package/dist/src/learning/requestNormalization.js +106 -0
  119. package/dist/src/learning/requestNormalization.test.js +101 -0
  120. package/dist/src/learning/store.js +116 -12
  121. package/dist/src/learning/store.test.js +299 -0
  122. package/dist/src/learning/vectorAccelerationAdapter.js +22 -6
  123. package/dist/src/learning/vectorAccelerationAdapter.test.js +30 -0
  124. package/dist/src/learning/workspaceHydration.js +168 -0
  125. package/dist/src/learning/workspaceHydration.test.js +30 -0
  126. package/dist/src/license.policy.contract.test.js +9 -4
  127. package/dist/src/markdown/MarkdownGateway.js +4 -0
  128. package/dist/src/markdown/MarkdownGateway.test.js +59 -0
  129. package/dist/src/middleware/SseResponseWriter.js +122 -0
  130. package/dist/src/middleware/SseResponseWriter.test.js +54 -0
  131. package/dist/src/middleware/auth.js +28 -7
  132. package/dist/src/middleware/auth.test.js +33 -0
  133. package/dist/src/middleware/index.js +2 -1
  134. package/dist/src/mobile.android.evidence.contract.test.js +146 -0
  135. package/dist/src/mobile.android.signing.contract.test.js +66 -0
  136. package/dist/src/mobile.artifact.contract.test.js +144 -0
  137. package/dist/src/mobile.budget.contract.test.js +90 -0
  138. package/dist/src/mobile.canonical-id.readiness.test.js +54 -0
  139. package/dist/src/mobile.graph.parity.contract.test.js +207 -0
  140. package/dist/src/mobile.identity.contract.test.js +66 -0
  141. package/dist/src/mobile.identity.corpus.manifest.test.js +72 -0
  142. package/dist/src/mobile.identity.corpus.test.js +40 -0
  143. package/dist/src/mobile.native.recovery.contract.test.js +64 -0
  144. package/dist/src/mobile.pipeline.test.js +11 -1
  145. package/dist/src/mobile.profile.contract.test.js +225 -0
  146. package/dist/src/mobile.semantic.parity.test.js +92 -0
  147. package/dist/src/mobile_exact_analyzer.test.js +113 -0
  148. package/dist/src/notemd/ContentGenerator.js +4 -9
  149. package/dist/src/notemd/FileProcessor.js +4 -9
  150. package/dist/src/notemd/NotemdService.js +12 -17
  151. package/dist/src/notemd/Translator.js +4 -9
  152. package/dist/src/notemd/progressReporter.js +9 -0
  153. package/dist/src/notemd.server.integration.test.js +119 -4
  154. package/dist/src/notemd.workflow.pipeline.test.js +1 -1
  155. package/dist/src/pathbridge.handshake.contract.test.js +29 -0
  156. package/dist/src/pathbridge.host-adapter.contract.test.js +133 -0
  157. package/dist/src/pathcore.bundle.contract.test.js +8 -0
  158. package/dist/src/platform/ExportProfile.js +28 -0
  159. package/dist/src/platform/MobileBudget.js +11 -0
  160. package/dist/src/platform/PlatformCapabilities.js +14 -0
  161. package/dist/src/platform/PlatformCapabilities.test.js +6 -0
  162. package/dist/src/query_backend.external_http.integration.test.js +2 -2
  163. package/dist/src/release.godot.mirror.contract.test.js +28 -4
  164. package/dist/src/resources/ResourceRegistry.js +40 -0
  165. package/dist/src/resources/ResourceRegistry.test.js +36 -0
  166. package/dist/src/routes/index.js +0 -4
  167. package/dist/src/routes/knowledge.js +46 -33
  168. package/dist/src/routes/knowledgePayload.js +185 -0
  169. package/dist/src/routes/knowledgePayload.test.js +53 -0
  170. package/dist/src/routes/markdown.js +8 -0
  171. package/dist/src/routes/notemd.js +990 -234
  172. package/dist/src/routes/registry.contract.test.js +5 -2
  173. package/dist/src/routes/registry.shadow.contract.test.js +22 -0
  174. package/dist/src/routes/render.js +14 -51
  175. package/dist/src/runtime.capabilities.test.js +18 -2
  176. package/dist/src/server.js +566 -1090
  177. package/dist/src/server.migration.test.js +417 -15
  178. package/dist/src/sidecar.build.lock.contract.test.js +201 -0
  179. package/dist/src/sidecar.freshness.contract.test.js +184 -0
  180. package/dist/src/sidecar.replacement.boundary.contract.test.js +6 -3
  181. package/dist/src/sidecar.supply.readiness.contract.test.js +11 -0
  182. package/dist/src/simulation.worker.offline.test.js +104 -0
  183. package/dist/src/storage.provider.capacitor.content.contract.test.js +4 -1
  184. package/dist/src/storage.provider.contract.test.js +152 -1
  185. package/dist/src/tauri.frontend.build.contract.test.js +8 -0
  186. package/dist/src/tauri.window.evidence.execution.test.js +68 -0
  187. package/dist/src/utils/RuntimePaths.js +6 -0
  188. package/dist/src/utils/RuntimePaths.test.js +27 -0
  189. package/dist/src/windows.installer.build.test.js +109 -0
  190. package/dist/src/windows.installer.payload.test.js +76 -0
  191. package/dist/src/windows.installer.runtime.test.js +69 -0
  192. package/dist/src/workspace/WorkspaceRegistry.js +13 -0
  193. package/package.json +32 -10
  194. package/dist/src/routes/data.js +0 -267
  195. package/dist/src/routes/diagnostics.js +0 -51
@@ -35,6 +35,9 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  const fs = __importStar(require("fs"));
37
37
  const path = __importStar(require("path"));
38
+ const browserTestWindow = {};
39
+ globalThis.window = browserTestWindow;
40
+ const storageProvider = require('./frontend/storage_provider.js');
38
41
  describe('storage provider abstraction contract', () => {
39
42
  const repoRoot = path.resolve(__dirname, '..');
40
43
  const storageProviderPath = path.join(repoRoot, 'src', 'frontend', 'storage_provider.js');
@@ -43,6 +46,138 @@ describe('storage provider abstraction contract', () => {
43
46
  expect(source).toContain('window.NoteConnectionStorage = {');
44
47
  expect(source).toContain('createProvider');
45
48
  expect(source).toContain('class RuntimeStorageProvider');
49
+ expect(source).toContain('getStorageResolution()');
50
+ expect(source).toContain('async refreshStorageResolution()');
51
+ expect(source).toContain("sidecarFetchJson('api/knowledge/store-diagnostics')");
52
+ expect(source).toContain('supportsProjection');
53
+ });
54
+ test('forces projection resolution on Android even when a stale SQLite capability is reported', () => {
55
+ const provider = storageProvider.createProvider({
56
+ runtimeCaps: {
57
+ platform: 'android',
58
+ supports_projection_store: true,
59
+ supports_sqlite: true,
60
+ storage_requested_provider: 'sqlite',
61
+ storage_resolved_provider: 'sqlite',
62
+ },
63
+ });
64
+ expect(provider.getStorageResolution()).toEqual(expect.objectContaining({
65
+ requestedProvider: 'sqlite',
66
+ resolvedProvider: 'projection',
67
+ fallbackReason: 'native_sqlite_runtime_unavailable',
68
+ supportsSqlite: false,
69
+ supportsProjection: true,
70
+ }));
71
+ });
72
+ test('forces projection resolution for an iOS/mobile host without Capacitor globals', () => {
73
+ const provider = storageProvider.createProvider({
74
+ runtimeCaps: {
75
+ platform: 'ios',
76
+ supports_projection_store: true,
77
+ supports_sqlite: true,
78
+ storage_requested_provider: 'sqlite',
79
+ storage_resolved_provider: 'sqlite',
80
+ },
81
+ });
82
+ expect(provider.getStorageResolution()).toEqual(expect.objectContaining({
83
+ requestedProvider: 'sqlite',
84
+ resolvedProvider: 'projection',
85
+ fallbackReason: 'native_sqlite_runtime_unavailable',
86
+ supportsSqlite: false,
87
+ supportsProjection: true,
88
+ }));
89
+ });
90
+ test('keeps SQLite as the desktop default and exposes file fallback explicitly', () => {
91
+ const sqliteProvider = storageProvider.createProvider({
92
+ runtimeCaps: {
93
+ platform: 'win32',
94
+ supports_sidecar: true,
95
+ supports_sqlite: true,
96
+ storage_resolved_provider: 'sqlite',
97
+ },
98
+ });
99
+ expect(sqliteProvider.getStorageResolution()).toEqual(expect.objectContaining({
100
+ requestedProvider: 'sqlite',
101
+ resolvedProvider: 'sqlite',
102
+ supportsSqlite: true,
103
+ }));
104
+ const fallbackProvider = storageProvider.createProvider({
105
+ runtimeCaps: {
106
+ platform: 'linux',
107
+ supports_sidecar: false,
108
+ supports_sqlite: false,
109
+ storage_requested_provider: 'sqlite',
110
+ storage_resolved_provider: 'file',
111
+ storage_fallback_reason: 'sqlite_runtime_unavailable',
112
+ },
113
+ });
114
+ expect(fallbackProvider.getStorageResolution()).toEqual(expect.objectContaining({
115
+ requestedProvider: 'sqlite',
116
+ resolvedProvider: 'file',
117
+ fallbackReason: 'sqlite_runtime_unavailable',
118
+ supportsSqlite: false,
119
+ supportsProjection: false,
120
+ }));
121
+ });
122
+ test('does not treat desktop capability support as an active SQLite resolution before diagnostics', () => {
123
+ const provider = storageProvider.createProvider({
124
+ runtimeCaps: {
125
+ platform: 'win32',
126
+ supports_sidecar: true,
127
+ supports_sqlite: true,
128
+ storage_requested_provider: 'sqlite',
129
+ storage_resolved_provider: 'unknown',
130
+ },
131
+ });
132
+ expect(provider.getStorageResolution()).toEqual(expect.objectContaining({
133
+ requestedProvider: 'sqlite',
134
+ resolvedProvider: undefined,
135
+ supportsSqlite: false,
136
+ }));
137
+ });
138
+ test('refreshes desktop resolution from authoritative sidecar diagnostics', async () => {
139
+ const previousWindow = globalThis.window;
140
+ const previousFetch = globalThis.fetch;
141
+ const runtimeWindow = {
142
+ NoteConnectionRuntime: {
143
+ buildUrl: (resourcePath) => `http://127.0.0.1:3000/${resourcePath}`,
144
+ buildFetchOptions: () => ({ cache: 'no-store' }),
145
+ },
146
+ };
147
+ globalThis.window = runtimeWindow;
148
+ globalThis.fetch = jest.fn().mockResolvedValue({
149
+ ok: true,
150
+ json: async () => ({
151
+ store: {
152
+ requestedProvider: 'sqlite',
153
+ resolvedProvider: 'file',
154
+ storageEngine: 'file',
155
+ fallbackReason: 'sqlite_runtime_unavailable',
156
+ },
157
+ }),
158
+ });
159
+ try {
160
+ const provider = storageProvider.createProvider({
161
+ runtimeCaps: {
162
+ platform: 'win32',
163
+ supports_sidecar: true,
164
+ supports_sqlite: true,
165
+ storage_requested_provider: 'sqlite',
166
+ storage_resolved_provider: 'sqlite',
167
+ },
168
+ });
169
+ await expect(provider.refreshStorageResolution()).resolves.toEqual(expect.objectContaining({
170
+ requestedProvider: 'sqlite',
171
+ resolvedProvider: 'file',
172
+ fallbackReason: 'sqlite_runtime_unavailable',
173
+ supportsSqlite: false,
174
+ }));
175
+ expect(globalThis.fetch).toHaveBeenCalledWith('http://127.0.0.1:3000/api/knowledge/store-diagnostics', { cache: 'no-store' });
176
+ }
177
+ finally {
178
+ globalThis.window = previousWindow;
179
+ globalThis.fetch = previousFetch;
180
+ }
46
181
  });
47
182
  test('supports sidecar, tauri, and read-only capability branches', () => {
48
183
  const source = fs.readFileSync(storageProviderPath, 'utf8');
@@ -53,6 +188,14 @@ describe('storage provider abstraction contract', () => {
53
188
  expect(source).toContain('getCapacitorFilesystemPlugin()');
54
189
  expect(source).toContain('ensureCapacitorFilesystemPermission');
55
190
  expect(source).toContain('normalizeCapacitorPath');
191
+ expect(source).toContain('createMobileResourceIdentity');
192
+ expect(source).toContain('canonicalMobileNodeIdFromIdentity');
193
+ expect(source).toContain('NoteConnectionMobileIdentity');
194
+ expect(source).toContain('extractMarkdownLinks');
195
+ expect(source).toContain('sourceUri: file.sourceUri ||');
196
+ expect(source).toContain('canonicalId: file.canonicalId ||');
197
+ expect(source).toContain('identityAliases: Array.isArray(file.identityAliases)');
198
+ expect(source).toContain('Capacitor graph contains duplicate legacy node id');
56
199
  expect(source).toContain('extractRelativePathFromKbMarker');
57
200
  expect(source).toContain('resolveCapacitorContentCandidatePath');
58
201
  expect(source).toContain('async function capacitorReadText(pathValue, options)');
@@ -64,11 +207,19 @@ describe('storage provider abstraction contract', () => {
64
207
  expect(source).toContain('CAPACITOR_GRAPH_BUILD_WORKER_TIMEOUT_MS');
65
208
  expect(source).toContain("buildMode: 'single-thread-fallback'");
66
209
  expect(source).toContain("buildMode: 'worker'");
210
+ expect(source).toContain('var linkedFile = resolveReference(file, rawLink, referenceIndex);');
211
+ expect(source).toContain('addEdge(sourceId, linkedFile && linkedFile.id, "wiki-link")');
212
+ expect(source).toContain('function buildCapacitorReferenceIndex(files)');
213
+ expect(source).toContain('Capacitor graph contains an ambiguous legacy basename');
214
+ expect(source).toContain('sourceUri: sourceNode && sourceNode.sourceUri');
67
215
  expect(source).toContain("const result = await capacitorBuildGraph(requestPayload || {}, this.runtimeCaps || {});");
68
216
  expect(source).toContain('buildModeDetail: resolveCapacitorBuildModeDetail(buildResult.buildMode, runtimeCaps || {})');
69
217
  expect(source).toContain('supportsMobileWasmCompute');
70
218
  expect(source).toContain('mobileWasmReason');
71
219
  expect(source).toContain("unsupportedOperationError('buildGraph')");
220
+ expect(source).toContain('storage_requested_provider');
221
+ expect(source).toContain('storage_resolved_provider');
222
+ expect(source).toContain('storage_fallback_reason');
72
223
  });
73
224
  test('defines core storage operations used by source manager and reader', () => {
74
225
  const source = fs.readFileSync(storageProviderPath, 'utf8');
@@ -80,7 +231,7 @@ describe('storage provider abstraction contract', () => {
80
231
  expect(source).toContain('async buildGraph(requestPayload)');
81
232
  expect(source).toContain('async readContent(filePath)');
82
233
  expect(source).toContain('const capacitorPath = resolveCapacitorContentCandidatePath(filePath);');
83
- expect(source).toContain('return await capacitorReadText(capacitorPath);');
234
+ expect(source).toContain('return await capacitorReadText(capacitorPath, {');
84
235
  expect(source).toContain('async setKbPath(kbPath)');
85
236
  expect(source).toContain('async readGeneratedAsset(filename)');
86
237
  });
@@ -40,6 +40,7 @@ describe('tauri frontend build contracts', () => {
40
40
  const packageJsonPath = path.join(repoRoot, 'package.json');
41
41
  const tauriConfigPath = path.join(repoRoot, 'src-tauri', 'tauri.conf.json');
42
42
  const wrapperScriptPath = path.join(repoRoot, 'scripts', 'run-tauri-frontend-build.js');
43
+ const viteConfigPath = path.join(repoRoot, 'vite.config.ts');
43
44
  function readJson(filePath) {
44
45
  return JSON.parse(fs.readFileSync(filePath, 'utf8'));
45
46
  }
@@ -57,4 +58,11 @@ describe('tauri frontend build contracts', () => {
57
58
  expect(wrapper.resolveFrontendBuildCommand({ frontendBuildMode: 'full' })).toBe('npm run build:full');
58
59
  expect(() => wrapper.resolveFrontendBuildCommand({ frontendBuildMode: 'legacy-full' })).toThrow(/Unsupported tauri frontend build mode/i);
59
60
  });
61
+ test('keeps Vite verification output separate from the Tauri runtime frontend directory', () => {
62
+ const viteConfig = fs.readFileSync(viteConfigPath, 'utf8');
63
+ expect(viteConfig).toContain("const viteVerificationDir = resolve(__dirname, 'dist', 'vite-verify')");
64
+ expect(viteConfig).toContain('outDir: viteVerificationDir');
65
+ expect(viteConfig).toContain('emptyOutDir: true');
66
+ expect(viteConfig).not.toContain('emptyOutDir: false');
67
+ });
60
68
  });
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const child_process_1 = require("child_process");
4
+ jest.mock('child_process', () => ({ ...jest.requireActual('child_process'), spawnSync: jest.fn() }));
5
+ const { runRustWindowEvidenceTests } = require('../scripts/verify-agent-workspace-tauri-window-evidence');
6
+ const { runRustContractTests } = require('../scripts/verify-agent-workspace-tauri-rust');
7
+ const spawn = child_process_1.spawnSync;
8
+ describe('native window test execution evidence', () => {
9
+ afterEach(() => jest.clearAllMocks());
10
+ test.each([
11
+ ['empty filter', 'running 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 30 filtered out;\n'],
12
+ ['ignored test', 'test tests::{name} ... ignored\n\ntest result: ok. 0 passed; 0 failed; 1 ignored;\n'],
13
+ ['different test', 'test tests::some_other_test ... ok\n\ntest result: ok. 1 passed; 0 failed; 0 ignored;\n'],
14
+ ['failed test', 'test tests::{name} ... FAILED\n'],
15
+ ])('does not qualify a zero-exit Cargo run with %s', (_label, stdout) => {
16
+ spawn.mockImplementation((_command, args) => ({ status: 0, stdout: stdout.replace('{name}', args[3]), stderr: '' }));
17
+ const results = runRustWindowEvidenceTests();
18
+ expect(results).toHaveLength(2);
19
+ expect(results.every((result) => result.passed === false)).toBe(true);
20
+ });
21
+ test('requires the named test to pass even when other test executables run zero tests', () => {
22
+ spawn.mockImplementation((_command, args) => ({ status: 0, stderr: '', stdout: `running 0 tests\ntest result: ok. 0 passed; 0 failed;\n\ntest tests::${args[3]} ... ok\n\ntest result: ok. 1 passed; 0 failed;\n` }));
23
+ expect(runRustWindowEvidenceTests().every((result) => result.passed)).toBe(true);
24
+ });
25
+ test('rejects a passing named test when Cargo later fails', () => {
26
+ spawn.mockImplementation((_command, args) => ({ status: 101, stderr: 'later test executable failed', stdout: `test tests::${args[3]} ... ok\n` }));
27
+ expect(runRustWindowEvidenceTests().every((result) => result.passed === false)).toBe(true);
28
+ });
29
+ test('selects the native window feature and library target in separate Cargo processes', () => {
30
+ spawn.mockReturnValue({ status: 0, stdout: '', stderr: '' });
31
+ runRustWindowEvidenceTests();
32
+ expect(spawn).toHaveBeenCalledTimes(2);
33
+ for (const [command, args] of spawn.mock.calls) {
34
+ expect(command).toBe('cargo');
35
+ expect(args).toEqual(expect.arrayContaining(['--lib', '--locked', '--features', 'native-window-tests', '--ignored', '--show-output']));
36
+ }
37
+ });
38
+ });
39
+ describe('ordinary Rust contract execution evidence', () => {
40
+ afterEach(() => jest.clearAllMocks());
41
+ const names = [
42
+ 'pathmode_window_toggle_plan_decouples_godot_signal_from_tauri_hide_restore_flags',
43
+ 'pathmode_window_toggle_plan_restores_tauri_focus_when_restore_policy_is_enabled',
44
+ 'pathmode_window_toggled_event_payload_contains_config_and_execution_plan',
45
+ ];
46
+ test.each([
47
+ ['empty selection', 0, 'running 0 tests\ntest result: ok. 0 passed; 0 failed;', ''],
48
+ ['unrelated passing test', 0, 'test tests::unrelated ... ok', ''],
49
+ ['ignored tests', 0, names.map(name => `test tests::${name} ... ignored`).join('\n'), ''],
50
+ ['failed Cargo invocation mentioning zero tests', 101, '', '0 tests matched after failure'],
51
+ ])('does not qualify %s', (_label, status, stdout, stderr) => {
52
+ spawn.mockReturnValue({ status, stdout, stderr });
53
+ const results = runRustContractTests();
54
+ expect(results.every((result) => result.passed === false)).toBe(true);
55
+ expect(results.every((result) => result.exitCode === status)).toBe(true);
56
+ });
57
+ test('requires every named case, including both toggle policy tests', () => {
58
+ spawn.mockReturnValue({ status: 0, stdout: `test tests::${names[0]} ... ok\n`, stderr: '' });
59
+ expect(runRustContractTests().every((result) => result.passed === false)).toBe(true);
60
+ });
61
+ test('qualifies executed names and preserves a later Cargo failure', () => {
62
+ const stdout = names.map(name => `test tests::${name} ... ok`).join('\n');
63
+ spawn.mockReturnValue({ status: 0, stdout, stderr: '' });
64
+ expect(runRustContractTests().every((result) => result.passed)).toBe(true);
65
+ spawn.mockReturnValue({ status: 101, stdout, stderr: 'process failed' });
66
+ expect(runRustContractTests().every((result) => result.passed === false)).toBe(true);
67
+ });
68
+ });
@@ -142,6 +142,11 @@ function resolveRuntimePaths(moduleDir) {
142
142
  throw new Error(`Unable to provision writable runtime data directory: ${runtimeDataDirCandidate}`);
143
143
  }
144
144
  const runtimeDataDir = path.resolve(runtimeDataDirCandidate);
145
+ // pkg's projectRoot is a virtual, read-only asset tree. Mutable discovery
146
+ // credentials belong to the writable runtime directory in packaged builds.
147
+ const runtimeManifestPath = process.pkg
148
+ ? path.join(runtimeDataDir, 'active-sidecar-runtime.json')
149
+ : path.join(projectRoot, 'tmp', 'active-sidecar-runtime.json');
145
150
  const kbCandidates = uniqPaths([
146
151
  envKbRoot,
147
152
  path.join(projectRoot, 'Knowledge_Base'),
@@ -154,6 +159,7 @@ function resolveRuntimePaths(moduleDir) {
154
159
  projectRoot,
155
160
  frontendDir,
156
161
  runtimeDataDir,
162
+ runtimeManifestPath,
157
163
  kbRoot
158
164
  };
159
165
  }
@@ -97,6 +97,33 @@ describe('resolveRuntimePaths', () => {
97
97
  expect(resolved.runtimeDataDir).toBe(path.resolve(runtimeDataDir));
98
98
  expect(resolved.kbRoot).toBe(path.resolve(kbRoot));
99
99
  });
100
+ test('keeps packaged runtime manifests in the writable runtime directory', () => {
101
+ const previousPkg = Object.getOwnPropertyDescriptor(process, 'pkg');
102
+ Object.defineProperty(process, 'pkg', { configurable: true, value: { entrypoint: '/snapshot/app/server.js' } });
103
+ restoreEnv.push(() => {
104
+ if (previousPkg)
105
+ Object.defineProperty(process, 'pkg', previousPkg);
106
+ else
107
+ Reflect.deleteProperty(process, 'pkg');
108
+ });
109
+ const projectRoot = temp.mkdir('packaged_assets');
110
+ temp.mkdir(path.join('packaged_assets', 'dist', 'src', 'frontend'));
111
+ const runtimeDataDir = temp.mkdir('user_runtime');
112
+ restoreEnv.push(setEnv('NOTE_CONNECTION_PROJECT_ROOT', projectRoot));
113
+ restoreEnv.push(setEnv('NOTE_CONNECTION_RUNTIME_DATA_DIR', runtimeDataDir));
114
+ const resolved = (0, RuntimePaths_1.resolveRuntimePaths)(temp.child('module'));
115
+ expect('runtimeManifestPath' in resolved ? resolved.runtimeManifestPath : undefined)
116
+ .toBe(path.join(runtimeDataDir, 'active-sidecar-runtime.json'));
117
+ });
118
+ test('preserves the development manifest location used by diagnostic tools', () => {
119
+ const projectRoot = temp.mkdir('project');
120
+ temp.mkdir(path.join('project', 'Knowledge_Base'));
121
+ restoreEnv.push(setEnv('NOTE_CONNECTION_PROJECT_ROOT', projectRoot));
122
+ restoreEnv.push(setEnv('NOTE_CONNECTION_RUNTIME_DATA_DIR', temp.mkdir('runtime')));
123
+ const resolved = (0, RuntimePaths_1.resolveRuntimePaths)(temp.child('module'));
124
+ expect('runtimeManifestPath' in resolved ? resolved.runtimeManifestPath : undefined)
125
+ .toBe(path.join(projectRoot, 'tmp', 'active-sidecar-runtime.json'));
126
+ });
100
127
  test('normalizes env kb path that points to a folder inside Knowledge_Base', () => {
101
128
  const projectRoot = temp.mkdir('project');
102
129
  const kbRoot = temp.mkdir(path.join('project', 'Knowledge_Base'));
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const fs = __importStar(require("node:fs"));
37
+ const path = __importStar(require("node:path"));
38
+ const node_child_process_1 = require("node:child_process");
39
+ jest.mock('node:child_process', () => ({ ...jest.requireActual('node:child_process'), spawnSync: jest.fn() }));
40
+ const { recordInstallerBuild, readInstallerBuild } = require('../scripts/verify-windows-installers');
41
+ describe('installer build provenance across fresh runners', () => {
42
+ let directory;
43
+ let receiptPath;
44
+ const environment = { GITHUB_SHA: 'a'.repeat(40), GITHUB_RUN_ID: '123', GITHUB_RUN_ATTEMPT: '1' };
45
+ const write = (relative, contents) => {
46
+ const file = path.join(directory, relative);
47
+ fs.mkdirSync(path.dirname(file), { recursive: true });
48
+ fs.writeFileSync(file, contents);
49
+ };
50
+ beforeEach(() => {
51
+ const temporaryRoot = path.resolve(__dirname, '../output/verification/installer-build-tests');
52
+ fs.mkdirSync(temporaryRoot, { recursive: true });
53
+ directory = fs.mkdtempSync(path.join(temporaryRoot, 'receipt-'));
54
+ receiptPath = path.join(directory, 'output/verification/windows-installer-build.json');
55
+ write('package.json', JSON.stringify({ version: '1.8.0' }));
56
+ write('src/fixture.ts', 'export const source = 1;');
57
+ write('src-tauri/target/release/npm.exe', 'PE __TAURI_BUNDLE_TYPE_VAR_UNK fixture');
58
+ for (const name of ['server', 'godot', 'markdown-worker'])
59
+ write(`src-tauri/bin/${name}-x86_64-pc-windows-msvc.exe`, name);
60
+ write('output/desktop/path_mode.pck', 'GDPC fixture');
61
+ write('src-tauri/target/release/bundle/nsis/NoteConnection_1.8.0_x64-setup.exe', 'NSIS fixture');
62
+ write('src-tauri/target/release/bundle/msi/NoteConnection_1.8.0_x64_en-US.msi', 'MSI fixture');
63
+ recordInstallerBuild(directory, environment);
64
+ });
65
+ afterEach(() => {
66
+ expect(node_child_process_1.spawnSync).not.toHaveBeenCalled();
67
+ jest.clearAllMocks();
68
+ fs.rmSync(directory, { recursive: true, force: true });
69
+ });
70
+ test.each(['nsis', 'msi'])('accepts %s without build dependencies, including failed-only retries', format => {
71
+ // A qualification runner has only source, the receipt and its installer.
72
+ fs.rmSync(path.join(directory, 'src-tauri/bin'), { recursive: true });
73
+ fs.unlinkSync(path.join(directory, 'src-tauri/target/release/npm.exe'));
74
+ fs.rmSync(path.join(directory, 'output/desktop'), { recursive: true });
75
+ const receipt = readInstallerBuild(format, directory, { ...environment, GITHUB_RUN_ATTEMPT: '2' });
76
+ expect(receipt.buildRunAttempt).toBe('1');
77
+ expect(receipt.expectedFiles[format]).toHaveLength(5);
78
+ expect(readInstallerBuild(format, directory, environment)).toEqual(receipt);
79
+ });
80
+ test.each([
81
+ ['sourceRevision', 'b'.repeat(40), /source revision differs/],
82
+ ['runId', '456', /workflow run differs/],
83
+ ['version', '9.0.0', /version differs/],
84
+ ['buildRunAttempt', '0', /build attempt/],
85
+ ['buildRunAttempt', '2', /build attempt/],
86
+ ['sourceFingerprint', {}, /source fingerprint differs/],
87
+ ])('rejects tampered %s before executing anything', (field, replacement, message) => {
88
+ const receipt = JSON.parse(fs.readFileSync(receiptPath, 'utf8'));
89
+ receipt[field] = replacement;
90
+ fs.writeFileSync(receiptPath, JSON.stringify(receipt));
91
+ expect(() => readInstallerBuild('msi', directory, environment)).toThrow(message);
92
+ });
93
+ test.each(['name', 'path'])('rejects redirected installer %s', field => {
94
+ const receipt = JSON.parse(fs.readFileSync(receiptPath, 'utf8'));
95
+ receipt.installers.msi[field] = '../other.msi';
96
+ fs.writeFileSync(receiptPath, JSON.stringify(receipt));
97
+ expect(() => readInstallerBuild('msi', directory, environment)).toThrow(/Installer (basename|path) differs/);
98
+ });
99
+ test.each(['MSI altered', 'truncated'])('rejects altered installer bytes (%s)', contents => {
100
+ write('src-tauri/target/release/bundle/msi/NoteConnection_1.8.0_x64_en-US.msi', contents);
101
+ expect(() => readInstallerBuild('msi', directory, environment)).toThrow(/Installer (size|hash) differs/);
102
+ });
103
+ test('rejects changed source and missing receipts without recomputing build expectations', () => {
104
+ write('src/fixture.ts', 'export const source = 2;');
105
+ expect(() => readInstallerBuild('msi', directory, environment)).toThrow(/source fingerprint differs/);
106
+ fs.unlinkSync(receiptPath);
107
+ expect(() => readInstallerBuild('msi', directory, environment)).toThrow(/ENOENT/);
108
+ });
109
+ });
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const fs = __importStar(require("fs"));
37
+ const os = __importStar(require("os"));
38
+ const path = __importStar(require("path"));
39
+ const { verifyInstalledPayload, fingerprintTauriInstallerExecutable } = require('../scripts/verify-windows-installers');
40
+ const { sha256File } = require('../scripts/sidecar-build-fingerprint');
41
+ describe('installed Windows payload verification', () => {
42
+ let directory;
43
+ let expected;
44
+ beforeEach(() => {
45
+ directory = fs.mkdtempSync(path.join(fs.realpathSync(os.tmpdir()), 'installed payload '));
46
+ fs.writeFileSync(path.join(directory, 'path_mode.pck'), 'GDPC fixture');
47
+ expected = [{ name: 'path_mode.pck', bytes: 12, sha256: sha256File(path.join(directory, 'path_mode.pck')) }];
48
+ });
49
+ afterEach(() => fs.rmSync(directory, { recursive: true, force: true }));
50
+ test('matches exact packaged bytes under paths containing spaces', () => {
51
+ expect(verifyInstalledPayload(directory, expected)).toEqual(expected);
52
+ });
53
+ test('rejects missing resources even when the installer reported success', () => {
54
+ fs.unlinkSync(path.join(directory, 'path_mode.pck'));
55
+ expect(() => verifyInstalledPayload(directory, expected)).toThrow(/missing path_mode.pck/);
56
+ });
57
+ test('rejects same-sized stale resources', () => {
58
+ fs.writeFileSync(path.join(directory, 'path_mode.pck'), 'GDPC altered');
59
+ expect(() => verifyInstalledPayload(directory, expected)).toThrow(/hash differs/);
60
+ });
61
+ test.each(['NSS', 'MSI'])('checks the exact Tauri %s stamp without ignoring executable bytes', (bundleCode) => {
62
+ const executable = path.join(directory, 'build.exe');
63
+ fs.writeFileSync(executable, 'PE before\0__TAURI_BUNDLE_TYPE_VAR_UNK\0PE after');
64
+ const fingerprint = fingerprintTauriInstallerExecutable(executable, bundleCode);
65
+ fs.writeFileSync(path.join(directory, 'npm.exe'), `PE before\0__TAURI_BUNDLE_TYPE_VAR_${bundleCode}\0PE after`);
66
+ expect(verifyInstalledPayload(directory, [fingerprint])).toEqual([fingerprint]);
67
+ fs.writeFileSync(path.join(directory, 'npm.exe'), `PE edited\0__TAURI_BUNDLE_TYPE_VAR_${bundleCode}\0PE after`);
68
+ expect(() => verifyInstalledPayload(directory, [fingerprint])).toThrow(/hash differs/);
69
+ expect(fs.readFileSync(executable, 'utf8')).toContain('_VAR_UNK');
70
+ });
71
+ test.each(['missing marker', '__TAURI_BUNDLE_TYPE_VAR_UNK__TAURI_BUNDLE_TYPE_VAR_UNK'])('rejects missing or ambiguous bundle stamps (%s)', (contents) => {
72
+ const executable = path.join(directory, 'build.exe');
73
+ fs.writeFileSync(executable, contents);
74
+ expect(() => fingerprintTauriInstallerExecutable(executable, 'NSS')).toThrow(/exactly one/);
75
+ });
76
+ });
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const fs = __importStar(require("node:fs"));
37
+ const os = __importStar(require("node:os"));
38
+ const path = __importStar(require("node:path"));
39
+ const node_child_process_1 = require("node:child_process");
40
+ jest.mock('node:child_process', () => ({ ...jest.requireActual('node:child_process'), spawnSync: jest.fn() }));
41
+ const { verifyInstalledRuntime } = require('../scripts/verify-windows-installers');
42
+ const spawn = node_child_process_1.spawnSync;
43
+ describe('installed runtime evidence verdict', () => {
44
+ let directory;
45
+ let run;
46
+ beforeEach(() => {
47
+ directory = fs.mkdtempSync(path.join(fs.realpathSync(os.tmpdir()), 'installer-runtime-'));
48
+ run = path.join(directory, 'runtime');
49
+ fs.mkdirSync(run);
50
+ spawn.mockReturnValue({ status: 0, stdout: '', stderr: '' });
51
+ fs.writeFileSync(path.join(run, 'report.json'), JSON.stringify({ passed: true, checks: { godotLayout: true, windowTransitions: true } }));
52
+ fs.writeFileSync(path.join(run, 'native-debug-policy.json'), '\uFEFF' + JSON.stringify({ policyValuesRemoved: true }));
53
+ });
54
+ afterEach(() => { jest.resetAllMocks(); fs.rmSync(directory, { recursive: true, force: true }); });
55
+ test('accepts completed runtime evidence only after temporary policies are removed', () => {
56
+ expect(verifyInstalledRuntime(directory, run).passed).toBe(true);
57
+ expect(spawn.mock.calls[0][0]).toBe('powershell.exe');
58
+ expect(spawn.mock.calls[0][1]).toContain(path.resolve(__dirname, '../scripts/verify-elevated-desktop-runtime.ps1'));
59
+ });
60
+ test('rejects a successful runtime when machine policy cleanup did not complete', () => {
61
+ fs.writeFileSync(path.join(run, 'native-debug-policy.json'), JSON.stringify({ policyValuesRemoved: false }));
62
+ expect(() => verifyInstalledRuntime(directory, run)).toThrow(/policies must be removed/);
63
+ });
64
+ test('does not accept an empty successful process or lose its setup failure', () => {
65
+ fs.unlinkSync(path.join(run, 'report.json'));
66
+ spawn.mockReturnValue({ status: 1, stdout: '', stderr: 'Existing app policy cannot be overwritten' });
67
+ expect(() => verifyInstalledRuntime(directory, run)).toThrow(/Existing app policy cannot be overwritten/);
68
+ });
69
+ });
@@ -80,6 +80,19 @@ class WorkspaceRegistry {
80
80
  }
81
81
  return this.bindings.get(bindingId) || null;
82
82
  }
83
+ /** Mirrors a committed document move for workspace-scoped path filters. */
84
+ updateDocumentSourcePath(documentId, sourcePath, updatedAt) {
85
+ const binding = this.resolveBindingByDocumentId(documentId);
86
+ if (!binding) {
87
+ return false;
88
+ }
89
+ this.bindings.set(binding.bindingId, {
90
+ ...binding,
91
+ sourcePath,
92
+ updatedAt,
93
+ });
94
+ return true;
95
+ }
83
96
  getWorkspaceById(workspaceId) {
84
97
  return this.workspaces.get(String(workspaceId || '').trim().toLowerCase()) || null;
85
98
  }