mainsequence 4.0.3__tar.gz → 4.0.6__tar.gz

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 (130) hide show
  1. {mainsequence-4.0.3 → mainsequence-4.0.6}/PKG-INFO +1 -1
  2. {mainsequence-4.0.3 → mainsequence-4.0.6}/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md +136 -11
  3. {mainsequence-4.0.3 → mainsequence-4.0.6}/agent_scaffold/skills/data_publishing/meta_tables/SKILL.md +68 -11
  4. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/client/data_sources_interfaces/__init__.py +7 -1
  5. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/client/data_sources_interfaces/duckdb.py +293 -263
  6. mainsequence-4.0.6/mainsequence/client/data_sources_interfaces/sqlite.py +486 -0
  7. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/client/models_metatables.py +93 -25
  8. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/client/models_tdag.py +397 -98
  9. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/tdag/__init__.py +1 -0
  10. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/tdag/base_persist_managers.py +28 -5
  11. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/tdag/data_nodes/__init__.py +1 -0
  12. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/tdag/data_nodes/build_operations.py +21 -3
  13. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/tdag/data_nodes/data_nodes.py +35 -4
  14. mainsequence-4.0.6/mainsequence/tdag/data_nodes/models.py +342 -0
  15. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/tdag/data_nodes/run_operations.py +6 -3
  16. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/tdag/meta_tables/__init__.py +9 -0
  17. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/tdag/meta_tables/hashing.py +54 -0
  18. mainsequence-4.0.6/mainsequence/tdag/meta_tables/sqlalchemy_contracts.py +1075 -0
  19. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/tdag/pydantic_metadata.py +9 -3
  20. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence.egg-info/PKG-INFO +1 -1
  21. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence.egg-info/SOURCES.txt +3 -1
  22. {mainsequence-4.0.3 → mainsequence-4.0.6}/pyproject.toml +1 -1
  23. {mainsequence-4.0.3 → mainsequence-4.0.6}/tests/test_build_operations_hashing.py +95 -9
  24. {mainsequence-4.0.3 → mainsequence-4.0.6}/tests/test_data_node_storage_dimension_queries.py +153 -0
  25. {mainsequence-4.0.3 → mainsequence-4.0.6}/tests/test_data_node_update_flow.py +34 -0
  26. {mainsequence-4.0.3 → mainsequence-4.0.6}/tests/test_dependency_extras.py +9 -0
  27. mainsequence-4.0.6/tests/test_duckdb_interface_dimensions.py +285 -0
  28. {mainsequence-4.0.3 → mainsequence-4.0.6}/tests/test_meta_tables_client_models.py +0 -4
  29. mainsequence-4.0.6/tests/test_meta_tables_sqlalchemy_contracts.py +598 -0
  30. mainsequence-4.0.6/tests/test_pod_project_resolution.py +431 -0
  31. mainsequence-4.0.6/tests/test_sqlite_interface_dimensions.py +294 -0
  32. mainsequence-4.0.3/mainsequence/client/data_sources_interfaces/timescale.py +0 -524
  33. mainsequence-4.0.3/mainsequence/tdag/data_nodes/models.py +0 -112
  34. mainsequence-4.0.3/mainsequence/tdag/meta_tables/sqlalchemy_contracts.py +0 -476
  35. mainsequence-4.0.3/tests/test_meta_tables_sqlalchemy_contracts.py +0 -209
  36. mainsequence-4.0.3/tests/test_pod_project_resolution.py +0 -98
  37. {mainsequence-4.0.3 → mainsequence-4.0.6}/LICENSE +0 -0
  38. {mainsequence-4.0.3 → mainsequence-4.0.6}/README.md +0 -0
  39. {mainsequence-4.0.3 → mainsequence-4.0.6}/agent_scaffold/AGENTS.md +0 -0
  40. {mainsequence-4.0.3 → mainsequence-4.0.6}/agent_scaffold/skills/a2a_communication/SKILL.md +0 -0
  41. {mainsequence-4.0.3 → mainsequence-4.0.6}/agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md +0 -0
  42. {mainsequence-4.0.3 → mainsequence-4.0.6}/agent_scaffold/skills/command_center/adapter_from_api/SKILL.md +0 -0
  43. {mainsequence-4.0.3 → mainsequence-4.0.6}/agent_scaffold/skills/command_center/api_mock_prototyping/SKILL.md +0 -0
  44. {mainsequence-4.0.3 → mainsequence-4.0.6}/agent_scaffold/skills/command_center/app_components/SKILL.md +0 -0
  45. {mainsequence-4.0.3 → mainsequence-4.0.6}/agent_scaffold/skills/command_center/connections/SKILL.md +0 -0
  46. {mainsequence-4.0.3 → mainsequence-4.0.6}/agent_scaffold/skills/command_center/workspace_analysis/SKILL.md +0 -0
  47. {mainsequence-4.0.3 → mainsequence-4.0.6}/agent_scaffold/skills/command_center/workspace_builder/SKILL.md +0 -0
  48. {mainsequence-4.0.3 → mainsequence-4.0.6}/agent_scaffold/skills/command_center/workspace_design/SKILL.md +0 -0
  49. {mainsequence-4.0.3 → mainsequence-4.0.6}/agent_scaffold/skills/dashboards/streamlit/SKILL.md +0 -0
  50. {mainsequence-4.0.3 → mainsequence-4.0.6}/agent_scaffold/skills/data_access/exploration/SKILL.md +0 -0
  51. {mainsequence-4.0.3 → mainsequence-4.0.6}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md +0 -0
  52. {mainsequence-4.0.3 → mainsequence-4.0.6}/agent_scaffold/skills/maintenance/local_journal/SKILL.md +0 -0
  53. {mainsequence-4.0.3 → mainsequence-4.0.6}/agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md +0 -0
  54. {mainsequence-4.0.3 → mainsequence-4.0.6}/agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md +0 -0
  55. {mainsequence-4.0.3 → mainsequence-4.0.6}/agent_scaffold/skills/project_builder/SKILL.md +0 -0
  56. {mainsequence-4.0.3 → mainsequence-4.0.6}/agent_scaffold/skills/project_to_agent/SKILL.md +0 -0
  57. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/__init__.py +0 -0
  58. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/__main__.py +0 -0
  59. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/bootstrap.py +0 -0
  60. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/cli/__init__.py +0 -0
  61. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/cli/api.py +0 -0
  62. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/cli/browser_auth.py +0 -0
  63. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/cli/cli.py +0 -0
  64. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/cli/config.py +0 -0
  65. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/cli/docker_utils.py +0 -0
  66. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/cli/doctor.py +0 -0
  67. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/cli/local_ops.py +0 -0
  68. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/cli/model_filters.py +0 -0
  69. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/cli/project_status.py +0 -0
  70. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/cli/pydantic_cli.py +0 -0
  71. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/cli/sdk_utils.py +0 -0
  72. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/cli/ssh_utils.py +0 -0
  73. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/cli/ui.py +0 -0
  74. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/client/__init__.py +0 -0
  75. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/client/agent_runtime_models.py +0 -0
  76. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/client/base.py +0 -0
  77. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/client/client.py +0 -0
  78. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/client/command_center/__init__.py +0 -0
  79. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/client/command_center/app_component.py +0 -0
  80. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/client/command_center/connections.py +0 -0
  81. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/client/command_center/data_models.py +0 -0
  82. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/client/command_center/workspace.py +0 -0
  83. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/client/command_center/workspace_snapshot.py +0 -0
  84. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/client/exceptions.py +0 -0
  85. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/client/fastapi/__init__.py +0 -0
  86. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/client/fastapi/auth.py +0 -0
  87. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/client/models_helpers.py +0 -0
  88. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/client/models_user.py +0 -0
  89. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/client/utils.py +0 -0
  90. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/compute_validation.py +0 -0
  91. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/defaults.py +0 -0
  92. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/instrumentation/__init__.py +0 -0
  93. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/instrumentation/utils.py +0 -0
  94. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/logconf.py +0 -0
  95. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/runtime_flags.py +0 -0
  96. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/tdag/__main__.py +0 -0
  97. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/tdag/config.py +0 -0
  98. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/tdag/configuration_models.py +0 -0
  99. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/tdag/data_nodes/filters.py +0 -0
  100. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/tdag/data_nodes/namespacing.py +0 -0
  101. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/tdag/data_nodes/persist_managers.py +0 -0
  102. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/tdag/data_nodes/utils.py +0 -0
  103. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/tdag/filters.py +0 -0
  104. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/tdag/future_registry.py +0 -0
  105. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/tdag/meta_tables/compiled_sql.py +0 -0
  106. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence/tdag/utils.py +0 -0
  107. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence.egg-info/dependency_links.txt +0 -0
  108. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence.egg-info/entry_points.txt +0 -0
  109. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence.egg-info/requires.txt +0 -0
  110. {mainsequence-4.0.3 → mainsequence-4.0.6}/mainsequence.egg-info/top_level.txt +0 -0
  111. {mainsequence-4.0.3 → mainsequence-4.0.6}/setup.cfg +0 -0
  112. {mainsequence-4.0.3 → mainsequence-4.0.6}/tests/test_auth_precedence.py +0 -0
  113. {mainsequence-4.0.3 → mainsequence-4.0.6}/tests/test_cli.py +0 -0
  114. {mainsequence-4.0.3 → mainsequence-4.0.6}/tests/test_cli_browser_auth.py +0 -0
  115. {mainsequence-4.0.3 → mainsequence-4.0.6}/tests/test_client.py +0 -0
  116. {mainsequence-4.0.3 → mainsequence-4.0.6}/tests/test_command_center_app_component_models.py +0 -0
  117. {mainsequence-4.0.3 → mainsequence-4.0.6}/tests/test_command_center_data_models.py +0 -0
  118. {mainsequence-4.0.3 → mainsequence-4.0.6}/tests/test_command_center_models.py +0 -0
  119. {mainsequence-4.0.3 → mainsequence-4.0.6}/tests/test_data_access_mixin_dimension_audit.py +0 -0
  120. {mainsequence-4.0.3 → mainsequence-4.0.6}/tests/test_data_node_search_join_filters.py +0 -0
  121. {mainsequence-4.0.3 → mainsequence-4.0.6}/tests/test_filter_normalization.py +0 -0
  122. {mainsequence-4.0.3 → mainsequence-4.0.6}/tests/test_logconf.py +0 -0
  123. {mainsequence-4.0.3 → mainsequence-4.0.6}/tests/test_models_user_request_bound_auth.py +0 -0
  124. {mainsequence-4.0.3 → mainsequence-4.0.6}/tests/test_project_batch_jobs_from_file.py +0 -0
  125. {mainsequence-4.0.3 → mainsequence-4.0.6}/tests/test_run_configuration.py +0 -0
  126. {mainsequence-4.0.3 → mainsequence-4.0.6}/tests/test_source_table_configuration.py +0 -0
  127. {mainsequence-4.0.3 → mainsequence-4.0.6}/tests/test_update_runner_uid_runtime.py +0 -0
  128. {mainsequence-4.0.3 → mainsequence-4.0.6}/tests/test_update_statistics.py +0 -0
  129. {mainsequence-4.0.3 → mainsequence-4.0.6}/tests/test_update_uid_guards.py +0 -0
  130. {mainsequence-4.0.3 → mainsequence-4.0.6}/tests/test_workspace_snapshot.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mainsequence
