mainsequence 4.2.16__tar.gz → 4.2.37__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 (128) hide show
  1. {mainsequence-4.2.16/mainsequence.egg-info → mainsequence-4.2.37}/PKG-INFO +2 -2
  2. {mainsequence-4.2.16 → mainsequence-4.2.37}/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md +23 -14
  3. {mainsequence-4.2.16 → mainsequence-4.2.37}/agent_scaffold/skills/data_publishing/meta_tables/SKILL.md +53 -42
  4. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/cli/cli.py +0 -3
  5. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/cli/migrations.py +318 -36
  6. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/client/metatables/core.py +113 -204
  7. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/meta_tables/__init__.py +11 -6
  8. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/meta_tables/migrations.py +280 -351
  9. mainsequence-4.2.37/mainsequence/meta_tables/schema_names.py +304 -0
  10. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/meta_tables/sqlalchemy_contracts.py +248 -1152
  11. {mainsequence-4.2.16 → mainsequence-4.2.37/mainsequence.egg-info}/PKG-INFO +2 -2
  12. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence.egg-info/SOURCES.txt +2 -0
  13. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence.egg-info/requires.txt +1 -1
  14. {mainsequence-4.2.16 → mainsequence-4.2.37}/pyproject.toml +2 -2
  15. {mainsequence-4.2.16 → mainsequence-4.2.37}/tests/test_cli.py +0 -3
  16. {mainsequence-4.2.16 → mainsequence-4.2.37}/tests/test_cli_migrations.py +255 -58
  17. {mainsequence-4.2.16 → mainsequence-4.2.37}/tests/test_meta_table_migrations.py +572 -160
  18. {mainsequence-4.2.16 → mainsequence-4.2.37}/tests/test_meta_tables_client_models.py +243 -219
  19. {mainsequence-4.2.16 → mainsequence-4.2.37}/tests/test_meta_tables_sqlalchemy_contracts.py +376 -351
  20. mainsequence-4.2.37/tests/test_schema_names.py +104 -0
  21. {mainsequence-4.2.16 → mainsequence-4.2.37}/LICENSE +0 -0
  22. {mainsequence-4.2.16 → mainsequence-4.2.37}/README.md +0 -0
  23. {mainsequence-4.2.16 → mainsequence-4.2.37}/agent_scaffold/AGENTS.md +0 -0
  24. {mainsequence-4.2.16 → mainsequence-4.2.37}/agent_scaffold/skills/a2a_communication/SKILL.md +0 -0
  25. {mainsequence-4.2.16 → mainsequence-4.2.37}/agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md +0 -0
  26. {mainsequence-4.2.16 → mainsequence-4.2.37}/agent_scaffold/skills/command_center/adapter_from_api/SKILL.md +0 -0
  27. {mainsequence-4.2.16 → mainsequence-4.2.37}/agent_scaffold/skills/command_center/api_mock_prototyping/SKILL.md +0 -0
  28. {mainsequence-4.2.16 → mainsequence-4.2.37}/agent_scaffold/skills/command_center/app_components/SKILL.md +0 -0
  29. {mainsequence-4.2.16 → mainsequence-4.2.37}/agent_scaffold/skills/command_center/connections/SKILL.md +0 -0
  30. {mainsequence-4.2.16 → mainsequence-4.2.37}/agent_scaffold/skills/command_center/workspace_analysis/SKILL.md +0 -0
  31. {mainsequence-4.2.16 → mainsequence-4.2.37}/agent_scaffold/skills/command_center/workspace_builder/SKILL.md +0 -0
  32. {mainsequence-4.2.16 → mainsequence-4.2.37}/agent_scaffold/skills/command_center/workspace_design/SKILL.md +0 -0
  33. {mainsequence-4.2.16 → mainsequence-4.2.37}/agent_scaffold/skills/dashboards/streamlit/SKILL.md +0 -0
  34. {mainsequence-4.2.16 → mainsequence-4.2.37}/agent_scaffold/skills/data_access/exploration/SKILL.md +0 -0
  35. {mainsequence-4.2.16 → mainsequence-4.2.37}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md +0 -0
  36. {mainsequence-4.2.16 → mainsequence-4.2.37}/agent_scaffold/skills/ms-markets/SKILL.md +0 -0
  37. {mainsequence-4.2.16 → mainsequence-4.2.37}/agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md +0 -0
  38. {mainsequence-4.2.16 → mainsequence-4.2.37}/agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md +0 -0
  39. {mainsequence-4.2.16 → mainsequence-4.2.37}/agent_scaffold/skills/project_builder/SKILL.md +0 -0
  40. {mainsequence-4.2.16 → mainsequence-4.2.37}/agent_scaffold/skills/project_to_agent/SKILL.md +0 -0
  41. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/__init__.py +0 -0
  42. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/__main__.py +0 -0
  43. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/bootstrap.py +0 -0
  44. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/cli/__init__.py +0 -0
  45. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/cli/api.py +0 -0
  46. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/cli/browser_auth.py +0 -0
  47. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/cli/config.py +0 -0
  48. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/cli/docker_utils.py +0 -0
  49. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/cli/doctor.py +0 -0
  50. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/cli/local_ops.py +0 -0
  51. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/cli/model_filters.py +0 -0
  52. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/cli/project_status.py +0 -0
  53. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/cli/pydantic_cli.py +0 -0
  54. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/cli/sdk_utils.py +0 -0
  55. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/cli/ssh_utils.py +0 -0
  56. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/cli/ui.py +0 -0
  57. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/client/__init__.py +0 -0
  58. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/client/agent_runtime_models.py +0 -0
  59. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/client/base.py +0 -0
  60. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/client/client.py +0 -0
  61. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/client/command_center/__init__.py +0 -0
  62. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/client/command_center/app_component.py +0 -0
  63. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/client/command_center/connections.py +0 -0
  64. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/client/command_center/data_models.py +0 -0
  65. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/client/command_center/workspace.py +0 -0
  66. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/client/command_center/workspace_snapshot.py +0 -0
  67. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/client/compute_validation.py +0 -0
  68. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/client/data_sources_interfaces/__init__.py +0 -0
  69. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/client/data_sources_interfaces/duckdb.py +0 -0
  70. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/client/data_sources_interfaces/local_paths.py +0 -0
  71. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/client/data_sources_interfaces/sqlite.py +0 -0
  72. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/client/dtype_codec.py +0 -0
  73. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/client/exceptions.py +0 -0
  74. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/client/fastapi/__init__.py +0 -0
  75. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/client/fastapi/auth.py +0 -0
  76. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/client/metatables/__init__.py +0 -0
  77. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/client/models_foundry.py +0 -0
  78. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/client/models_helpers.py +0 -0
  79. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/client/models_user.py +0 -0
  80. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/client/utils.py +0 -0
  81. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/defaults.py +0 -0
  82. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/instrumentation/__init__.py +0 -0
  83. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/instrumentation/utils.py +0 -0
  84. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/logconf.py +0 -0
  85. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/meta_tables/__main__.py +0 -0
  86. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/meta_tables/compiled_sql/__init__.py +0 -0
  87. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/meta_tables/compiled_sql/v1.py +0 -0
  88. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/meta_tables/data_nodes/__init__.py +0 -0
  89. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/meta_tables/data_nodes/build_operations.py +0 -0
  90. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/meta_tables/data_nodes/data_nodes.py +0 -0
  91. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/meta_tables/data_nodes/models.py +0 -0
  92. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/meta_tables/data_nodes/namespacing.py +0 -0
  93. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/meta_tables/data_nodes/persist_managers.py +0 -0
  94. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/meta_tables/data_nodes/run_operations.py +0 -0
  95. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/meta_tables/data_nodes/utils.py +0 -0
  96. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/meta_tables/future_registry.py +0 -0
  97. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/meta_tables/hashing.py +0 -0
  98. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/meta_tables/pydantic_metadata.py +0 -0
  99. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence/runtime_flags.py +0 -0
  100. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence.egg-info/dependency_links.txt +0 -0
  101. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence.egg-info/entry_points.txt +0 -0
  102. {mainsequence-4.2.16 → mainsequence-4.2.37}/mainsequence.egg-info/top_level.txt +0 -0
  103. {mainsequence-4.2.16 → mainsequence-4.2.37}/setup.cfg +0 -0
  104. {mainsequence-4.2.16 → mainsequence-4.2.37}/tests/test_auth_precedence.py +0 -0
  105. {mainsequence-4.2.16 → mainsequence-4.2.37}/tests/test_build_operations_hashing.py +0 -0
  106. {mainsequence-4.2.16 → mainsequence-4.2.37}/tests/test_cli_browser_auth.py +0 -0
  107. {mainsequence-4.2.16 → mainsequence-4.2.37}/tests/test_client.py +0 -0
  108. {mainsequence-4.2.16 → mainsequence-4.2.37}/tests/test_command_center_app_component_models.py +0 -0
  109. {mainsequence-4.2.16 → mainsequence-4.2.37}/tests/test_command_center_data_models.py +0 -0
  110. {mainsequence-4.2.16 → mainsequence-4.2.37}/tests/test_command_center_models.py +0 -0
  111. {mainsequence-4.2.16 → mainsequence-4.2.37}/tests/test_data_access_mixin_dimension_audit.py +0 -0
  112. {mainsequence-4.2.16 → mainsequence-4.2.37}/tests/test_data_node_storage_dimension_queries.py +0 -0
  113. {mainsequence-4.2.16 → mainsequence-4.2.37}/tests/test_data_node_update_flow.py +0 -0
  114. {mainsequence-4.2.16 → mainsequence-4.2.37}/tests/test_dependency_extras.py +0 -0
  115. {mainsequence-4.2.16 → mainsequence-4.2.37}/tests/test_duckdb_interface_dimensions.py +0 -0
  116. {mainsequence-4.2.16 → mainsequence-4.2.37}/tests/test_filter_normalization.py +0 -0
  117. {mainsequence-4.2.16 → mainsequence-4.2.37}/tests/test_logconf.py +0 -0
  118. {mainsequence-4.2.16 → mainsequence-4.2.37}/tests/test_models_user_request_bound_auth.py +0 -0
  119. {mainsequence-4.2.16 → mainsequence-4.2.37}/tests/test_pod_project_resolution.py +0 -0
  120. {mainsequence-4.2.16 → mainsequence-4.2.37}/tests/test_project_batch_jobs_from_file.py +0 -0
  121. {mainsequence-4.2.16 → mainsequence-4.2.37}/tests/test_run_configuration.py +0 -0
  122. {mainsequence-4.2.16 → mainsequence-4.2.37}/tests/test_secret_client_model.py +0 -0
  123. {mainsequence-4.2.16 → mainsequence-4.2.37}/tests/test_source_table_configuration.py +0 -0
  124. {mainsequence-4.2.16 → mainsequence-4.2.37}/tests/test_sqlite_interface_dimensions.py +0 -0
  125. {mainsequence-4.2.16 → mainsequence-4.2.37}/tests/test_update_runner_uid_runtime.py +0 -0
  126. {mainsequence-4.2.16 → mainsequence-4.2.37}/tests/test_update_statistics.py +0 -0
  127. {mainsequence-4.2.16 → mainsequence-4.2.37}/tests/test_update_uid_guards.py +0 -0
  128. {mainsequence-4.2.16 → mainsequence-4.2.37}/tests/test_workspace_snapshot.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mainsequence
