@githolon/dsl 0.108.7 → 0.110.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 (308) hide show
  1. package/assets/operation-contract.wasm +0 -0
  2. package/check_package.mjs +12 -15
  3. package/compile_package.mjs +17 -18
  4. package/compile_proof.mjs +16 -1
  5. package/dart/CHANGELOG.md +80 -1
  6. package/dart/README.md +25 -2
  7. package/dart/lib/src/admission_decision.dart +41 -0
  8. package/dart/lib/src/application_framework_calls.dart +52 -2
  9. package/dart/lib/src/application_model_adoption.dart +187 -0
  10. package/dart/lib/src/application_status.dart +73 -0
  11. package/dart/lib/src/console_enrolment.dart +271 -0
  12. package/dart/lib/src/console_http_io.dart +26 -0
  13. package/dart/lib/src/console_http_stub.dart +7 -0
  14. package/dart/lib/src/dev_cloud.dart +69 -22
  15. package/dart/lib/src/generated_call.dart +83 -23
  16. package/dart/lib/src/kernel_primitives.dart +119 -0
  17. package/dart/lib/src/nms_conformance_runtime.dart +1 -1
  18. package/dart/lib/src/nms_generated_runtime.dart +32 -10
  19. package/dart/lib/src/nms_test_environment.dart +1 -1
  20. package/dart/lib/src/node_runner_transport.dart +41 -46
  21. package/dart/lib/src/observed_session_directory.dart +70 -0
  22. package/dart/lib/src/operation_receipts.dart +112 -0
  23. package/dart/lib/src/peer_application_streams.dart +103 -0
  24. package/dart/lib/src/reception/dart_reception.dart +4280 -0
  25. package/dart/lib/src/reception/event_desk.dart +214 -0
  26. package/dart/lib/src/reception/extremum_read.dart +46 -0
  27. package/dart/lib/src/reception/local_copies.dart +371 -0
  28. package/dart/lib/src/reception/peer_office_connection.dart +11 -0
  29. package/dart/lib/src/reception/progress_desk.dart +268 -0
  30. package/dart/lib/src/reception/reception_contract.dart +11 -0
  31. package/dart/lib/src/reception/reception_session.dart +284 -0
  32. package/dart/lib/src/reception/sync_diagnostics.dart +26 -0
  33. package/dart/lib/src/reception/workspace_preparation_report.dart +126 -0
  34. package/dart/lib/src/replica_readiness.dart +123 -0
  35. package/dart/lib/src/runner/node-entry.mjs +23950 -15776
  36. package/dart/lib/src/runtime_v2_gateway.dart +56 -3
  37. package/dart/lib/src/runtime_v2_transport.dart +544 -3
  38. package/dart/lib/src/session_bound.dart +200 -311
  39. package/dart/lib/src/signing_identity_kernel.dart +84 -0
  40. package/dart/lib/src/sync_status_projection.dart +550 -0
  41. package/dart/lib/src/sync_status_reducer.dart +170 -0
  42. package/dart/lib/src/test_harness.dart +809 -1153
  43. package/dart/lib/src/testing/test_custody.dart +66 -0
  44. package/dart/lib/src/testing/test_peer.dart +328 -0
  45. package/dart/lib/src/typed_watch_registry.dart +169 -0
  46. package/dart/lib/src/workspace_open_progress.dart +27 -0
  47. package/dart/pubspec.lock +24 -1
  48. package/dart/pubspec.yaml +7 -2
  49. package/dart/pubspec_overrides.yaml +2 -0
  50. package/dart/test/admission_decision_test.dart +48 -0
  51. package/dart/test/background_model_adoption_test.dart +228 -0
  52. package/dart/test/bundle_version_pairing_test.dart +4 -4
  53. package/dart/test/child_authentication_repair_test.dart +359 -0
  54. package/dart/test/cloud_bound_local_shell_test.dart +119 -0
  55. package/dart/test/console_enrolment_refusal_test.dart +52 -0
  56. package/dart/test/console_enrolment_test.dart +227 -0
  57. package/dart/test/errors_test.dart +5 -5
  58. package/dart/test/event_plane_seam_test.dart +50 -164
  59. package/dart/test/extremum_read_test.dart +58 -0
  60. package/dart/test/fixtures/wedge/wedge-entry.mjs +3 -21
  61. package/dart/test/generated_query_test.dart +32 -0
  62. package/dart/test/harness_phase_timeout_test.dart +18 -23
  63. package/dart/test/kernel_author_after_read_test.dart +242 -0
  64. package/dart/test/kernel_fence_test.dart +137 -0
  65. package/dart/test/local_copies_test.dart +222 -0
  66. package/dart/test/model_adoption_test.dart +198 -0
  67. package/dart/test/node_peer_local_custody_test.dart +153 -0
  68. package/dart/test/offer_authentication_proof_test.dart +352 -52
  69. package/dart/test/operation_compatibility_test.dart +151 -0
  70. package/dart/test/platform_credential_effect_test.dart +386 -0
  71. package/dart/test/receiving_group_open_test.dart +213 -0
  72. package/dart/test/reception/workspace_preparation_report_test.dart +103 -0
  73. package/dart/test/reception_event_desk_close_test.dart +16 -0
  74. package/dart/test/reception_span_trace_test.dart +23 -0
  75. package/dart/test/replica_readiness_test.dart +156 -0
  76. package/dart/test/runtime_projections_test.dart +92 -0
  77. package/dart/test/runtime_v2_gateway_test.dart +131 -0
  78. package/dart/test/signing_identity_kernel_test.dart +75 -0
  79. package/dart/test/sync_status_preparation_report_test.dart +29 -0
  80. package/dart/test/sync_status_reducer_test.dart +419 -0
  81. package/dart/test/test_harness_test.dart +71 -95
  82. package/dart/test/typed_query_watch_test.dart +170 -0
  83. package/dart/test/typed_watch_registry_test.dart +249 -0
  84. package/dart_lints/README.md +18 -0
  85. package/dart_lints/lib/nomos_lints.dart +70 -0
  86. package/dart_lints/pubspec.lock +580 -0
  87. package/dart_lints/pubspec.yaml +12 -0
  88. package/dart_lints/test/reference_identity_test.dart +75 -0
  89. package/dart_types/CHANGELOG.md +18 -0
  90. package/dart_types/lib/nomos_kernel_protocol.dart +21 -1
  91. package/dart_types/lib/nomos_types.dart +9 -1
  92. package/dart_types/lib/src/admission_decision.dart +37 -0
  93. package/dart_types/lib/src/aggregate_meta.dart +1 -1
  94. package/dart_types/lib/src/approved_model.dart +65 -0
  95. package/dart_types/lib/src/custody.dart +10 -18
  96. package/dart_types/lib/src/error_info.dart +4 -4
  97. package/dart_types/lib/src/errors.dart +29 -29
  98. package/dart_types/lib/src/foreign_ref.dart +62 -33
  99. package/dart_types/lib/src/local_work_rejected.dart +3 -3
  100. package/dart_types/lib/src/nomos_ref.dart +3 -1
  101. package/dart_types/lib/src/principal.dart +2 -2
  102. package/dart_types/lib/src/proto/google/protobuf/any.pb.dart +163 -0
  103. package/dart_types/lib/src/proto/google/protobuf/any.pbenum.dart +11 -0
  104. package/dart_types/lib/src/proto/nomos/discovery/v1/workspace_placement.pb.dart +137 -0
  105. package/dart_types/lib/src/proto/nomos/discovery/v1/workspace_placement.pbenum.dart +11 -0
  106. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pb.dart +20721 -10433
  107. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pbenum.dart +555 -27
  108. package/dart_types/lib/src/proto/nomos/runtime/v2/application_desk.pb.dart +387 -0
  109. package/dart_types/lib/src/proto/nomos/runtime/v2/application_desk.pbenum.dart +46 -0
  110. package/dart_types/lib/src/proto/nomos/runtime/v2/application_readiness_desk.pb.dart +295 -0
  111. package/dart_types/lib/src/proto/nomos/runtime/v2/application_readiness_desk.pbenum.dart +139 -0
  112. package/dart_types/lib/src/proto/nomos/runtime/v2/authentication_desk.pb.dart +269 -0
  113. package/dart_types/lib/src/proto/nomos/runtime/v2/authentication_desk.pbenum.dart +103 -0
  114. package/dart_types/lib/src/proto/nomos/runtime/v2/compatibility_desk.pb.dart +417 -0
  115. package/dart_types/lib/src/proto/nomos/runtime/v2/compatibility_desk.pbenum.dart +62 -0
  116. package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pb.dart +9968 -0
  117. package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pbenum.dart +133 -0
  118. package/dart_types/lib/src/proto/nomos/runtime/v2/credentials_desk.pb.dart +204 -0
  119. package/dart_types/lib/src/proto/nomos/runtime/v2/credentials_desk.pbenum.dart +54 -0
  120. package/dart_types/lib/src/proto/nomos/runtime/v2/custody_desk.pb.dart +1550 -0
  121. package/dart_types/lib/src/proto/nomos/runtime/v2/custody_desk.pbenum.dart +11 -0
  122. package/dart_types/lib/src/proto/nomos/runtime/v2/framework.pb.dart +710 -0
  123. package/dart_types/lib/src/proto/nomos/runtime/v2/framework.pbenum.dart +11 -0
  124. package/dart_types/lib/src/proto/nomos/runtime/v2/identity_desk.pb.dart +845 -0
  125. package/dart_types/lib/src/proto/nomos/runtime/v2/identity_desk.pbenum.dart +11 -0
  126. package/dart_types/lib/src/proto/nomos/runtime/v2/interaction_desk.pb.dart +198 -0
  127. package/dart_types/lib/src/proto/nomos/runtime/v2/interaction_desk.pbenum.dart +11 -0
  128. package/dart_types/lib/src/proto/nomos/runtime/v2/law_desk.pb.dart +369 -0
  129. package/dart_types/lib/src/proto/nomos/runtime/v2/law_desk.pbenum.dart +11 -0
  130. package/dart_types/lib/src/proto/nomos/runtime/v2/maintenance_desk.pb.dart +609 -0
  131. package/dart_types/lib/src/proto/nomos/runtime/v2/maintenance_desk.pbenum.dart +65 -0
  132. package/dart_types/lib/src/proto/nomos/runtime/v2/model_adoption_desk.pb.dart +358 -0
  133. package/dart_types/lib/src/proto/nomos/runtime/v2/model_adoption_desk.pbenum.dart +100 -0
  134. package/dart_types/lib/src/proto/nomos/runtime/v2/observability_desk.pb.dart +2231 -0
  135. package/dart_types/lib/src/proto/nomos/runtime/v2/observability_desk.pbenum.dart +271 -0
  136. package/dart_types/lib/src/proto/nomos/runtime/v2/placement_desk.pb.dart +1286 -0
  137. package/dart_types/lib/src/proto/nomos/runtime/v2/placement_desk.pbenum.dart +11 -0
  138. package/dart_types/lib/src/proto/nomos/runtime/v2/receiving_group_desk.pb.dart +636 -0
  139. package/dart_types/lib/src/proto/nomos/runtime/v2/receiving_group_desk.pbenum.dart +116 -0
  140. package/dart_types/lib/src/proto/nomos/runtime/v2/records_desk.pb.dart +1637 -0
  141. package/dart_types/lib/src/proto/nomos/runtime/v2/records_desk.pbenum.dart +175 -0
  142. package/dart_types/lib/src/proto/nomos/runtime/v2/recovery_desk.pb.dart +1269 -0
  143. package/dart_types/lib/src/proto/nomos/runtime/v2/recovery_desk.pbenum.dart +129 -0
  144. package/dart_types/lib/src/proto/nomos/runtime/v2/residency_desk.pb.dart +2782 -0
  145. package/dart_types/lib/src/proto/nomos/runtime/v2/residency_desk.pbenum.dart +97 -0
  146. package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pb.dart +2232 -8627
  147. package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pbenum.dart +0 -358
  148. package/dart_types/lib/src/proto/nomos/runtime/v2/sealing_desk.pb.dart +1639 -0
  149. package/dart_types/lib/src/proto/nomos/runtime/v2/sealing_desk.pbenum.dart +46 -0
  150. package/dart_types/lib/src/runtime_outcome.dart +133 -0
  151. package/dart_types/lib/src/subscriptions.dart +116 -23
  152. package/dart_types/pubspec.yaml +1 -1
  153. package/dart_types/test/extremum_subscriptions_test.dart +40 -0
  154. package/dart_types/test/foreign_ref_resolution_test.dart +75 -0
  155. package/dart_types/test/nomos_ref_address_test.dart +25 -0
  156. package/dart_types/test/ordered_subscriptions_test.dart +34 -0
  157. package/operation_contract_check.mjs +24 -0
  158. package/package.json +11 -6
  159. package/src/aggregate.ts +70 -10
  160. package/src/application_topology.ts +18 -11
  161. package/src/attested.ts +3 -0
  162. package/src/attested_core.ts +46 -3
  163. package/src/birth_meter_ensure.ts +19 -7
  164. package/src/births.ts +29 -4
  165. package/src/build_package.ts +140 -13
  166. package/src/business_birth.ts +181 -4
  167. package/src/codegen_call_proto.ts +1276 -0
  168. package/src/codegen_call_usda.ts +49 -6
  169. package/src/codegen_dart.ts +83 -44
  170. package/src/codegen_dart_client.ts +458 -800
  171. package/src/{codegen_dart_application.ts → codegen_dart_facade.ts} +331 -582
  172. package/src/codegen_dart_framework_calls.ts +320 -37
  173. package/src/codegen_dart_indexed_variant.ts +159 -0
  174. package/src/codegen_dart_scenario.ts +42 -13
  175. package/src/codegen_proof.ts +3 -3
  176. package/src/codegen_provider_dart.ts +5 -0
  177. package/src/codegen_query_proto.ts +158 -0
  178. package/src/codegen_refusal.ts +189 -0
  179. package/src/codegen_result_proto.ts +931 -0
  180. package/src/codegen_result_usda.ts +4 -0
  181. package/src/codegen_ts.ts +464 -616
  182. package/src/codegen_workspace_access.ts +105 -0
  183. package/src/compatibility_graph.ts +759 -0
  184. package/src/compatibility_usda.ts +264 -0
  185. package/src/compile_package_main.ts +2017 -1195
  186. package/src/custody_placement.ts +156 -0
  187. package/src/custody_placement_usda.ts +91 -0
  188. package/src/decl_shapes.ts +21 -2
  189. package/src/describe_model.ts +1 -0
  190. package/src/directive.ts +59 -2
  191. package/src/drivers.ts +51 -0
  192. package/src/engine_entry.ts +52 -8
  193. package/src/evolve_preview.ts +11 -4
  194. package/src/exists.ts +2 -0
  195. package/src/fields.ts +75 -11
  196. package/src/flow_gate.ts +2 -1
  197. package/src/framework/application_home.ts +170 -18
  198. package/src/framework/attested_receipts.ts +8 -3
  199. package/src/framework/auth_provider_law.ts +169 -9
  200. package/src/framework/birth_certificate_law.ts +4 -0
  201. package/src/framework/capability.ts +34 -3
  202. package/src/framework/child_authentication.ts +346 -0
  203. package/src/framework/console_governance.ts +295 -0
  204. package/src/framework/delegated_roles.ts +301 -0
  205. package/src/framework/domain_lifecycle.ts +26 -13
  206. package/src/framework/founding_genesis.ts +193 -0
  207. package/src/framework/framework_aggregates.ts +1 -1
  208. package/src/framework/genesis.ts +6 -1
  209. package/src/framework/governance_relations.ts +19 -0
  210. package/src/framework/home.ts +5 -734
  211. package/src/framework/legacy/child_auth_provider_epoch.ts +73 -0
  212. package/src/framework/model_authority_repair.ts +116 -0
  213. package/src/framework/model_authority_seed.ts +185 -0
  214. package/src/framework/model_transition.ts +211 -0
  215. package/src/framework/owned_custody.ts +1 -1
  216. package/src/framework/principal_enrollment.ts +302 -0
  217. package/src/framework/receiving_group.ts +857 -0
  218. package/src/framework/receiving_group_recovery.ts +314 -0
  219. package/src/framework/relations.ts +4 -8
  220. package/src/framework/signing_identity.ts +242 -95
  221. package/src/framework/source_roster.ts +11 -5
  222. package/src/framework/workspace_access.ts +149 -0
  223. package/src/framework/workspace_invariant.ts +73 -52
  224. package/src/framework/workspaces.ts +407 -40
  225. package/src/framework_call_definitions.ts +32 -3
  226. package/src/generated_client_bundle_boundary.ts +97 -0
  227. package/src/generated_client_names.ts +1 -0
  228. package/src/identity_usda.ts +27 -0
  229. package/src/index.ts +183 -12
  230. package/src/indexed_variant.ts +83 -0
  231. package/src/intent.ts +173 -45
  232. package/src/law_check.ts +8 -5
  233. package/src/law_dependency_index.ts +231 -0
  234. package/src/law_graph.ts +508 -71
  235. package/src/law_graph_usda.ts +170 -24
  236. package/src/law_identity.ts +119 -81
  237. package/src/law_source_map.ts +4 -1
  238. package/src/law_units.ts +498 -0
  239. package/src/law_value_type.ts +7 -3
  240. package/src/member_authentication.ts +302 -0
  241. package/src/null_meta.ts +1 -1
  242. package/src/openusd_compiled_layer.ts +132 -0
  243. package/src/operation_contract_check.ts +31 -0
  244. package/src/ordered_read.ts +2 -1
  245. package/src/package_config.ts +33 -12
  246. package/src/package_dependencies.ts +1 -0
  247. package/src/plan_source_slice.ts +30 -1
  248. package/src/protobuf_field_number.ts +36 -0
  249. package/src/provenance_wire.ts +6 -0
  250. package/src/query.ts +2 -1
  251. package/src/read.ts +50 -0
  252. package/src/relation.ts +4 -1
  253. package/src/relation_schema.ts +15 -2
  254. package/src/report.ts +2 -2
  255. package/src/sqlite_computation_bundle.ts +78 -0
  256. package/src/sqlite_computed.ts +102 -0
  257. package/src/sqlite_counts.ts +154 -0
  258. package/src/sqlite_layout.ts +823 -0
  259. package/src/sqlite_reads.ts +275 -0
  260. package/src/sqlite_samples.ts +185 -0
  261. package/src/stable_id_identity_bridge.ts +1 -1
  262. package/src/stable_ids_for_module.ts +16 -4
  263. package/src/test_call_usda.ts +6 -6
  264. package/src/usda_relationship_integrity.ts +20 -3
  265. package/src/usda_text.ts +21 -1
  266. package/src/value_object.ts +81 -11
  267. package/src/wire.ts +22 -0
  268. package/src/wire_encode.ts +227 -20
  269. package/src/workspace_sharding.ts +15 -6
  270. package/src/workspace_sharing.ts +51 -49
  271. package/src/zod_introspection.ts +2 -0
  272. package/compat_package.mjs +0 -46
  273. package/dart/lib/src/home.dart +0 -15
  274. package/dart/lib/src/runner/offline-install.mjs +0 -15367
  275. package/dart/lib/src/source_trust.dart +0 -51
  276. package/dart/lib/src/webview_bridge.dart +0 -6540
  277. package/dart/lib/src/workspace_name.dart +0 -30
  278. package/dart/test/acquire_session_credentials_test.dart +0 -150
  279. package/dart/test/acquire_session_test.dart +0 -534
  280. package/dart/test/adv_realm_interference_test.dart +0 -600
  281. package/dart/test/cloud_bound_topology_test.dart +0 -168
  282. package/dart/test/cluster_births_test.dart +0 -202
  283. package/dart/test/cluster_device_keyed_acceptance_test.dart +0 -105
  284. package/dart/test/cluster_local_autosync_test.dart +0 -123
  285. package/dart/test/cluster_scale_test.dart +0 -122
  286. package/dart/test/compat_test.dart +0 -61
  287. package/dart/test/dlq_session_surface_test.dart +0 -222
  288. package/dart/test/first_app_gauntlet_test.dart +0 -386
  289. package/dart/test/fixtures/wedge/fake-install.mjs +0 -11
  290. package/dart/test/four_layer_nested_topology_test.dart +0 -290
  291. package/dart/test/home_parity_test.dart +0 -13
  292. package/dart/test/local_sync_inert_test.dart +0 -107
  293. package/dart/test/node_runner_transport_test.dart +0 -78
  294. package/dart/test/parent_signer_recovery_test.dart +0 -155
  295. package/dart/test/residency_refusal_test.dart +0 -49
  296. package/dart/test/sealing_client_test.dart +0 -181
  297. package/dart/test/session_bound_test.dart +0 -285
  298. package/dart/test/stale_bundle_test.dart +0 -80
  299. package/dart/test/vo_read_typing_test.dart +0 -131
  300. package/dart/test/watch_after_offer_stall_test.dart +0 -100
  301. package/dart/test/webview_bridge_test.dart +0 -1930
  302. package/dart/test/workspace_name_test.dart +0 -24
  303. package/runtime/generated-application-host.js +0 -22860
  304. package/src/codegen_query_usda.ts +0 -214
  305. package/src/compat_manifest.ts +0 -26
  306. package/src/compat_package_main.ts +0 -224
  307. package/src/shape_compat.ts +0 -352
  308. package/src/usd_state.ts +0 -505