3
- Version: 4.0.3
3
+ Version: 4.0.6
4
4
  Summary: Main Sequence SDK
5
5
  Author-email: Main Sequence GmbH <dev@main-sequence.io>
6
6
  License: MainSequence GmbH SDK License Agreement
@@ -17,12 +17,13 @@ This skill is for producer-side table engineering.
17
17
  - modify an existing `DataNode`
18
18
  - review whether a DataNode change is breaking or non-breaking
19
19
  - define or refactor `DataNodeConfiguration`
20
- - classify config fields into dataset meaning, updater scope, and runtime-only concerns
20
+ - classify config fields into dataset meaning, updater scope, and hash-excluded metadata
21
21
  - implement or review:
22
22
  - `dependencies()`
23
23
  - `update()`
24
24
  - `get_asset_list()`
25
- - metadata and record definitions
25
+ - `RecordDefinition` declarations and metadata
26
+ - DataNode source-table foreign key declarations to MetaTables
26
27
  - design single-index or `(time_index, unique_identifier)` MultiIndex outputs
27
28
  - define namespace-first validation strategy
28
29
  - write or review DataNode smoke tests
@@ -71,7 +72,8 @@ Before changing code, collect or infer:
71
72
  - dataset meaning
72
73
  - intended published identifier
73
74
  - expected index shape