3
- Version: 4.2.16
3
+ Version: 4.2.37
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
@@ -68,7 +68,7 @@ Requires-Dist: pydantic
68
68
  Requires-Dist: pytz
69
69
  Requires-Dist: pyyaml
70
70
  Requires-Dist: requests
71
- Requires-Dist: sqlalchemy
71
+ Requires-Dist: sqlalchemy<3,>=2
72
72
  Requires-Dist: structlog
73
73
  Requires-Dist: tqdm
74
74
  Requires-Dist: typer
@@ -139,7 +139,10 @@ import datetime
139
139
  from sqlalchemy import DateTime, Float, MetaData
140
140
  from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
141
141
 
142
- from mainsequence.meta_tables import PlatformTimeIndexMetaData
142
+ from mainsequence.meta_tables import PlatformTimeIndexMetaData, schema_table_name
143
+
144
+ PROJECT_NAME = "<project_name>"
145
+ PRICES_TABLE_NAME = schema_table_name(PROJECT_NAME, "prices")
143
146
 
144
147
 
145
148
  class Base(DeclarativeBase):
@@ -147,8 +150,9 @@ class Base(DeclarativeBase):
147
150
 
148
151
 
149
152
  class PricesTable(PlatformTimeIndexMetaData, Base):
153
+ __tablename__ = PRICES_TABLE_NAME
150
154
  __metatable_namespace__ = "<domain_namespace>"
