noteconnection 1.8.0 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) 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/appimage.update.test.js +127 -0
  8. package/dist/src/backend/FileLoader.js +18 -4
  9. package/dist/src/backend/FileLoader.test.js +125 -0
  10. package/dist/src/backend/GraphBuilder.identity.test.js +86 -0
  11. package/dist/src/backend/GraphBuilder.js +66 -18
  12. package/dist/src/backend/GraphBuilder.matching.test.js +127 -0
  13. package/dist/src/backend/GraphMetrics.js +0 -3
  14. package/dist/src/backend/ResourceIdentity.js +155 -0
  15. package/dist/src/backend/ResourceIdentity.test.js +113 -0
  16. package/dist/src/backend/algorithms/CycleDetection.js +0 -1
  17. package/dist/src/backend/algorithms/HybridEngine.js +0 -3
  18. package/dist/src/backend/algorithms/LayoutEngine.js +0 -1
  19. package/dist/src/backend/algorithms/StatisticalAnalyzer.js +0 -4
  20. package/dist/src/backend/algorithms/TopologicalSort.js +0 -2
  21. package/dist/src/backend/build_dag.js +0 -1
  22. package/dist/src/capacitor.bridge.serialization.contract.test.js +3 -0
  23. package/dist/src/capacitor.runtime.contract.test.js +11 -2
  24. package/dist/src/core/Graph.js +138 -42
  25. package/dist/src/core/Graph.test.js +74 -0
  26. package/dist/src/core/NoteConnection.js +2 -2
  27. package/dist/src/core/PathBridge.js +265 -2
  28. package/dist/src/core/ResourceReference.js +17 -0
  29. package/dist/src/desktop.godot.pack.test.js +142 -0
  30. package/dist/src/desktop.runtime.graph.test.js +66 -0
  31. package/dist/src/export/WorkspaceExportBundle.js +44 -0
  32. package/dist/src/export/WorkspaceExportBundle.test.js +61 -0
  33. package/dist/src/foundation.release.evidence.contract.test.js +127 -9
  34. package/dist/src/foundation.sqlite.runtime.contract.test.js +6 -0
  35. package/dist/src/frontend/README.md +257 -1803
  36. package/dist/src/frontend/agent_workspace.js +665 -7
  37. package/dist/src/frontend/index.html +31 -2
  38. package/dist/src/frontend/knowledge_projection_contract.js +230 -0
  39. package/dist/src/frontend/knowledge_projection_store.js +214 -0
  40. package/dist/src/frontend/libs/path_core.js +158 -42
  41. package/dist/src/frontend/locales/en.json +32 -0
  42. package/dist/src/frontend/locales/zh.json +32 -0
  43. package/dist/src/frontend/mobile_budget_runtime.js +33 -0
  44. package/dist/src/frontend/mobile_exact_analyzer.js +393 -0
  45. package/dist/src/frontend/mobile_identity_contract.js +221 -0
  46. package/dist/src/frontend/mobile_semantic_comparator.js +264 -0
  47. package/dist/src/frontend/path.html +6 -2
  48. package/dist/src/frontend/simulationWorker.js +1 -1
  49. package/dist/src/frontend/source_manager.js +110 -4
  50. package/dist/src/frontend/storage_provider.js +661 -71
  51. package/dist/src/frontend/styles.css +107 -13
  52. package/dist/src/frontend/workspace_panes.js +111 -7
  53. package/dist/src/frontend.runtime.assets.contract.test.js +81 -0
  54. package/dist/src/graph.answer.operator.contract.test.js +72 -0
  55. package/dist/src/index.js +6 -2
  56. package/dist/src/index.knowledge-root.test.js +118 -0
  57. package/dist/src/indexing/IndexLifecycle.js +22 -0
  58. package/dist/src/indexing/IndexLifecycle.test.js +24 -0
  59. package/dist/src/knowledge.api.contract.test.js +4 -2
  60. package/dist/src/knowledge_projection.contract.test.js +134 -0
  61. package/dist/src/knowledge_projection_store.test.js +200 -0
  62. package/dist/src/learning/AnswerQualityEvaluation.js +153 -0
  63. package/dist/src/learning/AnswerQualityEvaluation.test.js +118 -0
  64. package/dist/src/learning/KnowledgeLearningPlatform.js +4687 -3892
  65. package/dist/src/learning/KnowledgeLearningPlatform.persistence.test.js +418 -0
  66. package/dist/src/learning/KnowledgeLearningPlatform.test.js +243 -6
  67. package/dist/src/learning/KnowledgeWorkspaceConversationRegression.js +25 -1
  68. package/dist/src/learning/KnowledgeWorkspaceConversationRegression.test.js +13 -5
  69. package/dist/src/learning/MeasurementEvidenceConflicts.test.js +78 -0
  70. package/dist/src/learning/PublicAnswerContract.test.js +85 -0
  71. package/dist/src/learning/PublicEvidenceSelection.test.js +94 -0
  72. package/dist/src/learning/PublicMeasurementDisclosure.test.js +54 -0
  73. package/dist/src/learning/agentConversationExecution.js +211 -0
  74. package/dist/src/learning/agentConversationExecution.test.js +209 -0
  75. package/dist/src/learning/agentConversationSerialization.js +266 -0
  76. package/dist/src/learning/agentConversationSerialization.test.js +165 -0
  77. package/dist/src/learning/agentResponseBudget.js +179 -0
  78. package/dist/src/learning/agentResponseBudget.test.js +97 -0
  79. package/dist/src/learning/agentWorkspaceCapabilityContracts.js +65 -0
  80. package/dist/src/learning/answerReleaseReview.js +1191 -250
  81. package/dist/src/learning/answerReleaseReview.test.js +1293 -7
  82. package/dist/src/learning/answerTaskPlan.js +473 -0
  83. package/dist/src/learning/conversationComposer.js +627 -386
  84. package/dist/src/learning/conversationComposer.test.js +1403 -0
  85. package/dist/src/learning/evidenceContextAssembler.js +312 -120
  86. package/dist/src/learning/graphAnswerCoverage.js +37 -0
  87. package/dist/src/learning/graphAnswerCoverage.test.js +93 -0
  88. package/dist/src/learning/graphAnswerCoverageCalibration.js +59 -0
  89. package/dist/src/learning/graphAnswerCoverageCalibration.test.js +46 -0
  90. package/dist/src/learning/graphAnswerFacts.js +94 -0
  91. package/dist/src/learning/graphAnswerFacts.test.js +39 -0
  92. package/dist/src/learning/graphAnswerPlan.js +908 -0
  93. package/dist/src/learning/graphAnswerPlan.test.js +907 -0
  94. package/dist/src/learning/graphAnswerQualityCalibration.js +255 -0
  95. package/dist/src/learning/graphAnswerQualityCalibration.test.js +72 -0
  96. package/dist/src/learning/graphAnswerQualityPolicy.js +30 -0
  97. package/dist/src/learning/graphClaimMatcher.js +104 -0
  98. package/dist/src/learning/graphConditionedContext.js +109 -0
  99. package/dist/src/learning/graphConditionedContext.test.js +99 -0
  100. package/dist/src/learning/graphContextAssembler.js +3 -13
  101. package/dist/src/learning/graphContextAssembler.test.js +2 -2
  102. package/dist/src/learning/graphExpansionPolicy.js +22 -0
  103. package/dist/src/learning/graphExpansionPolicy.test.js +33 -0
  104. package/dist/src/learning/identityMetadata.test.js +26 -0
  105. package/dist/src/learning/index.js +1 -0
  106. package/dist/src/learning/mobileAnswerProjection.js +104 -0
  107. package/dist/src/learning/mobileAnswerProjection.test.js +102 -0
  108. package/dist/src/learning/queryBackend.js +34 -10
  109. package/dist/src/learning/queryBackend.test.js +21 -0
  110. package/dist/src/learning/ragContextPack.js +115 -16
  111. package/dist/src/learning/ragContextPack.test.js +151 -0
  112. package/dist/src/learning/ragEvidenceQuality.js +96 -0
  113. package/dist/src/learning/ragEvidenceQuality.test.js +29 -0
  114. package/dist/src/learning/ragPublicText.js +67 -4
  115. package/dist/src/learning/ragPublicText.test.js +57 -0
  116. package/dist/src/learning/ragSufficiencyJudge.js +11 -1
  117. package/dist/src/learning/ragSufficiencyProviderJudge.js +16 -26
  118. package/dist/src/learning/ragSufficiencyProviderJudge.test.js +51 -0
  119. package/dist/src/learning/requestNormalization.js +106 -0
  120. package/dist/src/learning/requestNormalization.test.js +101 -0
  121. package/dist/src/learning/store.js +116 -12
  122. package/dist/src/learning/store.test.js +299 -0
  123. package/dist/src/learning/vectorAccelerationAdapter.js +22 -6
  124. package/dist/src/learning/vectorAccelerationAdapter.test.js +30 -0
  125. package/dist/src/learning/workspaceHydration.js +168 -0
  126. package/dist/src/learning/workspaceHydration.test.js +30 -0
  127. package/dist/src/license.policy.contract.test.js +9 -4
  128. package/dist/src/markdown/MarkdownGateway.js +4 -0
  129. package/dist/src/markdown/MarkdownGateway.test.js +59 -0
  130. package/dist/src/middleware/SseResponseWriter.js +122 -0
  131. package/dist/src/middleware/SseResponseWriter.test.js +54 -0
  132. package/dist/src/middleware/auth.js +28 -7
  133. package/dist/src/middleware/auth.test.js +33 -0
  134. package/dist/src/middleware/index.js +2 -1
  135. package/dist/src/mobile.android.evidence.contract.test.js +146 -0
  136. package/dist/src/mobile.android.signing.contract.test.js +66 -0
  137. package/dist/src/mobile.artifact.contract.test.js +144 -0
  138. package/dist/src/mobile.budget.contract.test.js +90 -0
  139. package/dist/src/mobile.canonical-id.readiness.test.js +54 -0
  140. package/dist/src/mobile.graph.parity.contract.test.js +207 -0
  141. package/dist/src/mobile.identity.contract.test.js +66 -0
  142. package/dist/src/mobile.identity.corpus.manifest.test.js +72 -0
  143. package/dist/src/mobile.identity.corpus.test.js +40 -0
  144. package/dist/src/mobile.native.recovery.contract.test.js +64 -0
  145. package/dist/src/mobile.pipeline.test.js +11 -1
  146. package/dist/src/mobile.profile.contract.test.js +225 -0
  147. package/dist/src/mobile.semantic.parity.test.js +92 -0
  148. package/dist/src/mobile_exact_analyzer.test.js +113 -0
  149. package/dist/src/notemd/ContentGenerator.js +4 -9
  150. package/dist/src/notemd/FileProcessor.js +4 -9
  151. package/dist/src/notemd/NotemdService.js +12 -17
  152. package/dist/src/notemd/Translator.js +4 -9
  153. package/dist/src/notemd/progressReporter.js +9 -0
  154. package/dist/src/notemd.server.integration.test.js +119 -4
  155. package/dist/src/notemd.workflow.pipeline.test.js +1 -1
  156. package/dist/src/pathbridge.handshake.contract.test.js +29 -0
  157. package/dist/src/pathbridge.host-adapter.contract.test.js +133 -0
  158. package/dist/src/pathcore.bundle.contract.test.js +8 -0
  159. package/dist/src/platform/ExportProfile.js +28 -0
  160. package/dist/src/platform/MobileBudget.js +11 -0
  161. package/dist/src/platform/PlatformCapabilities.js +14 -0
  162. package/dist/src/platform/PlatformCapabilities.test.js +6 -0
  163. package/dist/src/query_backend.external_http.integration.test.js +2 -2
  164. package/dist/src/release.godot.mirror.contract.test.js +28 -4
  165. package/dist/src/resources/ResourceRegistry.js +40 -0
  166. package/dist/src/resources/ResourceRegistry.test.js +36 -0
  167. package/dist/src/routes/index.js +0 -4
  168. package/dist/src/routes/knowledge.js +46 -33
  169. package/dist/src/routes/knowledgePayload.js +185 -0
  170. package/dist/src/routes/knowledgePayload.test.js +53 -0
  171. package/dist/src/routes/markdown.js +8 -0
  172. package/dist/src/routes/notemd.js +990 -234
  173. package/dist/src/routes/registry.contract.test.js +5 -2
  174. package/dist/src/routes/registry.shadow.contract.test.js +22 -0
  175. package/dist/src/routes/render.js +14 -51
  176. package/dist/src/runtime.capabilities.test.js +18 -2
  177. package/dist/src/server.js +566 -1090
  178. package/dist/src/server.migration.test.js +417 -15
  179. package/dist/src/sidecar.build.lock.contract.test.js +201 -0
  180. package/dist/src/sidecar.freshness.contract.test.js +184 -0
  181. package/dist/src/sidecar.replacement.boundary.contract.test.js +6 -3
  182. package/dist/src/sidecar.supply.readiness.contract.test.js +11 -0
  183. package/dist/src/simulation.worker.offline.test.js +104 -0
  184. package/dist/src/storage.provider.capacitor.content.contract.test.js +4 -1
  185. package/dist/src/storage.provider.contract.test.js +152 -1
  186. package/dist/src/tauri.frontend.build.contract.test.js +8 -0
  187. package/dist/src/tauri.window.evidence.execution.test.js +68 -0
  188. package/dist/src/utils/RuntimePaths.js +6 -0
  189. package/dist/src/utils/RuntimePaths.test.js +27 -0
  190. package/dist/src/windows.installer.build.test.js +109 -0
  191. package/dist/src/windows.installer.payload.test.js +76 -0
  192. package/dist/src/windows.installer.runtime.test.js +69 -0
  193. package/dist/src/workspace/WorkspaceRegistry.js +13 -0
  194. package/package.json +32 -10
  195. package/dist/src/routes/data.js +0 -267
  196. package/dist/src/routes/diagnostics.js +0 -51