74
- - expected columns and dtypes
75
+ - expected columns and dtypes, declared as `RecordDefinition`
76
+ - whether declared columns should reference registered MetaTables through foreign keys
75
77
  - upstream dependencies
76
78
  - whether the node is asset-indexed
77
79
  - first-run or backfill bounds
@@ -106,7 +108,8 @@ Do not change them casually.
106
108
 
107
109
  - dataset meaning belongs in table identity
108
110
  - updater scope belongs in updater identity
109
- - runtime-only knobs belong outside hashed identity
111
+ - descriptive metadata belongs in `hash_excluded` fields
112
+ - runtime knobs belong outside hashed identity
110
113
 
111
114
  Do not mix these.
112
115
 
@@ -140,12 +143,127 @@ If the node emits `(time_index, unique_identifier)`:
140
143
  - `get_asset_list()` must reflect the effective updater asset scope
141
144
  - missing assets should be resolved or registered when required by the workflow
142
145
 
143
- ### 7. Metadata is not optional for production-quality nodes
144
-
145
- When the node is not a throwaway example, provide:
146
-
147
- - table metadata
148
- - column metadata or record definitions
146
+ ### 7. `RecordDefinition` is the canonical schema surface
147
+
148
+ Every new or materially edited `DataNodeConfiguration` must declare output
149
+ records with `RecordDefinition` unless there is a documented compatibility
150
+ reason not to. Treat `records` as the canonical table schema declaration, not
151
+ as optional UI metadata.
152
+
153
+ The canonical pattern is:
154
+
155
+ ```python
156
+ from pydantic import Field
157
+
158
+ from mainsequence.tdag import DataNodeConfiguration, RecordDefinition
159
+
160
+
161
+ class PricesConfig(DataNodeConfiguration):
162
+ records: list[RecordDefinition] = Field(
163
+ default_factory=lambda: [
164
+ RecordDefinition(
165
+ column_name="price",
166
+ dtype="float64",
167
+ label="Price",
168
+ description="Observed price.",
169
+ )
170
+ ]
171
+ )
172
+ ```
173
+
174
+ Rules:
175
+
176
+ - `column_name` and `dtype` are structural and define the persisted record contract.
177
+ - `label` and `description` are descriptive discovery metadata and must not be treated as runtime controls.
178
+ - The DataFrame returned by `update()` must match declared `records`.
179
+ - Do not invent another schema object or parallel record declaration.
180
+ - Prefer `DataNodeConfiguration.records` over overriding `get_column_metadata()` for normal nodes.
181
+
182
+ ### 8. Use `SourceTableForeignKey` when a DataNode references a MetaTable
183
+
184
+ When a DataNode source table has a column that should reference a registered
185
+ MetaTable, declare that relationship in `DataNodeConfiguration.foreign_keys`.
186
+ Do this only for DataNode source-table to MetaTable relationships. Do not
187
+ invent DataNode-to-DataNode or MetaTable-to-DataNode foreign keys.
188
+
189
+ The canonical pattern is:
190
+
191
+ ```python
192
+ from pydantic import Field
193
+
194
+ from mainsequence.tdag import (
195
+ DataNodeConfiguration,
196
+ RecordDefinition,
197
+ SourceTableForeignKey,
198
+ )
199
+
200
+
201
+ ASSET_UID = RecordDefinition(
202
+ column_name="asset_uid",
203
+ dtype="uuid",
204
+ label="Asset",
205
+ description="Asset UID.",
206
+ )
207
+
208
+
209
+ class PricesConfig(DataNodeConfiguration):
210
+ records: list[RecordDefinition] = Field(
211
+ default_factory=lambda: [
212
+ RecordDefinition(
213
+ column_name="time_index",
214
+ dtype="datetime64[ns, UTC]",
215
+ label="Time",
216
+ description="UTC observation timestamp.",
217
+ ),
218
+ ASSET_UID,
219
+ RecordDefinition(
220
+ column_name="price",
221
+ dtype="float64",
222
+ label="Price",
223
+ description="Observed price.",
224
+ ),
225
+ ]
226
+ )
227
+ foreign_keys: list[SourceTableForeignKey] = Field(
228
+ default_factory=lambda: [
229
+ SourceTableForeignKey(
230
+ target=Asset,
231
+ source_columns=[ASSET_UID],
232
+ target_columns=[Asset.uid],
233
+ on_delete="restrict",
234
+ )
235
+ ]
236
+ )
237
+ ```
238
+
239
+ Rules:
240
+
241
+ - `SourceTableForeignKey` is the authoring model; do not hand-author
242
+ `SourceTableForeignKeyContract` in DataNode configs.
243
+ - `source_columns` should reference the same `RecordDefinition` objects listed
244
+ in `records`.
245
+ - `target_columns` should use MetaTable/SQLAlchemy column references such as
246
+ `Asset.uid`, not backend UID strings.
247
+ - Do not ask users to provide FK names.
248
+ - Do not ask users to provide `target_meta_table_uid`; the SDK resolves the
249
+ target MetaTable public `uid`.
250
+ - FK hash material is source column names, target MetaTable public `uid`,
251
+ target column names, and `on_delete`.
252
+ - FK hash material must not include generated names, backend database primary
253
+ keys, source-table FK row UIDs, backend projection/enforcement fields, target
254
+ storage hashes, or Python object/class repr values.
255
+ - If FK target registration or MetaTable ownership is unclear, route to
256
+ `.agents/skills/mainsequence/data_publishing/meta_tables/SKILL.md`.
257
+
258
+ ### 9. Metadata is still required for production-quality nodes
259
+
260
+ When the node is not a throwaway example, also provide table metadata through
261
+ `DataNodeConfiguration.node_metadata` when a stable published identifier or
262
+ description is needed.
263
+
264
+ Use `json_schema_extra={"hash_excluded": True}` for descriptive metadata that
265
+ must not rotate `update_hash` or `storage_hash`. Keep the older `runtime_only`
266
+ marker only for legacy compatibility.
149
267
 