@@ -0,0 +1,823 @@
1
+ /** Experimental, disposable SQLite projection plan. NOT law or custody.
2
+ * One traversal emits access offsets, DDL and compatibility costs. Only opted-in
3
+ * experiments consume this; unsupported shapes fail, never silently become JSON.
4
+ */
5
+ import { createHash } from "node:crypto";
6
+ import type { LawGraph, LawValueTypeRef } from "./law_graph.js";
7
+ import {
8
+ planSqliteComputations,
9
+ type SqliteComputedPlan,
10
+ } from "./sqlite_computed.js";
11
+ import {
12
+ planSqliteCounts,
13
+ type SqliteCountPlan,
14
+ type SqliteCountSnapshot,
15
+ } from "./sqlite_counts.js";
16
+ import {
17
+ inspectSqliteReads,
18
+ planSqliteQueries,
19
+ type SqliteQueryPlan,
20
+ } from "./sqlite_reads.js";
21
+
22
+ export interface SqliteColumn {
23
+ name: string;
24
+ type: "TEXT" | "INTEGER" | "BLOB";
25
+ }
26
+ export interface SqliteSlot {
27
+ name: string;
28
+ sid: string;
29
+ presence: number; // 0 absent, 1 explicit null, 2 value
30
+ required: boolean;
31
+ nullable: boolean;
32
+ metadata: number[]; // physical, logical, replica, author, contested, schema
33
+ value: SqliteValue;
34
+ surfaced?: SqliteValue;
35
+ }
36
+ export interface SqliteValue {
37
+ kind:
38
+ | "string"
39
+ | "integer"
40
+ | "boolean"
41
+ | "number"
42
+ | "null"
43
+ | "object"
44
+ | "map"
45
+ | "set"
46
+ | "list"
47
+ | "nominal-map"
48
+ | "union"
49
+ | "reference"
50
+ | "versioned-reference";
51
+ column?: number;
52
+ typeName?: string;
53
+ members?: SqliteSlot[];
54
+ table?: string;
55
+ columns?: SqliteColumn[];
56
+ element?: SqliteSlot;
57
+ variants?: { label: string; value: SqliteValue }[];
58
+ literal?: string | number | boolean | null;
59
+ }
60
+ export interface SqliteLayout {
61
+ version: 2;
62
+ aggregateSid: string;
63
+ wireType: string;
64
+ table: string;
65
+ columns: SqliteColumn[];
66
+ fields: SqliteSlot[];
67
+ indexColumn: number;
68
+ spatialIndexes: { sid: string; name: string; path: string[] }[];
69
+ queryReads: SqliteQueryPlan[];
70
+ queryRefusals: { sid: string; reason: string }[];
71
+ countReads: SqliteCountPlan[];
72
+ countRefusals: { sid: string; reason: string }[];
73
+ countSnapshot: SqliteCountSnapshot | null;
74
+ computedReads: SqliteComputedPlan[];
75
+ computedRefusals: { sid: string; reason: string }[];
76
+ ddl: string;
77
+ fingerprint: string;
78
+ budget: {
79
+ columnLimit: number;
80
+ tables: { name: string; columns: number }[];
81
+ maxColumns: number;
82
+ totalColumns: number;
83
+ indexes: number;
84
+ queryIndexes: { name: string; keyColumns: number; parameters: number }[];
85
+ computedSubstrate: {
86
+ declarations: number;
87
+ sqlTemporalReads: number;
88
+ sealedEngineReads: number;
89
+ storedColumns: 0;
90
+ queueTables: 0;
91
+ };
92
+ countSubstrate: {
93
+ declarations: number;
94
+ tables: { name: string; columns: number }[];
95
+ snapshotColumns: number;
96
+ snapshotParameters: number;
97
+ };
98
+ // Shared once per database, not multiplied by aggregate or declaration.
99
+ spatialSubstrate: {
100
+ declarations: number;
101
+ tables: { name: string; columns: number }[];
102
+ };
103
+ statementsPerPage: number;
104
+ maxJoinTables: number;
105
+ maxResultColumns: number;
106
+ maxInsertParameters: number;
107
+ warnings: string[];
108
+ };
109
+ }
110
+
111
+ export function planSqliteLayout(
112
+ closure: readonly LawGraph[],
113
+ aggregateSid: string,
114
+ indexFieldSid: string,
115
+ options: { columnLimit?: number; warnColumns?: number } = {}
116
+ ): SqliteLayout {
117
+ return planWithNodes(
118
+ new Map(
119
+ closure.flatMap((g) => g.nodes.map((n) => [n.entity.id, n] as const))
120
+ ),
121
+ aggregateSid,
122
+ indexFieldSid,
123
+ options
124
+ );
125
+ }
126
+
127
+ // Reuse the graph index for whole-package inspection. The private field probe is
128
+ // diagnostics only; it is never emitted as an executable full-aggregate layout.
129
+ function planWithNodes(
130
+ nodes: ReadonlyMap<string, LawGraph["nodes"][number]>,
131
+ aggregateSid: string,
132
+ indexFieldSid: string,
133
+ options: { columnLimit?: number; warnColumns?: number } = {},
134
+ onlyFieldSid?: string
135
+ ): SqliteLayout {
136
+ const columnLimit = options.columnLimit ?? 2000;
137
+ // Target compatibility, not SQLITE_MAX_COLUMN's theoretical configurable ceiling.
138
+ if (!Number.isInteger(columnLimit) || columnLimit < 1 || columnLimit > 2000)
139
+ throw new Error("SQLite experimental target column limit must be 1..2000");
140
+ const aggregate = [...nodes.values()].find(
141
+ (n) => n.kind === "aggregate" && n.entity.sid === aggregateSid
142
+ );
143
+ if (aggregate?.kind !== "aggregate")
144
+ throw new Error(`unknown aggregate SID ${aggregateSid}`);
145
+ const identifier = (sid: string) => {
146
+ if (!/^[a-f0-9]+$/i.test(sid))
147
+ throw new Error(`unsafe stable identity ${sid}`);
148
+ return sid.toLowerCase();
149
+ };
150
+ const table = `a_${identifier(aggregateSid)}`;
151
+ const spatialIndexes = [...nodes.values()]
152
+ .flatMap((n) => {
153
+ if (
154
+ n.kind !== "read" ||
155
+ n.readKind !== "spatial" ||
156
+ n.of.id !== aggregate.entity.id
157
+ )
158
+ return [];
159
+ if (n.predicate)
160
+ throw new Error(
161
+ `spatial ${n.entity.label}: filtered index mapping is not supported`
162
+ );
163
+ return [
164
+ {
165
+ sid: n.entity.sid,
166
+ name: n.entity.label,
167
+ path: n.geometry.authoredPath.split("."),
168
+ },
169
+ ];
170
+ })
171
+ .sort((a, b) => a.sid.localeCompare(b.sid));
172
+ const tables: { name: string; columns: SqliteColumn[]; child: boolean }[] =
173
+ [];
174
+ const rootColumns: SqliteColumn[] = [{ name: "id", type: "TEXT" }];
175
+ tables.push({ name: table, columns: rootColumns, child: false });
176
+ const column = (columns: SqliteColumn[], type: SqliteColumn["type"]) => {
177
+ const offset = columns.length;
178
+ columns.push({ name: `c${offset}`, type });
179
+ // Stop expansion promptly; do not build a million-column graph just to report it.
180
+ if (columns.length > columnLimit)
181
+ throw new Error(
182
+ `SQLite layout ${aggregate.entity.label}: table exceeds ${columnLimit} columns (including metadata/presence)`
183
+ );
184
+ return offset;
185
+ };
186
+ const slot = (
187
+ name: string,
188
+ sid: string,
189
+ columns: SqliteColumn[],
190
+ required: boolean,
191
+ nullable: boolean,
192
+ driven: boolean,
193
+ build: () => SqliteValue
194
+ ): SqliteSlot => ({
195
+ name,
196
+ sid,
197
+ required,
198
+ nullable,
199
+ presence: column(columns, "INTEGER"),
200
+ metadata: driven
201
+ ? (["BLOB", "INTEGER", "BLOB", "TEXT", "INTEGER", "TEXT"] as const).map(
202
+ (t) => column(columns, t)
203
+ )
204
+ : [],
205
+ value: (() => {
206
+ try {
207
+ return build();
208
+ } catch (error) {
209
+ throw new Error(`${name}: ${(error as Error).message}`);
210
+ }
211
+ })(),
212
+ });
213
+ // Each finite schema path owns a table. Instance ancestry is a length-prefixed
214
+ // parent key, not a JSON path; nested lists/maps never expand root row width.
215
+ function collection(
216
+ kind: SqliteValue["kind"],
217
+ build: (c: SqliteColumn[]) => SqliteSlot
218
+ ): SqliteValue {
219
+ const name = `${table}_n${tables.length}`;
220
+ const columns: SqliteColumn[] = [
221
+ { name: "id", type: "TEXT" },
222
+ { name: "key", type: "TEXT" },
223
+ { name: "parent", type: "TEXT" },
224
+ ];
225
+ tables.push({ name, columns, child: true });
226
+ return { kind, table: name, columns, element: build(columns) };
227
+ }
228
+ const isNullable = (ref: LawValueTypeRef): boolean => {
229
+ const n = nodes.get(ref.id);
230
+ return (
231
+ n?.kind === "structural" &&
232
+ n.structuralKind === "value-type" &&
233
+ (n.expression.kind === "nullable" ||
234
+ (n.expression.kind === "default" && isNullable(n.expression.value)))
235
+ );
236
+ };
237
+ function value(
238
+ ref: LawValueTypeRef,
239
+ columns: SqliteColumn[],
240
+ active: Set<string>,
241
+ nominalName?: string
242
+ ): SqliteValue {
243
+ if (active.has(ref.id))
244
+ throw new Error(
245
+ "SQLite layout: recursive shape requires a separate mapping experiment"
246
+ );
247
+ const next = new Set(active).add(ref.id);
248
+ const node = nodes.get(ref.id);
249
+ if (node?.kind === "type") {
250
+ if (node.form === "geometry") {
251
+ // Geometry's public encoder emits Int for integral coordinates, Double
252
+ // otherwise. A tagged numeric leaf retains both (not coercion to REAL).
253
+ const coords = (c: SqliteColumn[], depth: number): SqliteValue =>
254
+ collection("list", (inner) =>
255
+ slot("element", node.entity.id, inner, true, false, false, () =>
256
+ depth === 1
257
+ ? {
258
+ kind: "number",
259
+ column: column(inner, "BLOB"),
260
+ typeName: "geometry-number",
261
+ }
262
+ : coords(inner, depth - 1)
263
+ )
264
+ );
265
+ return {
266
+ kind: "object",
267
+ typeName: node.entity.label,
268
+ members: [
269
+ slot("type", node.entity.id, columns, true, false, false, () => ({
270
+ kind: "string",
271
+ column: column(columns, "TEXT"),
272
+ literal: node.geometry!,
273
+ })),
274
+ slot(
275
+ "coordinates",
276
+ node.entity.id,
277
+ columns,
278
+ true,
279
+ false,
280
+ false,
281
+ () =>
282
+ coords(
283
+ columns,
284
+ node.geometry === "Point"
285
+ ? 1
286
+ : node.geometry === "Polygon"
287
+ ? 3
288
+ : 2
289
+ )
290
+ ),
291
+ ],
292
+ };
293
+ }
294
+ if (node.form === "sum")
295
+ return {
296
+ kind: "union",
297
+ typeName: node.entity.label,
298
+ column: column(columns, "INTEGER"),
299
+ variants: node.variants.map((r) => {
300
+ const arm = nodes.get(r.id);
301
+ if (
302
+ arm?.kind !== "structural" ||
303
+ arm.structuralKind !== "value-variant" ||
304
+ arm.label === undefined
305
+ )
306
+ throw new Error("missing nominal arm");
307
+ const target = nodes.get(arm.value.id);
308
+ const body =
309
+ target?.kind === "type"
310
+ ? {
311
+ kind: "object" as const,
312
+ typeName: node.entity.label,
313
+ members: [
314
+ slot(
315
+ "value",
316
+ arm.entity.id,
317
+ columns,
318
+ true,
319
+ false,
320
+ false,
321
+ () => value(arm.value, columns, next)
322
+ ),
323
+ ],
324
+ }
325
+ : value(arm.value, columns, next, node.entity.label);
326
+ return { label: arm.label, value: body };
327
+ }),
328
+ };
329
+ return {
330
+ kind: "object",
331
+ typeName: node.entity.label,
332
+ members: node.fields.map((r) => {
333
+ const f = nodes.get(r.id);
334
+ if (f?.kind !== "field")
335
+ throw new Error(`missing type member ${r.id}`);
336
+ return slot(
337
+ f.entity.label,
338
+ f.entity.sid,
339
+ columns,
340
+ f.required,
341
+ f.nullable,
342
+ false,
343
+ () => value(f.value, columns, next)
344
+ );
345
+ }),
346
+ };
347
+ }
348
+ if (node?.kind !== "structural" || node.structuralKind !== "value-type")
349
+ throw new Error(`missing value type ${ref.id}`);
350
+ const e = node.expression;
351
+ switch (e.kind) {
352
+ case "default":
353
+ case "nullable":
354
+ return value(e.value, columns, next);
355
+ case "string":
356
+ case "enum":
357
+ return { kind: "string", column: column(columns, "TEXT") };
358
+ case "integer":
359
+ return { kind: "integer", column: column(columns, "INTEGER") };
360
+ case "boolean":
361
+ return { kind: "boolean", column: column(columns, "INTEGER") };
362
+ // IEEE bits retain negative zero; no text decode, no SQLite numeric coercion.
363
+ case "number":
364
+ return { kind: "number", column: column(columns, "BLOB") };
365
+ case "null":
366
+ return { kind: "null" };
367
+ case "literal": {
368
+ if (typeof e.value === "string")
369
+ return {
370
+ kind: "string",
371
+ column: column(columns, "TEXT"),
372
+ literal: e.value,
373
+ };
374
+ if (typeof e.value === "boolean")
375
+ return { kind: "boolean", column: column(columns, "INTEGER") };
376
+ throw new Error("SQLite layout: unsupported literal representation");
377
+ }
378
+ case "record": {
379
+ const typeName =
380
+ e.semantic?.kind === "value-object" ? e.semantic.name : nominalName;
381
+ const reference =
382
+ e.semantic?.kind === "address-reference" ||
383
+ e.semantic?.kind === "versioned-reference";
384
+ if (typeName === undefined && !reference)
385
+ throw new Error(
386
+ "SQLite layout: anonymous record needs an explicit nominal storage mapping"
387
+ );
388
+ const result: SqliteValue = {
389
+ kind: reference
390
+ ? e.semantic!.kind === "versioned-reference"
391
+ ? "versioned-reference"
392
+ : "reference"
393
+ : "object",
394
+ ...(typeName === undefined ? {} : { typeName }),
395
+ members: e.fields.map((r) => {
396
+ const f = nodes.get(r.id);
397
+ if (f?.kind !== "structural" || f.structuralKind !== "value-field")
398
+ throw new Error(`missing record member ${r.id}`);
399
+ return slot(
400
+ f.name,
401
+ f.entity.id,
402
+ columns,
403
+ f.required,
404
+ isNullable(f.value),
405
+ false,
406
+ () => value(f.value, columns, next)
407
+ );
408
+ }),
409
+ };
410
+ if (e.semantic?.kind === "value-object" && e.semantic.geometry) {
411
+ const mark = (s: SqliteValue) => {
412
+ if (s.kind === "number") s.typeName = "geometry-number";
413
+ s.members?.forEach((m) => mark(m.value));
414
+ if (s.element) mark(s.element.value);
415
+ };
416
+ mark(result);
417
+ }
418
+ return result;
419
+ }
420
+ case "list":
421
+ case "map": {
422
+ const child = e.kind === "list" ? e.element : e.value;
423
+ return collection(e.kind === "list" ? "list" : "nominal-map", (c) =>
424
+ slot("element", ref.id, c, true, isNullable(child), false, () =>
425
+ value(child, c, next)
426
+ )
427
+ );
428
+ }
429
+ case "sum": {
430
+ // Descriptor-lowered named sums carry authored `type` fields in their
431
+ // structural arms, unlike nominal LawTypeNode variants. Preserve both.
432
+ if (e.semantic?.kind !== "value-object")
433
+ throw new Error("unsupported anonymous union storage");
434
+ const name = e.semantic.name;
435
+ return {
436
+ kind: "union",
437
+ typeName: name,
438
+ column: column(columns, "INTEGER"),
439
+ variants: e.variants.map((r) => {
440
+ const arm = nodes.get(r.id);
441
+ if (
442
+ arm?.kind !== "structural" ||
443
+ arm.structuralKind !== "value-variant"
444
+ )
445
+ throw new Error("missing sum arm");
446
+ const body = value(arm.value, columns, next, name);
447
+ const discriminator = body.members?.find((s) => s.name === "type")
448
+ ?.value.literal;
449
+ if (typeof discriminator !== "string")
450
+ throw new Error("named union has no literal discriminator");
451
+ return { label: discriminator, value: body };
452
+ }),
453
+ };
454
+ }
455
+ default:
456
+ throw new Error(
457
+ `SQLite layout: unsupported ${e.kind}; no JSON fallback`
458
+ );
459
+ }
460
+ }
461
+ const fields = [
462
+ slot("__type", "builtin:type", rootColumns, true, false, true, () => ({
463
+ kind: "string",
464
+ column: column(rootColumns, "TEXT"),
465
+ })),
466
+ ];
467
+ for (const r of aggregate.fields) {
468
+ const f = nodes.get(r.id);
469
+ if (f?.kind !== "field") throw new Error(`missing aggregate field ${r.id}`);
470
+ if (onlyFieldSid !== undefined && f.entity.sid !== onlyFieldSid) continue;
471
+ if (
472
+ // Explicit top-level null is a different kernel Value representation;
473
+ // this slice only models null inside nominal records.
474
+ f.encrypted ||
475
+ f.blindIndexed ||
476
+ f.mergeDrivers.some(
477
+ (d) =>
478
+ d.kind !== "Lww" &&
479
+ d.kind !== "FirstWriterWins" &&
480
+ d.kind !== "FirstWriterCandidates" &&
481
+ d.kind !== "MapOf" &&
482
+ d.kind !== "AddWins" &&
483
+ d.kind !== "Surfaced"
484
+ )
485
+ ) {
486
+ throw new Error(
487
+ `SQLite layout: unsupported storage/driver on ${f.entity.label} ` +
488
+ `(kind=${f.storageKind}; drivers=${f.mergeDrivers
489
+ .map((d) => d.kind)
490
+ .join("/")}; ` +
491
+ `nullable=${f.nullable}; encrypted=${f.encrypted}; blindIndexed=${f.blindIndexed})`
492
+ );
493
+ }
494
+ fields.push(
495
+ slot(
496
+ f.entity.label,
497
+ f.entity.sid,
498
+ rootColumns,
499
+ f.required,
500
+ f.nullable,
501
+ true,
502
+ () => {
503
+ if (f.storageKind === "set" || f.storageKind === "map") {
504
+ const type = nodes.get(f.value.id);
505
+ if (
506
+ type?.kind !== "structural" ||
507
+ type.structuralKind !== "value-type"
508
+ )
509
+ throw new Error("missing collection value type");
510
+ const e = type.expression;
511
+ const child =
512
+ e.kind === "list"
513
+ ? e.element
514
+ : e.kind === "map"
515
+ ? e.value
516
+ : undefined;
517
+ if (!child) throw new Error("unsupported collection shape");
518
+ if (
519
+ f.storageKind === "map" &&
520
+ !["string", "int", "bool", "json"].includes(f.mapValueKind ?? "")
521
+ )
522
+ throw new Error("unsupported driven map value kind");
523
+ return collection(f.storageKind, (columns) =>
524
+ slot(
525
+ "element",
526
+ f.entity.sid,
527
+ columns,
528
+ true,
529
+ false,
530
+ f.storageKind === "map",
531
+ () => value(child, columns, new Set())
532
+ )
533
+ );
534
+ }
535
+ if (
536
+ // storedFieldValue lowers ordinary ID references to string. Atomic
537
+ // address/versioned refs retain their structural shape and still
538
+ // pass through value(), which refuses an unsupported mapping.
539
+ !["string", "int", "bool", "enum", "json", "ref"].includes(
540
+ f.storageKind ?? ""
541
+ )
542
+ )
543
+ throw new Error(`unsupported field storage kind ${f.storageKind}`);
544
+ return value(f.value, rootColumns, new Set());
545
+ }
546
+ )
547
+ );
548
+ if (f.mergeDrivers.some((d) => d.kind === "Surfaced")) {
549
+ fields.at(-1)!.surfaced = collection("list", (columns) => {
550
+ // observation stamp + principal + authored law + schema are independent
551
+ // of outer Field provenance. Six dedicated columns, never a winner-only view.
552
+ const s = slot(
553
+ "observation",
554
+ f.entity.sid,
555
+ columns,
556
+ true,
557
+ f.nullable,
558
+ true,
559
+ () => value(f.value, columns, new Set())
560
+ );
561
+ columns[s.metadata[4]!]!.type = "TEXT"; // authored_under, not was_contested
562
+ return s;
563
+ });
564
+ }
565
+ }
566
+ const indexed = fields.find((f) => f.sid === indexFieldSid);
567
+ const hasReads = [...nodes.values()].some(
568
+ (n) =>
569
+ n.kind === "read" &&
570
+ (n.of.id === aggregate.entity.id ||
571
+ (n.readKind === "combined" && n.reads.id === aggregate.entity.id))
572
+ );
573
+ if (hasReads && onlyFieldSid === undefined) {
574
+ // Kernel archive metadata is retained, never translated to a tenant field.
575
+ fields.push(
576
+ slot(
577
+ "__archived",
578
+ "builtin:archived",
579
+ rootColumns,
580
+ false,
581
+ false,
582
+ true,
583
+ () => ({ kind: "integer", column: column(rootColumns, "INTEGER") })
584
+ )
585
+ );
586
+ for (const index of spatialIndexes) {
587
+ let members = fields;
588
+ let shape: SqliteValue | undefined;
589
+ for (const name of index.path) {
590
+ const member = members.find((f) => f.name === name);
591
+ if (!member)
592
+ throw new Error(
593
+ `spatial ${index.name}: unmapped path ${index.path.join(".")}`
594
+ );
595
+ shape = member.value;
596
+ members = shape.members ?? [];
597
+ }
598
+ if (
599
+ shape?.kind !== "object" ||
600
+ !shape.members?.some((m) => m.name === "coordinates")
601
+ )
602
+ throw new Error(
603
+ `spatial ${index.name}: path does not resolve to mapped geometry`
604
+ );
605
+ }
606
+ }
607
+ if (
608
+ !indexed ||
609
+ indexed.value.kind !== "string" ||
610
+ indexed.value.column === undefined
611
+ )
612
+ throw new Error("experimental page index requires a string field SID");
613
+ const indexColumn = indexed.value.column;
614
+ const queries =
615
+ onlyFieldSid === undefined
616
+ ? planSqliteQueries(nodes, aggregate.entity.id, {
617
+ table,
618
+ wireType: aggregate.wireType,
619
+ fields,
620
+ })
621
+ : { plans: [], refusals: [] };
622
+ const maintained =
623
+ onlyFieldSid === undefined
624
+ ? planSqliteCounts(nodes, aggregate.entity.id, {
625
+ table,
626
+ wireType: aggregate.wireType,
627
+ fields,
628
+ })
629
+ : { plans: [], refusals: [], snapshot: null };
630
+ const computed =
631
+ onlyFieldSid === undefined
632
+ ? planSqliteComputations(nodes, aggregate.entity.id, {
633
+ table,
634
+ wireType: aggregate.wireType,
635
+ fields,
636
+ })
637
+ : { plans: [], refusals: [] };
638
+ const ddl =
639
+ tables
640
+ .map(
641
+ (t) =>
642
+ `CREATE TABLE ${t.name} (${t.columns
643
+ .map((c) => `${c.name} ${c.type}`)
644
+ .join(",")}, PRIMARY KEY(${
645
+ t.child ? "id,parent,key" : "id"
646
+ })) WITHOUT ROWID;`
647
+ )
648
+ .join("\n") +
649
+ `\nCREATE INDEX ${table}_page ON ${table}(c${indexColumn},id) WHERE c${indexed.presence}=2;` +
650
+ queries.plans.map((q) => "\n" + q.ddl).join("");
651
+ const counts = tables.map((t) => ({
652
+ name: t.name,
653
+ columns: t.columns.length,
654
+ }));
655
+ const maxColumns = Math.max(...counts.map((t) => t.columns));
656
+ const result = {
657
+ version: 2 as const,
658
+ aggregateSid,
659
+ wireType: aggregate.wireType,
660
+ table,
661
+ columns: rootColumns,
662
+ fields,
663
+ indexColumn,
664
+ spatialIndexes,
665
+ queryReads: queries.plans,
666
+ queryRefusals: queries.refusals,
667
+ countReads: maintained.plans,
668
+ countRefusals: maintained.refusals,
669
+ countSnapshot: maintained.snapshot,
670
+ computedReads: computed.plans,
671
+ computedRefusals: computed.refusals,
672
+ ddl,
673
+ budget: {
674
+ columnLimit,
675
+ tables: counts,
676
+ maxColumns,
677
+ totalColumns: counts.reduce((sum, t) => sum + t.columns, 0),
678
+ indexes: 1 + queries.plans.length,
679
+ computedSubstrate: {
680
+ declarations: computed.plans.length,
681
+ sqlTemporalReads: computed.plans.filter((p) => p.temporal).length,
682
+ sealedEngineReads: computed.plans.filter((p) => !p.temporal).length,
683
+ storedColumns: 0 as const,
684
+ queueTables: 0 as const,
685
+ },
686
+ queryIndexes: queries.plans.map((q) => ({
687
+ name: q.index,
688
+ keyColumns: q.keys.length + 1,
689
+ parameters: q.keys.length + 2,
690
+ })),
691
+ countSubstrate: {
692
+ declarations: maintained.plans.length,
693
+ tables: maintained.plans.length
694
+ ? [{ name: "flat_counts", columns: 3 }]
695
+ : [],
696
+ snapshotColumns: maintained.snapshot?.columns ?? 0,
697
+ snapshotParameters: maintained.snapshot ? 1 : 0,
698
+ },
699
+ spatialSubstrate: {
700
+ declarations: spatialIndexes.length,
701
+ tables: spatialIndexes.length
702
+ ? [
703
+ { name: "spatial_rtree", columns: 5 },
704
+ { name: "spatial_rowid", columns: 3 },
705
+ ]
706
+ : [],
707
+ },
708
+ statementsPerPage: tables.length,
709
+ maxJoinTables: tables.length > 1 ? 2 : 1,
710
+ maxResultColumns: maxColumns,
711
+ maxInsertParameters: maxColumns,
712
+ warnings:
713
+ maxColumns > (options.warnColumns ?? 256)
714
+ ? [
715
+ "Wide table: benchmark prepare, read and write costs; 256 is an advisory threshold, not a measured performance guarantee.",
716
+ ]
717
+ : [],
718
+ },
719
+ };
720
+ return {
721
+ ...result,
722
+ fingerprint: createHash("sha256")
723
+ .update(JSON.stringify(result))
724
+ .digest("hex"),
725
+ };
726
+ }
727
+
728
+ /** A report of the REAL compiler closure. No graph serialization, law emission,
729
+ * tenant-specific name switches, or successful-looking budgets for blocked types.
730
+ * The single builtin-type index is a diagnostic probe, NOT tenant query planning.
731
+ */
732
+ export function inspectSqliteLayouts(closure: readonly LawGraph[]) {
733
+ const nodes = new Map(
734
+ closure.flatMap((g) => g.nodes.map((n) => [n.entity.id, n] as const))
735
+ );
736
+ const domains = new Map<string, string[]>();
737
+ for (const g of closure)
738
+ for (const n of g.nodes)
739
+ if (n.kind === "aggregate") {
740
+ const owners = domains.get(n.entity.sid) ?? [];
741
+ if (!owners.includes(g.domain.entity.label))
742
+ owners.push(g.domain.entity.label);
743
+ domains.set(n.entity.sid, owners);
744
+ }
745
+ const aggregates = [...nodes.values()]
746
+ .filter((n) => n.kind === "aggregate")
747
+ .map((a) => {
748
+ const base = {
749
+ sid: a.entity.sid,
750
+ label: a.entity.label,
751
+ wireType: a.wireType,
752
+ domains: domains.get(a.entity.sid)!.sort(),
753
+ fields: a.fields.length,
754
+ };
755
+ try {
756
+ const layout = planWithNodes(nodes, a.entity.sid, "builtin:type");
757
+ return { ...base, status: "planned" as const, layout };
758
+ } catch (error) {
759
+ const fieldRefusals: { field: string; sid: string; reason: string }[] =
760
+ [];
761
+ let supportedFields = 0;
762
+ for (const r of a.fields) {
763
+ const f = nodes.get(r.id);
764
+ if (f?.kind !== "field") throw new Error(`missing field ${r.id}`);
765
+ try {
766
+ planWithNodes(
767
+ nodes,
768
+ a.entity.sid,
769
+ "builtin:type",
770
+ {},
771
+ f.entity.sid
772
+ );
773
+ supportedFields++;
774
+ } catch (e) {
775
+ fieldRefusals.push({
776
+ field: f.entity.label,
777
+ sid: f.entity.sid,
778
+ reason: (e as Error).message,
779
+ });
780
+ }
781
+ }
782
+ return {
783
+ ...base,
784
+ status: "blocked" as const,
785
+ reason: (error as Error).message,
786
+ supportedFields,
787
+ fieldRefusals,
788
+ };
789
+ }
790
+ })
791
+ .sort((a, b) => a.wireType.localeCompare(b.wireType));
792
+ const planned = aggregates.filter((a) => a.status === "planned");
793
+ return {
794
+ version: 1,
795
+ kind: "experimental-sqlite-layout-inspection",
796
+ indexPolicy:
797
+ "builtin type / id, declared query expression indexes, maintained count totals and spatial indexes; see per-read coverage and runtime gates",
798
+ summary: {
799
+ domains: closure.length,
800
+ uniqueAggregates: aggregates.length,
801
+ planned: planned.length,
802
+ blocked: aggregates.length - planned.length,
803
+ maxPlannedColumns: Math.max(
804
+ 0,
805
+ ...planned.map((a) => a.layout.budget.maxColumns)
806
+ ),
807
+ maxPlannedTables: Math.max(
808
+ 0,
809
+ ...planned.map((a) => a.layout.budget.tables.length)
810
+ ),
811
+ allSupported: planned.length === aggregates.length,
812
+ spatialIndexes: planned.reduce(
813
+ (n, a) => n + a.layout.spatialIndexes.length,
814
+ 0
815
+ ),
816
+ },
817
+ aggregates,
818
+ readCoverage: inspectSqliteReads(
819
+ nodes,
820
+ planned.map((a) => a.layout)
821
+ ),
822
+ };
823
+ }