151
- __metatable_identifier__ = "<table_identifier>"
155
+ __metatable_identifier__ = "<project_name>.<table_identifier>"
152
156
  __metatable_extra_hash_components__ = {"storage_name": "<stable_storage_name>"}
153
157
  __metatable_description__ = (
154
158
  "Daily close prices keyed by asset unique identifier for portfolio and "
@@ -378,23 +382,24 @@ changing it changes the dependency graph and update identity.
378
382
 
379
383
  Do not construct dependency graphs dynamically inside `update()`.
380
384
 
381
- ### 8. Foreign Keys Belong To The Storage Contract
385
+ ### 8. Foreign Keys Belong To SQLAlchemy And Alembic
382
386
 
383
387
  For new code, model foreign keys on the `PlatformTimeIndexMetaData` storage
384
- class, or route the storage-contract work to the MetaTable skill. When a
385
- DataNode storage table needs a platform-managed FK, use
386
- `MetaTableForeignKey(TargetModel, column=...)` on the storage class. Do not use
387
- `ForeignKey(Target.__table__.c.uid)`, table fullnames, or explicit target UID
388
- maps in DataNode examples.
388
+ class, or route the storage work to the MetaTable skill. When a DataNode storage
389
+ table needs a platform-managed FK, use normal SQLAlchemy `ForeignKey(...)` /
390
+ `ForeignKeyConstraint(...)` metadata on the storage class. Prefer
391
+ project-prefixed SQLAlchemy table names for explicit FK string targets so
392
+ projects sharing one schema do not collide. Generate those names with
393
+ `schema_table_name(project_or_app, concept)` from `mainsequence.meta_tables`.
389
394
 
390
- Do not ask users to name these foreign keys. `MetaTableForeignKey(...)` derives
391
- a stable contract name when `name` is omitted; `name=...` is only for deliberate
392
- overrides.
395
+ Do not ask users to put FK target `MetaTable.uid` values into DataNode config or
396
+ MetaTable registration contracts. Alembic, SQLAlchemy, and the database own FK
397
+ DDL and physical FK constraint names.
393
398
 
394
399
  Registration of the storage class follows the MetaTable migration lifecycle.
395
- Migration tooling recursively resolves/registers unresolved FK target model
396
- classes, uses the local process registry keyed by `storage_hash`, and writes the
397
- target `MetaTable.uid` into the FK contract.
400
+ The migration provider reserves the MetaTable rows, Alembic renders/applies FK
401
+ DDL from SQLAlchemy metadata, and catalog finalization refreshes the MetaTable
402
+ binding after upgrade.
398
403
 
399
404
  Do not add DataNode configuration fields just to mutate storage metadata.
400
405
 
@@ -402,6 +407,10 @@ Do not add DataNode configuration fields just to mutate storage metadata.
402
407
 
403
408
  Production-quality table identifiers, descriptions, labels, column docs, and
404
409
  foreign-key metadata belong to the storage class/MetaTable registration path.
410
+ Prefix explicit table identifiers, explicit physical table names, and Alembic
411
+ version table names with the project or package name rather than using bare
412
+ names that can collide across projects. Use `schema_table_name(...)` for
413
+ authored SQLAlchemy table names, including DataNode storage tables.
405
414
 
406
415
  Do not put schema or published table metadata on the DataNode configuration.
407
416
 
@@ -17,7 +17,7 @@ This skill is for schema-driven application tables registered through TS Manager
17
17
  - choose `platform_managed` or `external_registered` management mode
18
18
  - register platform-managed tables through the model class API
19
19
  - build registration requests from resolved SQLAlchemy metadata when inspection is useful
20
- - define indexes and foreign keys in the table contract
20
+ - define indexes and foreign keys in SQLAlchemy metadata for Alembic-owned DDL
21
21
  - design governed compiled SQL read and write operations
22
22
  - design provider-based Alembic contract evolution for MetaTables
23
23
  - run the documented `mainsequence migrations ...` lifecycle for Alembic-backed MetaTable changes
@@ -122,11 +122,15 @@ Keep the application table model as the authoring source for the neutral table c
122
122
 
123
123
  Do not hand-build contract fragments when the SQLAlchemy helper can derive them.
124
124
 
125
- ### 2. Use storage-hash physical names for backend-managed tables
125
+ ### 2. Use explicit project-prefixed table names
126
126
 
127
127
  For `platform_managed`, inherit from `PlatformManagedMetaTable`.
128
128
 
129
- 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.
129
+ Declare an explicit project-prefixed SQLAlchemy `__tablename__`. Use
130
+ `schema_table_name(project_or_app, concept)` from `mainsequence.meta_tables` to
131
+ generate that name. The mixin derives only the logical `storage_hash` from
132
+ storage-relevant configuration and table shape; it must not use that hash as the
133
+ SQLAlchemy table name.
130
134
 
131
135
  When a platform-managed table must support in-place contract migrations from its
132
136
  first version, use Alembic. Keep the SDK model as a normal
@@ -152,12 +156,26 @@ table. Do not use it for labels, descriptions, runtime options, test isolation,
152
156
  backend UIDs, data-source UIDs, or updater scope. Use `hash_namespace` for test
153
157
  or experiment isolation.
154
158
 
159
+ Prefix explicit table identifiers, explicit physical table names, and Alembic
160
+ version table names with the project or package name. Bare names such as
161
+ `Account`, `Asset`, or `alembic_version` can collide across projects sharing an
162
+ organization or database schema. Prefer `schema_table_name(...)` over
163
+ hand-built f-strings so project/app prefixes, bounded length, and separators are
164
+ consistent.
165
+
155
166
  Register through the class API:
156
167
 
157
168
  ```python
169
+ from mainsequence.meta_tables import PlatformManagedMetaTable, schema_table_name
170
+
171
+ PROJECT_NAME = "sdk_examples"
172
+ ACCOUNT_TABLE_NAME = schema_table_name(PROJECT_NAME, "account")
173
+
174
+
158
175
  class Account(PlatformManagedMetaTable, Base):
176
+ __tablename__ = ACCOUNT_TABLE_NAME
159
177
  __metatable_namespace__ = "sdk-examples"
160
- __metatable_identifier__ = "Account"
178
+ __metatable_identifier__ = "sdk_examples.Account"
161
179
  __metatable_extra_hash_components__ = {"storage_name": "account"}
162
180
  __metatable_description__ = (
163
181
  "Customer account master records used to scope balances, holdings, and "
@@ -210,29 +228,25 @@ Do not add generic labels such as `"meta-table"` or `"platform-managed"` to exam
210
228
  Do not add a `MAINSEQUENCE_META_TABLE_REGISTER` toggle in platform-managed
211
229
  examples. Platform-managed examples should be migration-first.
212
230
 
213
- ### 3. Register parent tables before child tables
214
-
215
- Foreign-key contracts reference the target `MetaTable` UID.
231
+ ### 3. Keep foreign keys in SQLAlchemy/Alembic metadata
216
232
 
217
- For `PlatformManagedMetaTable`, define foreign keys with
218
- `MetaTableForeignKey(TargetModel, column=...)`. Do not write raw SQLAlchemy
219
- table fullnames, `Parent.__table__.c.<column>` targets, or explicit target UID
220
- maps in the platform-managed path. Migration is the lifecycle path. Migration
221
- tooling resolves/registers unresolved target model classes, stores each
222
- returned `MetaTable` in a local process registry keyed by `storage_hash`, and
223
- uses the target `MetaTable.uid` in the child FK contract.
233
+ For `PlatformManagedMetaTable`, define foreign keys with normal SQLAlchemy
234
+ `ForeignKey(...)` / `ForeignKeyConstraint(...)` metadata. Do not write explicit
235
+ target `MetaTable.uid` maps in the platform-managed path. Migration is the
236
+ lifecycle path: the SDK reserves provider MetaTable rows, Alembic renders and
237
+ applies FK/index DDL from SQLAlchemy metadata, and finalization refreshes the
238
+ catalog after upgrade.
224
239
 
225
- Do not require users to provide foreign-key names. `MetaTableForeignKey(...)`
226
- accepts `name=...` only as an override; when omitted, the SDK derives a stable
227
- PostgreSQL-safe contract name from the child table and source column after the
228
- column is attached to the SQLAlchemy table.
240
+ Prefer project-prefixed SQLAlchemy table names for explicit FK string targets.
241
+ Alembic, SQLAlchemy, and the database own physical FK/index names unless the
242
+ project explicitly names them in SQLAlchemy.
229
243
 
230
244
  Use this pattern:
231
245
 
232
246
  ```python
233
247
  account_uid: Mapped[uuid.UUID] = mapped_column(
234
248
  Uuid,
235
- MetaTableForeignKey(Account, column="uid", ondelete="RESTRICT"),
249
+ ForeignKey("public.sdk_examples__account.uid", ondelete="RESTRICT"),
236
250
  nullable=False,
237
251
  )
238
252
  ```
@@ -249,21 +263,19 @@ migration = AlembicMetaTableMigration(
249
263
  )
250
264
  ```
251
265
 
252
- Migration tooling registers `Account` first if `Asset` depends on it and it has
253
- not already been bound in the current process. The local registry prevents
254
- duplicate backend registration attempts for the same `storage_hash` and raises
255
- a clear error for recursive registration cycles.
266
+ Migration tooling reserves provider MetaTable rows in the provider-declared
267
+ model order. Include related parent and child models in the same selected
268
+ provider when Alembic must create or evolve their FK DDL.
256
269
 
257
- For `external_registered`, there is no platform-managed parent lookup. Register
258
- the parent first, then build the child registration request with
259
- `target_meta_tables={Account: account_meta_table}`:
270
+ For `external_registered`, register each external table contract directly. Do
271
+ not encode FK target MetaTable UIDs in the child registration request; FKs are
272
+ database DDL/introspection metadata, not SDK registration contract fields:
260
273
 
261
274
  ```python
262
275
  account_meta_table = MetaTable.register(account_request)
263
276
  asset_request = external_registered_registration_request_from_sqlalchemy_model(
264
277
  Asset,
265
278
  data_source_uid=data_source_uid,
266
- target_meta_tables={Account: account_meta_table},
267
279
  )
268
280
  asset_meta_table = MetaTable.register(asset_request)
269
281
  ```
@@ -288,18 +300,19 @@ For contract evolution, define or update one selected
288
300
  - set `package`, `migration_namespace`, `script_location`, and `target_metadata`
289
301
  - set `alembic_registry` to an `AlembicVersionMetaTable` subclass
290
302
  - list the post-apply catalog scope in `metatable_models`
291
- - generate, render, dry-run, apply, and refresh catalog bindings
303
+ - generate revisions, apply migrations, and refresh catalog bindings
292
304
  through `mainsequence migrations ...` commands
293
305
 
294
306
  `alembic_version_meta_table_uid` is the UID of the catalog binding for Alembic's
295
307
  version table. It is not the UID of the table being migrated.
296
308
 
297
- Application MetaTable catalog sync resolves existing rows by exact
298
- `identifier`. If a model declares `__metatable_identifier__`, that value is the
299
- global identity. If it does not, the SDK derives the identifier from
300
- `[project].name` in `pyproject.toml` plus
301
- `<model.__module__>.<model.__qualname__>`. Pin an explicit identifier when a
302
- class is renamed or moved but must keep the same platform identity.
309
+ Application MetaTable catalog sync resolves existing rows by the authored
310
+ SQLAlchemy table name used by the provider model. Keep that table name stable
311
+ when a class is renamed or moved but must keep the same platform identity.
312
+ When declaring an explicit identifier, explicit physical table name, or Alembic
313
+ version table name, prefix it with the project or package name rather than using
314
+ a bare table name. Use `schema_table_name(project_or_app, concept)` for the
315
+ physical table and Alembic version table names.
303
316
 
304
317
  Do not ask users to construct backend migration payloads, call low-level
305
318
  migration request models, or use SDK helper functions directly. The backend
@@ -352,9 +365,8 @@ When reviewing an existing MetaTable workflow, look for:
352
365
  - backend-managed examples that use namespace environment variables instead of a plain `sdk-examples` namespace
353
366
  - duplicate schema sources outside SQLAlchemy table metadata
354
367
  - external tables registered with unstable physical names
355
- - platform-managed examples that manually sequence parent registration instead
356
- of relying on `MetaTableForeignKey(...)` recursive registration
357
- - external child registrations that do not map foreign-key targets to registered parent `MetaTable.uid` values
368
+ - platform-managed examples that try to sequence parent registration through FK metadata
369
+ - external child registrations that try to encode FK target MetaTable UIDs in registration contracts
358
370
  - contract changes attempted through normal registration instead of an Alembic migration
359
371
  - migration work that asks users to define backend payloads, artifact rows, or SDK request objects
360
372
  - compiled SQL operations without complete table scope
@@ -369,9 +381,9 @@ Do not claim success until you have checked:
369
381
  - the table has an intention-rich `__metatable_description__`
370
382
  - every mapped column has an intention-rich `info.description`
371
383
  - indexes are intentional
372
- - foreign keys resolve to the correct dependency targets
384
+ - foreign keys are present in SQLAlchemy metadata for Alembic when required
373
385
  - management mode is correct
374
- - backend-managed physical names match the storage hash
386
+ - authored physical names are explicit, project-prefixed SQLAlchemy table names
375
387
  - registration returns a `MetaTable.uid`
376
388
  - compiled SQL operations declare table scope
377
389
  - migrations use Alembic-rendered SQL
@@ -384,9 +396,8 @@ Do not claim success until you have checked:
384
396
  For related tables, also check:
385
397
 
386
398
  - aliases are readable
387
- - platform-managed child registration recursively resolves parent
388
- `MetaTableForeignKey(...)` targets
389
- - external child registration requests map FK targets to the registered parent UIDs
399
+ - platform-managed child tables and parent tables are included in the selected migration provider
400
+ - FK target strings use stable project-prefixed SQLAlchemy table names where explicit strings are authored
390
401
  - query results still match the expected response contract
391
402
 
392
403
  ## This Skill Must Stop And Escalate When
@@ -7038,9 +7038,6 @@ def _meta_table_detail_impl(meta_table_uid: str, timeout: int | None) -> None:
7038
7038
  ("Contract Version", str(meta_table.get("contract_version") or "-")),
7039
7039
  ("Table Contract", _format_json_value(meta_table.get("table_contract"))),
7040
7040
  ("Columns", _format_json_value(meta_table.get("columns"))),
7041
- ("Indexes", _format_json_value(meta_table.get("indexes_meta"))),
7042
- ("Foreign Keys", _format_json_value(meta_table.get("foreign_keys"))),
7043
- ("Incoming FKs", _format_json_value(meta_table.get("incoming_fks"))),
7044
7041
  ("Introspection", _format_json_value(meta_table.get("introspection_snapshot"))),
7045
7042
  ],
7046
7043
  )