150
268
  ## Review Rules
151
269
 
@@ -153,7 +271,10 @@ When reviewing an existing DataNode, look for:
153
271
 
154
272
  - identifier collisions
155
273
  - accidental schema breaks
156
- - wrong meaning/scope/runtime-only split
274
+ - wrong meaning/scope/hash-excluded split
275
+ - missing `RecordDefinition` declarations
276
+ - missing or incorrectly authored `SourceTableForeignKey` declarations when a
277
+ DataNode column references a MetaTable
157
278
  - misuse of `hash_namespace`
158
279
  - non-incremental `update()` behavior
159
280
  - hidden dependency creation inside `update()`
@@ -167,6 +288,10 @@ Do not claim success until you have checked:
167
288
  - the relevant docs were read first
168
289
  - the identifier choice is intentional
169
290
  - config fields are classified correctly
291
+ - `DataNodeConfiguration.records` is present for new or materially edited nodes
292
+ - declared `RecordDefinition` names and dtypes match the DataFrame returned by `update()`
293
+ - any DataNode-to-MetaTable relationships use `SourceTableForeignKey` with
294
+ source record references and target column references
170
295
  - `dependencies()` is deterministic
171
296
  - `update()` is incremental
172
297
  - the DataFrame shape is valid
@@ -15,7 +15,8 @@ This skill is for schema-driven application tables registered through TS Manager
15
15
 