@@ -1,17 +1,38 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.setSidecarAuthToken = setSidecarAuthToken;
4
+ exports.isRequestTokenAuthorized = isRequestTokenAuthorized;
4
5
  exports.isAuthorizedRequest = isAuthorizedRequest;
5
6
  let sidecarAuthToken = null;
6
7
  function setSidecarAuthToken(token) {
7
- sidecarAuthToken = token;
8
+ sidecarAuthToken = String(token || '').trim() || null;
8
9
  }
9
- function isAuthorizedRequest(req) {
10
- if (!sidecarAuthToken)
10
+ function readHeader(req, name) {
11
+ const value = req.headers[name];
12
+ if (Array.isArray(value)) {
13
+ return String(value[0] || '').trim();
14
+ }
15
+ return String(value || '').trim();
16
+ }
17
+ /**
18
+ * Shared credential decision for the sidecar and HTTP server entry points.
19
+ * A configured token changes the default from anonymous allow to explicit deny.
20
+ */
21
+ function isRequestTokenAuthorized(req, expectedToken) {
22
+ const expected = String(expectedToken || '').trim();
23
+ if (!expected)
11
24
  return true;
12
- const authHeader = req.headers.authorization || '';
13
- if (authHeader.startsWith('Bearer ')) {
14
- return authHeader.slice(7) === sidecarAuthToken;
25
+ const legacyToken = readHeader(req, 'x-noteconnection-token');
26
+ if (legacyToken) {
27
+ return legacyToken === expected;
15
28
  }
16
- return true;
29
+ const authHeader = readHeader(req, 'authorization');
30
+ if (!/^Bearer\s+/i.test(authHeader)) {
31
+ return false;
32
+ }
33
+ const bearerToken = authHeader.slice(7).trim();
34
+ return bearerToken.length > 0 && bearerToken === expected;
35
+ }
36
+ function isAuthorizedRequest(req) {
37
+ return isRequestTokenAuthorized(req, sidecarAuthToken || '');
17
38
  }
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const auth_1 = require("./auth");
4
+ function request(headers) {
5
+ return { headers };
6
+ }
7
+ describe('request authorization boundary', () => {
8
+ afterEach(() => {
9
+ (0, auth_1.setSidecarAuthToken)('');
10
+ });
11
+ test('allows requests when no token is configured', () => {
12
+ expect((0, auth_1.isRequestTokenAuthorized)(request({}), '')).toBe(true);
13
+ expect((0, auth_1.isAuthorizedRequest)(request({}))).toBe(true);
14
+ });
15
+ test('accepts a valid Bearer credential', () => {
16
+ expect((0, auth_1.isRequestTokenAuthorized)(request({ authorization: 'Bearer secret' }), 'secret')).toBe(true);
17
+ expect((0, auth_1.isRequestTokenAuthorized)(request({ authorization: 'bearer secret' }), 'secret')).toBe(true);
18
+ });
19
+ test('rejects missing, malformed, and invalid credentials when configured', () => {
20
+ expect((0, auth_1.isRequestTokenAuthorized)(request({}), 'secret')).toBe(false);
21
+ expect((0, auth_1.isRequestTokenAuthorized)(request({ authorization: 'Basic secret' }), 'secret')).toBe(false);
22
+ expect((0, auth_1.isRequestTokenAuthorized)(request({ authorization: 'Bearer ' }), 'secret')).toBe(false);
23
+ expect((0, auth_1.isRequestTokenAuthorized)(request({ authorization: 'Bearer wrong' }), 'secret')).toBe(false);
24
+ });
25
+ test('preserves the legacy token header', () => {
26
+ expect((0, auth_1.isRequestTokenAuthorized)(request({ 'x-noteconnection-token': 'secret' }), 'secret')).toBe(true);
27
+ });
28
+ test('sidecar middleware uses the same strict decision', () => {
29
+ (0, auth_1.setSidecarAuthToken)('secret');
30
+ expect((0, auth_1.isAuthorizedRequest)(request({}))).toBe(false);
31
+ expect((0, auth_1.isAuthorizedRequest)(request({ authorization: 'Bearer secret' }))).toBe(true);
32
+ });
33
+ });
@@ -1,10 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.sendError = exports.sendJson = exports.parseJsonBody = exports.readRequestBody = exports.ERROR_CODE_HEADER = exports.appendRuntimeApiRequestTrace = exports.getRuntimeRequestTrace = exports.normalizeApiErrorCodeToken = exports.finishRequestTrace = exports.startRequestTraceHandler = exports.resolveRequestId = exports.setSidecarAuthToken = exports.isAuthorizedRequest = exports.applyCorsHeaders = void 0;
3
+ exports.sendError = exports.sendJson = exports.parseJsonBody = exports.readRequestBody = exports.ERROR_CODE_HEADER = exports.appendRuntimeApiRequestTrace = exports.getRuntimeRequestTrace = exports.normalizeApiErrorCodeToken = exports.finishRequestTrace = exports.startRequestTraceHandler = exports.resolveRequestId = exports.setSidecarAuthToken = exports.isRequestTokenAuthorized = exports.isAuthorizedRequest = exports.applyCorsHeaders = void 0;
4
4
  var cors_1 = require("./cors");
5
5
  Object.defineProperty(exports, "applyCorsHeaders", { enumerable: true, get: function () { return cors_1.applyCorsHeaders; } });
6
6
  var auth_1 = require("./auth");
7
7
  Object.defineProperty(exports, "isAuthorizedRequest", { enumerable: true, get: function () { return auth_1.isAuthorizedRequest; } });
8
+ Object.defineProperty(exports, "isRequestTokenAuthorized", { enumerable: true, get: function () { return auth_1.isRequestTokenAuthorized; } });
8
9
  Object.defineProperty(exports, "setSidecarAuthToken", { enumerable: true, get: function () { return auth_1.setSidecarAuthToken; } });
9
10
  var request_trace_1 = require("./request-trace");
10
11
  Object.defineProperty(exports, "resolveRequestId", { enumerable: true, get: function () { return request_trace_1.resolveRequestId; } });
@@ -0,0 +1,146 @@
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 path = __importStar(require("path"));
38
+ describe('Tauri Android device evidence contract', () => {
39
+ const repoRoot = path.resolve(__dirname, '..');
40
+ const scriptPath = path.join(repoRoot, 'scripts', 'capture-tauri-android-rss-evidence.js');
41
+ const releaseWorkflowPath = path.join(repoRoot, '.github', 'workflows', 'release-desktop-multi-os.yml');
42
+ const script = require(scriptPath);
43
+ let fixtureRoot;
44
+ beforeEach(() => {
45
+ fixtureRoot = fs.mkdtempSync(path.join(repoRoot, '.tmp-tauri-android-evidence-'));
46
+ });
47
+ afterEach(() => {
48
+ fs.rmSync(fixtureRoot, { recursive: true, force: true });
49
+ });
50
+ test('requires an ordered native workload with restart continuity', () => {
51
+ const specPath = path.join(fixtureRoot, 'workload.json');
52
+ fs.writeFileSync(specPath, JSON.stringify({
53
+ schemaVersion: 1,
54
+ steps: script.requiredWorkloadSteps.map((name) => ({
55
+ name,
56
+ adbArgs: ['shell', 'echo', name],
57
+ })),
58
+ }), 'utf8');
59
+ const parsed = script.parseWorkloadSpec(specPath);
60
+ expect(parsed.schemaVersion).toBe(1);
61
+ expect(parsed.requiredSteps).toEqual(['saf-import', 'graph-build', 'exact-query', 'path', 'continuity']);
62
+ expect(parsed.steps.map((step) => step.name)).toEqual(parsed.requiredSteps);
63
+ });
64
+ test('fails closed when a required workload phase is absent or reordered', () => {
65
+ const missingPath = path.join(fixtureRoot, 'missing.json');
66
+ fs.writeFileSync(missingPath, JSON.stringify({
67
+ schemaVersion: 1,
68
+ steps: script.requiredWorkloadSteps.slice(0, 4).map((name) => ({
69
+ name,
70
+ adbArgs: ['shell', 'echo', name],
71
+ })),
72
+ }), 'utf8');
73
+ expect(() => script.parseWorkloadSpec(missingPath)).toThrow(/missing required steps.*continuity/i);
74
+ const reorderedPath = path.join(fixtureRoot, 'reordered.json');
75
+ fs.writeFileSync(reorderedPath, JSON.stringify({
76
+ schemaVersion: 1,
77
+ steps: [...script.requiredWorkloadSteps].reverse().map((name) => ({
78
+ name,
79
+ adbArgs: ['shell', 'echo', name],
80
+ })),
81
+ }), 'utf8');
82
+ expect(() => script.parseWorkloadSpec(reorderedPath)).toThrow(/must be ordered/i);
83
+ const extraPath = path.join(fixtureRoot, 'extra.json');
84
+ fs.writeFileSync(extraPath, JSON.stringify({
85
+ schemaVersion: 1,
86
+ steps: [
87
+ ...script.requiredWorkloadSteps.map((name) => ({ name, adbArgs: ['shell', 'echo', name] })),
88
+ { name: 'unrecorded-helper', adbArgs: ['shell', 'echo', 'ignored'] },
89
+ ],
90
+ }), 'utf8');
91
+ expect(() => script.parseWorkloadSpec(extraPath)).toThrow(/unsupported steps/i);
92
+ });
93
+ test('keeps device identity private and parses proc pid output deterministically', () => {
94
+ expect(script.maskSerial('R58M123456789')).toBe('R5***89');
95
+ expect(script.maskSerial('1234')).toBe('1234');
96
+ expect(script.parsePid('12345\n')).toBe(12345);
97
+ expect(script.parsePid('')).toBe(0);
98
+ });
99
+ test('requires measurable arm64 hardware budget inputs', () => {
100
+ expect(script.parseAbiList('arm64-v8a, armeabi-v7a\n')).toEqual(['arm64-v8a', 'armeabi-v7a']);
101
+ expect(script.parseAbiList('')).toEqual([]);
102
+ expect(script.parseTotalRamBytes('MemTotal: 2097152 kB\n')).toBe(2097152 * 1024);
103
+ expect(script.parseTotalRamBytes('MemTotal: 2097152 kB\r\n')).toBe(2097152 * 1024);
104
+ expect(script.parseTotalRamBytes('')).toBe(0);
105
+ });
106
+ test('exposes signed artifact and RSS release gates in the package contract', () => {
107
+ const packageJson = JSON.parse(fs.readFileSync(path.join(repoRoot, 'package.json'), 'utf8'));
108
+ expect(packageJson.scripts?.['verify:mobile:artifact:release']).toContain('--require-signed');
109
+ expect(packageJson.scripts?.['verify:mobile:artifact:release']).toContain('--require-arm64-only');
110
+ expect(packageJson.scripts?.['capture:tauri:android:evidence']).toContain('capture-tauri-android-rss-evidence.js');
111
+ });
112
+ test('release workflow refuses unsigned Android uploads and verifies arm64 artifacts', () => {
113
+ const workflow = fs.readFileSync(releaseWorkflowPath, 'utf8');
114
+ expect(workflow).toContain('Materialize Android release signing key');
115
+ expect(workflow).toContain('Build Android arm64 APK');
116
+ expect(workflow).toContain('NOTE_CONNECTION_TAURI_ANDROID_TARGET=aarch64');
117
+ expect(workflow).not.toContain('NOTE_CONNECTION_TAURI_ANDROID_TARGET=universal npm run tauri:android:build');
118
+ expect(workflow).toContain('NOTE_CONNECTION_ANDROID_KEYSTORE_BASE64');
119
+ expect(workflow).toContain('NOTE_CONNECTION_ANDROID_REQUIRE_SIGNING: "1"');
120
+ expect(workflow).toContain('--require-arm64');
121
+ expect(workflow).toContain('--require-arm64-only');
122
+ expect(workflow).toContain('--require-signed');
123
+ expect(workflow).toContain('verify-android-device-evidence');
124
+ expect(workflow).toContain('self-hosted, android-arm64');
125
+ expect(workflow).toContain('--require-rss');
126
+ expect(workflow).toContain('publish-android-release-assets');
127
+ expect(workflow).toContain('name: release-${{ needs.ensure-release.outputs.tag_name }}-android-evidence');
128
+ expect(workflow).not.toContain('name: release-${{ github.event.inputs.tag }}-android-evidence');
129
+ expect(workflow).toContain('noteconnection-arm64-release.apk');
130
+ expect(workflow).toContain('noteconnection-arm64-release.aab');
131
+ expect(workflow).toContain("! -name '*unsigned*'");
132
+ expect(workflow).toContain('build/release/mobile/*');
133
+ expect(workflow).not.toContain('src-tauri/gen/android/app/build/outputs/apk/**/*.apk');
134
+ const androidBuildJob = workflow.split(' build-and-upload-android:')[1]
135
+ .split(' verify-android-device-evidence:')[0];
136
+ expect(androidBuildJob).not.toContain('softprops/action-gh-release@v2');
137
+ });
138
+ test('RSS harness keeps the release ABI contract strict', () => {
139
+ const evidenceScript = fs.readFileSync(scriptPath, 'utf8');
140
+ const verifierCalls = evidenceScript.match(/verifyMobileArtifact\(\{[\s\S]*?\}\);/g) || [];
141
+ expect(verifierCalls.length).toBeGreaterThanOrEqual(2);
142
+ verifierCalls.forEach((call) => {
143
+ expect(call).toContain('requireArm64Only: true');
144
+ });
145
+ });
146
+ });
@@ -0,0 +1,66 @@
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 path = __importStar(require("path"));
37
+ const fs = __importStar(require("fs"));
38
+ describe('Tauri Android signing configuration contract', () => {
39
+ const repoRoot = path.resolve(__dirname, '..');
40
+ const signingScriptPath = path.join(repoRoot, 'scripts', 'configure-tauri-android-signing.js');
41
+ const runnerScriptPath = path.join(repoRoot, 'scripts', 'run-tauri-android.js');
42
+ const signing = require(signingScriptPath);
43
+ test('keeps signing opt-in and injects the config inside the Android DSL', () => {
44
+ const source = `plugins { id("com.android.application") }\n\nandroid {\n defaultConfig {\n applicationId = "com.example"\n }\n buildTypes {\n getByName("release") {\n isMinifyEnabled = true\n }\n }\n}`;
45
+ const configured = signing.addSigningConfig(source);
46
+ expect(configured.indexOf('// NOTE_CONNECTION_ANDROID_SIGNING_START')).toBeGreaterThan(configured.indexOf('android {'));
47
+ expect(configured).toContain('signingConfigs.create("noteConnectionRelease")');
48
+ expect(configured).toContain('signingConfig = noteConnectionReleaseSigningConfig');
49
+ expect(configured).toContain('NOTE_CONNECTION_ANDROID_KEYSTORE_PASSWORD');
50
+ expect(configured).toContain('applicationId = "com.example"');
51
+ });
52
+ test('removes a stale generated signing block without touching unrelated Gradle code', () => {
53
+ const source = `android {\n${signing.renderSigningBlock()}\n defaultConfig {\n applicationId = "com.example"\n }\n}`;
54
+ const cleaned = signing.removeExistingBlock(source);
55
+ expect(cleaned).not.toContain('NOTE_CONNECTION_ANDROID_SIGNING_START');
56
+ expect(cleaned).toContain('applicationId = "com.example"');
57
+ });
58
+ test('does not embed secret values and runner invokes the signer around generated builds', () => {
59
+ const runner = fs.readFileSync(runnerScriptPath, 'utf8');
60
+ const block = signing.renderSigningBlock();
61
+ expect(block).toContain('System.getenv');
62
+ expect(block).not.toContain('password123');
63
+ expect(runner).toContain('configure-tauri-android-signing.js');
64
+ expect(runner).toContain('Failed to synchronize Android release signing configuration');
65
+ });
66
+ });
@@ -0,0 +1,144 @@
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 path = __importStar(require("path"));
38
+ function createStoredZip(entries) {
39
+ const localParts = [];
40
+ const centralParts = [];
41
+ let localOffset = 0;
42
+ for (const entry of entries) {
43
+ const name = Buffer.from(entry.name, 'utf8');
44
+ const local = Buffer.alloc(30 + name.length + entry.content.length);
45
+ local.writeUInt32LE(0x04034b50, 0);
46
+ local.writeUInt16LE(20, 4);
47
+ local.writeUInt16LE(0, 6);
48
+ local.writeUInt16LE(0, 8);
49
+ local.writeUInt16LE(0, 10);
50
+ local.writeUInt16LE(0, 12);
51
+ local.writeUInt32LE(0, 14);
52
+ local.writeUInt32LE(entry.content.length, 18);
53
+ local.writeUInt32LE(entry.content.length, 22);
54
+ local.writeUInt16LE(name.length, 26);
55
+ local.writeUInt16LE(0, 28);
56
+ name.copy(local, 30);
57
+ entry.content.copy(local, 30 + name.length);
58
+ localParts.push(local);
59
+ const central = Buffer.alloc(46 + name.length);
60
+ central.writeUInt32LE(0x02014b50, 0);
61
+ central.writeUInt16LE(20, 4);
62
+ central.writeUInt16LE(20, 6);
63
+ central.writeUInt16LE(0, 8);
64
+ central.writeUInt16LE(0, 10);
65
+ central.writeUInt16LE(0, 12);
66
+ central.writeUInt16LE(0, 14);
67
+ central.writeUInt32LE(0, 16);
68
+ central.writeUInt32LE(entry.content.length, 20);
69
+ central.writeUInt32LE(entry.content.length, 24);
70
+ central.writeUInt16LE(name.length, 28);
71
+ central.writeUInt16LE(0, 30);
72
+ central.writeUInt16LE(0, 32);
73
+ central.writeUInt16LE(0, 34);
74
+ central.writeUInt16LE(0, 36);
75
+ central.writeUInt32LE(0, 38);
76
+ central.writeUInt32LE(localOffset, 42);
77
+ name.copy(central, 46);
78
+ centralParts.push(central);
79
+ localOffset += local.length;
80
+ }
81
+ const localData = Buffer.concat(localParts);
82
+ const centralData = Buffer.concat(centralParts);
83
+ const eocd = Buffer.alloc(22);
84
+ eocd.writeUInt32LE(0x06054b50, 0);
85
+ eocd.writeUInt16LE(0, 8);
86
+ eocd.writeUInt16LE(entries.length, 10);
87
+ eocd.writeUInt32LE(centralData.length, 12);
88
+ eocd.writeUInt32LE(localData.length, 16);
89
+ return Buffer.concat([localData, centralData, eocd]);
90
+ }
91
+ describe('mobile APK/AAB artifact verifier contract', () => {
92
+ const repoRoot = path.resolve(__dirname, '..');
93
+ const verifier = require(path.join(repoRoot, 'scripts', 'verify-mobile-artifact.js'));
94
+ let fixtureRoot;
95
+ beforeEach(() => {
96
+ fixtureRoot = fs.mkdtempSync(path.join(repoRoot, '.tmp-mobile-artifact-'));
97
+ });
98
+ afterEach(() => {
99
+ fs.rmSync(fixtureRoot, { recursive: true, force: true });
100
+ });
101
+ test('checks APK entries and requires RSS in release mode', () => {
102
+ const artifactPath = path.join(fixtureRoot, 'app-arm64-v8a.apk');
103
+ const rssPath = path.join(fixtureRoot, 'rss.json');
104
+ fs.writeFileSync(artifactPath, createStoredZip([
105
+ { name: 'assets/index.html', content: Buffer.from('<html />') },
106
+ { name: 'lib/arm64-v8a/libnoteconnection.so', content: Buffer.from('runtime') },
107
+ ]));
108
+ fs.writeFileSync(rssPath, JSON.stringify({ peakResidentBytes: 128 * 1024 * 1024 }));
109
+ const result = verifier.verifyMobileArtifact({
110
+ artifactPath,
111
+ profile: 'mobile-low',
112
+ rssEvidencePath: rssPath,
113
+ requireRss: true,
114
+ requireArm64Only: true,
115
+ });
116
+ expect(result.artifactKind).toBe('apk');
117
+ expect(result.rssStatus).toBe('within-budget');
118
+ expect(result.hasArm64Payload).toBe(true);
119
+ expect(result.nativeAbis).toEqual(['arm64-v8a']);
120
+ expect(result.compressedPayloadBytes).toBeGreaterThan(0);
121
+ });
122
+ test('rejects forbidden runtime entries and missing release RSS evidence', () => {
123
+ const artifactPath = path.join(fixtureRoot, 'app.aab');
124
+ fs.writeFileSync(artifactPath, createStoredZip([
125
+ { name: 'assets/models/tiny.gguf', content: Buffer.from('model') },
126
+ ]));
127
+ expect(() => verifier.verifyMobileArtifact({ artifactPath, profile: 'mobile-standard' }))
128
+ .toThrow(/Forbidden mobile artifact entries/);
129
+ const cleanArtifactPath = path.join(fixtureRoot, 'clean.aab');
130
+ fs.writeFileSync(cleanArtifactPath, createStoredZip([
131
+ { name: 'assets/index.html', content: Buffer.from('<html />') },
132
+ ]));
133
+ expect(() => verifier.verifyMobileArtifact({
134
+ artifactPath: cleanArtifactPath,
135
+ profile: 'mobile-standard',
136
+ requireRss: true,
137
+ })).toThrow(/RSS evidence is required/);
138
+ expect(() => verifier.verifyMobileArtifact({
139
+ artifactPath: cleanArtifactPath,
140
+ profile: 'mobile-standard',
141
+ requireSigned: true,
142
+ })).toThrow(/signature/i);
143
+ });
144
+ });
@@ -0,0 +1,90 @@
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 path = __importStar(require("path"));
38
+ const vm = __importStar(require("vm"));
39
+ const MobileBudget_1 = require("./platform/MobileBudget");
40
+ describe('versioned mobile budget contract', () => {
41
+ const repoRoot = path.resolve(__dirname, '..');
42
+ const contractPath = path.join(repoRoot, 'config', 'mobile-budget.v1.json');
43
+ const contractModulePath = path.join(repoRoot, 'scripts', 'mobile-budget-contract.js');
44
+ const slimVerifierPath = path.join(repoRoot, 'scripts', 'verify-mobile-slim-budget.js');
45
+ const artifactVerifierPath = path.join(repoRoot, 'scripts', 'verify-mobile-artifact.js');
46
+ const preparePath = path.join(repoRoot, 'scripts', 'prepare-mobile-slim.js');
47
+ const runtimeProjectionPath = path.join(repoRoot, 'src', 'frontend', 'mobile_budget_runtime.js');
48
+ test('declares matching low and standard artifact/RSS profiles plus runtime limits', () => {
49
+ const contract = JSON.parse(fs.readFileSync(contractPath, 'utf8'));
50
+ const loaded = require(contractModulePath);
51
+ const slimVerifier = require(slimVerifierPath);
52
+ const artifactVerifier = require(artifactVerifierPath);
53
+ expect(loaded.MOBILE_BUDGET_CONTRACT.schemaVersion).toBe(contract.schemaVersion);
54
+ expect(loaded.MOBILE_BUDGET_CONTRACT.profiles).toEqual(contract.profiles);
55
+ expect(loaded.MOBILE_BUDGET_CONTRACT.runtime).toEqual(contract.runtime);
56
+ expect(loaded.MOBILE_BUDGET_CONTRACT.contractSha256).toMatch(/^[a-f0-9]{64}$/);
57
+ expect(slimVerifier.DEFAULT_ASSET_BUDGET_BYTES).toBe(contract.profiles['mobile-low'].artifactCompressedBytes);
58
+ expect(slimVerifier.DEFAULT_MAX_RESIDENT_BYTES).toBe(contract.profiles['mobile-low'].maxResidentBytes);
59
+ expect(artifactVerifier.PROFILES['mobile-low'].compressedBudgetBytes).toBe(contract.profiles['mobile-low'].artifactCompressedBytes);
60
+ expect(artifactVerifier.PROFILES['mobile-standard'].maxResidentBytes).toBe(contract.profiles['mobile-standard'].maxResidentBytes);
61
+ expect(artifactVerifier.PROFILES['mobile-low'].maxDeviceRamBytes).toBe(contract.profiles['mobile-low'].maxDeviceRamBytes);
62
+ });
63
+ test('persists runtime budgets into the slim manifest and keeps the contract outside the runtime bundle', () => {
64
+ const prepareSource = fs.readFileSync(preparePath, 'utf8');
65
+ expect(prepareSource).toContain("require('./mobile-budget-contract')");
66
+ expect(prepareSource).toContain('contractSchemaVersion');
67
+ expect(prepareSource).toContain('runtime: MOBILE_BUDGET_CONTRACT.runtime');
68
+ expect(fs.readFileSync(contractPath, 'utf8')).not.toContain('mobile_exact_analyzer');
69
+ expect(prepareSource).toContain("profile: 'mobile-low'");
70
+ expect(prepareSource).toContain('contractSha256');
71
+ });
72
+ test('excludes Vite verification chunks from the mobile staging input', () => {
73
+ const prepare = require(preparePath);
74
+ expect(prepare.shouldExcludeSourceAsset('assets/main-stale.js')).toBe(true);
75
+ expect(prepare.shouldExcludeSourceAsset('vite-assets/main-stale.js')).toBe(true);
76
+ expect(prepare.shouldExcludeSourceAsset('workspace_panes.js')).toBe(false);
77
+ });
78
+ test('browser budget projection stays aligned with the versioned contract', () => {
79
+ const contract = JSON.parse(fs.readFileSync(contractPath, 'utf8'));
80
+ const context = {};
81
+ vm.runInNewContext(fs.readFileSync(runtimeProjectionPath, 'utf8'), context);
82
+ const runtime = context.NoteConnectionMobileBudget;
83
+ expect(runtime?.schemaVersion).toBe(contract.schemaVersion);
84
+ expect(runtime?.profiles).toEqual(contract.profiles);
85
+ expect(runtime?.runtime).toEqual(contract.runtime);
86
+ expect(MobileBudget_1.MOBILE_LOW_ARTIFACT_BUDGET_BYTES).toBe(contract.profiles['mobile-low'].artifactCompressedBytes);
87
+ expect(MobileBudget_1.MOBILE_LOW_MAX_RESIDENT_BYTES).toBe(contract.profiles['mobile-low'].maxResidentBytes);
88
+ expect(MobileBudget_1.MOBILE_LOW_MAX_DEVICE_RAM_BYTES).toBe(contract.profiles['mobile-low'].maxDeviceRamBytes);
89
+ });
90
+ });
@@ -0,0 +1,54 @@
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 path = __importStar(require("path"));
38
+ describe('canonical ID migration readiness boundary', () => {
39
+ const scriptPath = path.resolve(process.cwd(), 'scripts', 'verify-canonical-id-readiness.js');
40
+ const manifestPath = path.resolve(process.cwd(), 'config', 'identity-corpus.v1.json');
41
+ test('keeps readiness read-only and public-ID cutover blocked', () => {
42
+ const source = fs.readFileSync(scriptPath, 'utf8');
43
+ expect(source).toContain("canonicalPublicIdCutover: 'blocked'");
44
+ expect(source).toContain('independentReviewRequired: true');
45
+ expect(source).toContain("nativeDeviceEvidence === true");
46
+ expect(source).toContain("process.argv.includes('--strict')");
47
+ expect(source).not.toContain('migratePublicIds(');
48
+ expect(source).not.toContain('switchPublicIds(');
49
+ });
50
+ test('manifest keeps all compatibility changes false before review', () => {
51
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
52
+ expect(Object.values(manifest.compatibility)).toEqual([false, false, false, false]);
53
+ });
54
+ });