16
16
  - define SQLAlchemy/Core or ORM table models for `MetaTable` registration
17
17
  - choose `platform_managed` or `external_registered` management mode
18
- - build registration requests from resolved SQLAlchemy metadata
18
+ - register platform-managed tables through the model class API
19
+ - build registration requests from resolved SQLAlchemy metadata when inspection is useful
19
20
  - define indexes and foreign keys in the table contract
20
21
  - design governed compiled SQL read and write operations
21
22
  - review table contracts for physical-name, namespace, and identifier issues
@@ -67,7 +68,7 @@ Before changing code, collect or infer:
67
68
  - expected read patterns
68
69
  - expected mutation patterns
69
70
  - whether TS Manager should create the physical table
70
- - the target `DynamicTableDataSource` UID
71
+ - for `external_registered`, the target `DynamicTableDataSource` UID
71
72
 
72
73
  If ownership of the physical table lifecycle is unclear, stop before choosing a management mode.
73
74
 
@@ -92,15 +93,67 @@ Do not hand-build contract fragments when the SQLAlchemy helper can derive them.
92
93
 
93
94
  ### 2. Use storage-hash physical names for backend-managed tables
94
95
 
95
- For `platform_managed`, use `metatable_tablename(...)` as the SQLAlchemy `__tablename__`.
96
+ For `platform_managed`, inherit from `PlatformManagedMetaTable`.
96
97
 
97
- The backend expects that deterministic physical name to match the registration storage hash.
98
+ The mixin derives the SQLAlchemy physical table name from storage-relevant configuration and table shape. Do not hand-write `__tablename__` for normal backend-managed tables.
99
+
100
+ Schema must come from SQLAlchemy table metadata, usually `__table_args__ = {"schema": "public"}` or the tuple form ending in `{"schema": ...}`. Do not add a separate MetaTable-specific schema attribute.
101
+
102
+ Register through the class API:
103
+
104
+ ```python
105
+ account_meta_table = Account.register(
106
+ description="Example account table.",
107
+ labels=["sdk-example"],
108
+ )
109
+ ```
110
+
111
+ For platform-managed registration, the data source is resolved from the active Main Sequence project/session, the same way DataNode does. Do not require or thread a `data_source_uid` through normal platform-managed example code.
112
+
113
+ Only call `build_registration_request()` when the task explicitly needs to inspect or validate the payload before registration.
114
+
115
+ For SDK examples, use a plain namespace constant:
116
+
117
+ ```python
118
+ NAMESPACE = "sdk-examples"
119
+ ```
120
+
121
+ Do not add an environment variable for namespace in examples.
122
+
123
+ Do not add generic labels such as `"meta-table"` or `"platform-managed"` to examples. Keep labels specific to the example or domain.
124
+
125
+ Do not add a `MAINSEQUENCE_META_TABLE_REGISTER` toggle in registration examples. Registration examples should register directly.
98
126
 
99
127
  ### 3. Register parent tables before child tables
100
128
 
101
129
  Foreign-key contracts reference the target `MetaTable` UID.
102
130
 
103
- Register parent tables first, then pass their UIDs when building child table registration requests.
131
+ For `PlatformManagedMetaTable`, register parent tables first and then register child tables normally. The SDK inspects SQLAlchemy foreign-key constraints and resolves each target `MetaTable` by looking up the already registered table in the same data source, schema, and physical table name.
132
+
133
+ Example registration order:
134
+
135
+ ```python
136
+ account_meta_table = Account.register(...)
137
+ asset_meta_table = Asset.register(...)
138
+ ```
139
+
140
+ The child registration will fail if the parent table has not already been registered, because there is no target `MetaTable.uid` for the backend FK contract.
141
+
142
+ Do not pass `target_meta_tables` or `target_meta_table_uid_by_fullname` in the normal platform-managed path. Use explicit FK target mappings only for edge cases where automatic lookup is ambiguous or impossible.
143
+
144
+ For `external_registered`, there is no platform-managed parent lookup through the model class. Register the parent first, then build the child registration request with the parent UID mapped by target table fullname:
145
+
146
+ ```python
147
+ account_meta_table = MetaTable.register(account_request)
148
+ asset_request = external_registered_registration_request_from_sqlalchemy_model(
149
+ Asset,
150
+ data_source_uid=data_source_uid,
151
+ target_meta_table_uid_by_fullname={
152
+ Account.__table__.fullname: account_meta_table.uid,
153
+ },
154
+ )
155
+ asset_meta_table = MetaTable.register(asset_request)
156
+ ```
104
157
 
105
158
  ### 4. Governed operations declare scope
106
159
 
@@ -110,7 +163,7 @@ Do not execute unrestricted SQL outside the MetaTable operation contract.
110
163
 
111
164
  ### 5. Physical names come from registered resources
112
165
 
113
- Only use physical schema and table names returned by registered `MetaTable` objects when composing SQL strings.
166
+ Only use physical table names returned by registered `MetaTable` objects when composing SQL strings.
114
167
 
115
168
  Do not hardcode platform-managed physical names manually.
116
169
 
@@ -119,9 +172,12 @@ Do not hardcode platform-managed physical names manually.
119
172
  When reviewing an existing MetaTable workflow, look for:
120
173
 
121
174
  - missing namespace or identifier
122
- - backend-managed models that do not use `metatable_tablename(...)`
175
+ - backend-managed models that do not inherit `PlatformManagedMetaTable`
176
+ - backend-managed examples that use namespace environment variables instead of a plain `sdk-examples` namespace
177
+ - duplicate schema sources outside SQLAlchemy table metadata
123
178
  - external tables registered with unstable physical names
124
- - foreign keys that do not reference target MetaTable UIDs
179
+ - platform-managed child tables that are registered before parent tables
180
+ - external child registrations that do not map foreign-key targets to registered parent `MetaTable.uid` values
125
181
  - compiled SQL operations without complete table scope
126
182
  - raw SQL that hardcodes stale physical names
127
183
  - a table that should really be modeled as a DataNode instead
@@ -132,7 +188,7 @@ Do not claim success until you have checked:
132
188
 
133
189
  - the table contract matches the intended row contract
134
190
  - indexes are intentional
135
- - foreign keys point to the correct dependency targets
191
+ - foreign keys resolve to the correct dependency targets
136
192
  - management mode is correct
137
193
  - backend-managed physical names match the storage hash
138
194
  - registration returns a `MetaTable.uid`
@@ -141,13 +197,14 @@ Do not claim success until you have checked:
141
197
  For related tables, also check:
142
198
 
143
199
  - aliases are readable
144
- - parent table UIDs are passed into child contracts
200
+ - platform-managed parent tables are registered before child tables
201
+ - external child registration requests map FK targets to the registered parent UIDs
145
202
  - query results still match the expected response contract
146
203
 
147
204
  ## This Skill Must Stop And Escalate When
148
205
 
149
206
  - physical table lifecycle ownership is unclear
150
- - the target data source is unknown
207
+ - the target data source is unknown for an `external_registered` workflow
151
208
  - the task really requires a time-series published table
152
209
  - the workflow requires direct database credentials outside TS Manager governance
153
210
  - the task is actually an API or orchestration problem
@@ -16,4 +16,10 @@ def get_duckdb_interface_class():
16
16
  return DuckDBInterface
17
17
 
18
18
 
19
- __all__ = ["get_duckdb_interface_class"]
19
+ def get_sqlite_interface_class():
20
+ from .sqlite import SQLiteInterface
21
+
22
+ return SQLiteInterface
23
+
24
+
25
+ __all__ = ["get_duckdb_interface_class", "get_sqlite_interface_class"]