mainsequence 4.1.9__tar.gz → 4.1.11__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.
- {mainsequence-4.1.9 → mainsequence-4.1.11}/PKG-INFO +1 -1
- {mainsequence-4.1.9 → mainsequence-4.1.11}/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md +55 -18
- {mainsequence-4.1.9 → mainsequence-4.1.11}/agent_scaffold/skills/data_publishing/meta_tables/SKILL.md +37 -6
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/meta_tables/data_nodes/build_operations.py +7 -2
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/meta_tables/data_nodes/data_nodes.py +9 -24
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/meta_tables/data_nodes/persist_managers.py +38 -25
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/meta_tables/sqlalchemy_contracts.py +134 -92
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence.egg-info/PKG-INFO +1 -1
- {mainsequence-4.1.9 → mainsequence-4.1.11}/pyproject.toml +1 -1
- {mainsequence-4.1.9 → mainsequence-4.1.11}/tests/test_build_operations_hashing.py +33 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/tests/test_meta_tables_sqlalchemy_contracts.py +70 -29
- {mainsequence-4.1.9 → mainsequence-4.1.11}/LICENSE +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/README.md +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/agent_scaffold/AGENTS.md +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/agent_scaffold/skills/a2a_communication/SKILL.md +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/agent_scaffold/skills/command_center/adapter_from_api/SKILL.md +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/agent_scaffold/skills/command_center/api_mock_prototyping/SKILL.md +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/agent_scaffold/skills/command_center/app_components/SKILL.md +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/agent_scaffold/skills/command_center/connections/SKILL.md +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/agent_scaffold/skills/command_center/workspace_analysis/SKILL.md +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/agent_scaffold/skills/command_center/workspace_builder/SKILL.md +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/agent_scaffold/skills/command_center/workspace_design/SKILL.md +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/agent_scaffold/skills/dashboards/streamlit/SKILL.md +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/agent_scaffold/skills/data_access/exploration/SKILL.md +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/agent_scaffold/skills/ms-markets/SKILL.md +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/agent_scaffold/skills/project_builder/SKILL.md +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/agent_scaffold/skills/project_to_agent/SKILL.md +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/__init__.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/__main__.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/bootstrap.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/cli/__init__.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/cli/api.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/cli/browser_auth.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/cli/cli.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/cli/config.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/cli/docker_utils.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/cli/doctor.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/cli/local_ops.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/cli/model_filters.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/cli/project_status.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/cli/pydantic_cli.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/cli/sdk_utils.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/cli/ssh_utils.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/cli/ui.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/client/__init__.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/client/agent_runtime_models.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/client/base.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/client/client.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/client/command_center/__init__.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/client/command_center/app_component.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/client/command_center/connections.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/client/command_center/data_models.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/client/command_center/workspace.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/client/command_center/workspace_snapshot.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/client/compute_validation.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/client/data_sources_interfaces/__init__.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/client/data_sources_interfaces/duckdb.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/client/data_sources_interfaces/local_paths.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/client/data_sources_interfaces/sqlite.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/client/dtype_codec.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/client/exceptions.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/client/fastapi/__init__.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/client/fastapi/auth.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/client/models_foundry.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/client/models_helpers.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/client/models_metatables.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/client/models_user.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/client/utils.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/defaults.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/instrumentation/__init__.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/instrumentation/utils.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/logconf.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/meta_tables/__init__.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/meta_tables/__main__.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/meta_tables/compiled_sql/__init__.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/meta_tables/compiled_sql/v1.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/meta_tables/data_nodes/__init__.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/meta_tables/data_nodes/models.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/meta_tables/data_nodes/namespacing.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/meta_tables/data_nodes/run_operations.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/meta_tables/data_nodes/utils.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/meta_tables/future_registry.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/meta_tables/hashing.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/meta_tables/pydantic_metadata.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/runtime_flags.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence.egg-info/SOURCES.txt +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence.egg-info/dependency_links.txt +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence.egg-info/entry_points.txt +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence.egg-info/requires.txt +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence.egg-info/top_level.txt +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/setup.cfg +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/tests/test_auth_precedence.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/tests/test_cli.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/tests/test_cli_browser_auth.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/tests/test_client.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/tests/test_command_center_app_component_models.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/tests/test_command_center_data_models.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/tests/test_command_center_models.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/tests/test_data_access_mixin_dimension_audit.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/tests/test_data_node_storage_dimension_queries.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/tests/test_data_node_update_flow.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/tests/test_dependency_extras.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/tests/test_duckdb_interface_dimensions.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/tests/test_filter_normalization.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/tests/test_logconf.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/tests/test_meta_tables_client_models.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/tests/test_models_user_request_bound_auth.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/tests/test_pod_project_resolution.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/tests/test_project_batch_jobs_from_file.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/tests/test_run_configuration.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/tests/test_secret_client_model.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/tests/test_source_table_configuration.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/tests/test_sqlite_interface_dimensions.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/tests/test_update_runner_uid_runtime.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/tests/test_update_statistics.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/tests/test_update_uid_guards.py +0 -0
- {mainsequence-4.1.9 → mainsequence-4.1.11}/tests/test_workspace_snapshot.py +0 -0
{mainsequence-4.1.9 → mainsequence-4.1.11}/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md
RENAMED
|
@@ -10,13 +10,13 @@ description: Use this skill when the task is about producing, changing, validati
|
|
|
10
10
|
Use this skill when the task changes a DataNode producer.
|
|
11
11
|
|
|
12
12
|
A DataNode is an update process. It is not the canonical storage model. Storage
|
|
13
|
-
is defined by a
|
|
13
|
+
is defined by a `PlatformTimeIndexMetaData` SQLAlchemy model.
|
|
14
14
|
|
|
15
15
|
Canonical workflow:
|
|
16
16
|
|
|
17
17
|
1. Define a `PlatformTimeIndexMetaData` storage class.
|
|
18
|
-
2.
|
|
19
|
-
3.
|
|
18
|
+
2. Construct the DataNode with `config=...` and `storage_table=StorageClass`.
|
|
19
|
+
3. Let the SDK register the output storage class automatically when needed.
|
|
20
20
|
4. Return a DataFrame from `update()` that matches the storage class contract.
|
|
21
21
|
|
|
22
22
|
## This Skill Can Do
|
|
@@ -41,12 +41,13 @@ Canonical workflow:
|
|
|
41
41
|
This skill must not claim ownership of:
|
|
42
42
|
|
|
43
43
|
- generic MetaTable registration or governed operation semantics
|
|
44
|
-
- storage
|
|
44
|
+
- storage registration internals beyond the SDK lifecycle call
|
|
45
45
|
- HTTP route design or FastAPI response contracts
|
|
46
46
|
- workspace/widget layout payloads
|
|
47
47
|
- job creation, scheduling, image pinning, or release creation
|
|
48
48
|
- RBAC or sharing policy
|
|
49
49
|
- domain strategy semantics
|
|
50
|
+
- ad hoc storage creation or registration inside `update()`
|
|
50
51
|
|
|
51
52
|
If the task depends on one of those areas, route it explicitly instead of guessing.
|
|
52
53
|
|
|
@@ -75,7 +76,7 @@ If the task depends on one of those areas, route it explicitly instead of guessi
|
|
|
75
76
|
Before changing code, collect or infer:
|
|
76
77
|
|
|
77
78
|
- dataset meaning
|
|
78
|
-
-
|
|
79
|
+
- `PlatformTimeIndexMetaData` output storage class or the class to create
|
|
79
80
|
- expected time index and identity index shape
|
|
80
81
|
- expected columns and dtypes from the storage class
|
|
81
82
|
- upstream dependencies
|
|
@@ -91,7 +92,7 @@ For every non-trivial DataNode task, make these decisions explicitly:
|
|
|
91
92
|
|
|
92
93
|
1. Is this a new dataset or the same dataset?
|
|
93
94
|
2. Is this change storage-contract work or update-process work?
|
|
94
|
-
3. Is the storage class
|
|
95
|
+
3. Is the storage class contract correct, or should the MetaTable skill handle it?
|
|
95
96
|
4. Is the node single-index or MultiIndex?
|
|
96
97
|
5. Does the first validation run happen under an explicit `hash_namespace(...)`?
|
|
97
98
|
|
|
@@ -112,6 +113,11 @@ Every storage class must include `__metatable_description__`. The description
|
|
|
112
113
|
should explain the table's intention, row grain, and downstream use, not only
|
|
113
114
|
list columns or schema mechanics.
|
|
114
115
|
|
|
116
|
+
Every `mapped_column(...)` in a storage class must include
|
|
117
|
+
`info={"label": ..., "description": ...}`. The column description must explain
|
|
118
|
+
what the value means for this dataset and how downstream users should interpret
|
|
119
|
+
it; do not merely repeat the column name or dtype.
|
|
120
|
+
|
|
115
121
|
Use `__metatable_extra_hash_components__` on storage classes when distinct
|
|
116
122
|
DataNode storage tables could otherwise have the same storage-relevant shape.
|
|
117
123
|
For example, two one-index daily tables with one float column need a stable
|
|
@@ -153,18 +159,38 @@ class PricesTable(PlatformTimeIndexMetaData, Base):
|
|
|
153
159
|
time_index: Mapped[datetime.datetime] = mapped_column(
|
|
154
160
|
DateTime(timezone=True),
|
|
155
161
|
nullable=False,
|
|
162
|
+
info={
|
|
163
|
+
"label": "Time Index",
|
|
164
|
+
"description": "UTC timestamp for the close-price observation.",
|
|
165
|
+
},
|
|
166
|
+
)
|
|
167
|
+
unique_identifier: Mapped[str] = mapped_column(
|
|
168
|
+
nullable=False,
|
|
169
|
+
info={
|
|
170
|
+
"label": "Unique Identifier",
|
|
171
|
+
"description": "Stable asset identifier for the observed price.",
|
|
172
|
+
},
|
|
173
|
+
)
|
|
174
|
+
close: Mapped[float] = mapped_column(
|
|
175
|
+
Float,
|
|
176
|
+
nullable=False,
|
|
177
|
+
info={
|
|
178
|
+
"label": "Close",
|
|
179
|
+
"description": "Daily close price used by portfolio and risk analytics.",
|
|
180
|
+
},
|
|
156
181
|
)
|
|
157
|
-
unique_identifier: Mapped[str] = mapped_column(nullable=False)
|
|
158
|
-
close: Mapped[float] = mapped_column(Float, nullable=False)
|
|
159
182
|
```
|
|
160
183
|
|
|
161
|
-
|
|
184
|
+
Storage registration is inferred from the class metadata and active Main
|
|
185
|
+
Sequence project/session. The DataNode constructor ensures the output
|
|
186
|
+
`storage_table` is registered when needed. You may still call it explicitly
|
|
187
|
+
during bootstrap when code needs the returned metadata object:
|
|
162
188
|
|
|
163
189
|
```python
|
|
164
|
-
PricesTable.register(
|
|
190
|
+
PricesTable.register()
|
|
165
191
|
```
|
|
166
192
|
|
|
167
|
-
`PlatformTimeIndexMetaData.register(
|
|
193
|
+
`PlatformTimeIndexMetaData.register()` is the storage lifecycle path. Treat it
|
|
168
194
|
as an idempotent get-or-create operation: the platform returns the registered
|
|
169
195
|
metadata and UID, and the SDK records that metadata on the class. Do not manually
|
|
170
196
|
attach an existing UID, reconstruct a generic `MetaTable`, or use manual bind
|
|
@@ -175,18 +201,21 @@ helpers as an authoring step.
|
|
|
175
201
|
The DataNode constructor should accept:
|
|
176
202
|
|
|
177
203
|
- a `DataNodeConfiguration`
|
|
178
|
-
- a
|
|
204
|
+
- a `storage_table: type[PlatformTimeIndexMetaData]`
|
|
179
205
|
- optional `hash_namespace`
|
|
180
206
|
|
|
181
207
|
The constructor `storage_table` is the output storage contract. Keep it out of
|
|
182
|
-
`DataNodeConfiguration`.
|
|
208
|
+
`DataNodeConfiguration`. Do not pre-register this output storage class just to
|
|
209
|
+
construct the node; `DataNode` and `PersistManager` call the SDK registration
|
|
210
|
+
lifecycle automatically when the class is not yet bound.
|
|
183
211
|
|
|
184
212
|
If the DataNode needs to select another DataNode's storage table as a
|
|
185
213
|
dependency, put that dependency storage reference in the config as
|
|
186
214
|
`type[PlatformTimeIndexMetaData]`. Do not add an extra constructor argument for
|
|
187
215
|
dependency storage tables. Config values of this type are hashed by the bound
|
|
188
|
-
`TimeIndexMetaData.uid` from `StorageClass.__time_index_metadata__
|
|
189
|
-
|
|
216
|
+
`TimeIndexMetaData.uid` from `StorageClass.__time_index_metadata__`; if the
|
|
217
|
+
class is not yet bound, the config serializer calls `StorageClass.register()`
|
|
218
|
+
before reading the UID.
|
|
190
219
|
|
|
191
220
|
Do not accept `test_node`. It has been removed. Use explicit
|
|
192
221
|
`hash_namespace(...)` or `hash_namespace="..."`.
|
|
@@ -359,6 +388,10 @@ DataNode storage table needs a platform-managed FK, use
|
|
|
359
388
|
`ForeignKey(Target.__table__.c.uid)`, table fullnames, or explicit target UID
|
|
360
389
|
maps in DataNode examples.
|
|
361
390
|
|
|
391
|
+
Do not ask users to name these foreign keys. `MetaTableForeignKey(...)` derives
|
|
392
|
+
a stable contract name when `name` is omitted; `name=...` is only for deliberate
|
|
393
|
+
overrides.
|
|
394
|
+
|
|
362
395
|
Registration of the storage class follows the MetaTable lifecycle:
|
|
363
396
|
`register()` recursively registers unresolved FK target model classes, uses the
|
|
364
397
|
local process registry keyed by `storage_hash`, and writes the target
|
|
@@ -379,11 +412,13 @@ When reviewing an existing DataNode, look for:
|
|
|
379
412
|
|
|
380
413
|
- output storage contract hidden in `DataNodeConfiguration`
|
|
381
414
|
- missing `__metatable_description__` on the storage table
|
|
415
|
+
- storage columns without `mapped_column(info={"label": ..., "description": ...})`
|
|
382
416
|
- dependency storage table passed as an ad hoc constructor argument
|
|
383
417
|
- schema or published table metadata hidden in DataNode configuration
|
|
384
418
|
- `test_node=True`
|
|
385
419
|
- missing explicit `storage_table`
|
|
386
|
-
-
|
|
420
|
+
- manual pre-registration of the output `storage_table` when no returned
|
|
421
|
+
metadata object is needed
|
|
387
422
|
- wrong split between hashed config fields and non-config class/runtime values
|
|
388
423
|
- misuse of `hash_namespace`
|
|
389
424
|
- non-incremental `update()` behavior
|
|
@@ -397,10 +432,12 @@ When reviewing an existing DataNode, look for:
|
|
|
397
432
|
Do not claim success until you have checked:
|
|
398
433
|
|
|
399
434
|
- the relevant docs were read first
|
|
400
|
-
- storage is a
|
|
435
|
+
- output storage is a `PlatformTimeIndexMetaData` class that the SDK can register
|
|
401
436
|
- storage has an intention-rich `__metatable_description__`
|
|
437
|
+
- every storage column has an intention-rich `info.description`
|
|
402
438
|
- the DataNode constructor requires `storage_table`
|
|
403
|
-
- dependency storage-table references live in config and
|
|
439
|
+
- dependency storage-table references live in config and rely on SDK
|
|
440
|
+
registration during config serialization
|
|
404
441
|
- config fields are updater-scoped by default
|
|
405
442
|
- no removed hash metadata markers remain
|
|
406
443
|
- no `test_node` usage remains
|
|
@@ -102,6 +102,9 @@ Schema must come from SQLAlchemy table metadata, usually `__table_args__ = {"sch
|
|
|
102
102
|
Always declare `__metatable_description__` on the model. The description must
|
|
103
103
|
explain the table's business intention, row grain, and expected use, not only
|
|
104
104
|
the schema. Column-level descriptions stay in `mapped_column(info={...})`.
|
|
105
|
+
Every mapped column must include `info={"label": ..., "description": ...}`.
|
|
106
|
+
The column description must explain what the value means in this table and how
|
|
107
|
+
it is used, not just restate the column name or dtype.
|
|
105
108
|
|
|
106
109
|
Use `__metatable_extra_hash_components__` when two backend-managed tables could
|
|
107
110
|
otherwise produce the same storage hash because their storage-relevant shape is
|
|
@@ -124,13 +127,32 @@ class Account(PlatformManagedMetaTable, Base):
|
|
|
124
127
|
"Customer account master records used to scope balances, holdings, and "
|
|
125
128
|
"account-level limits."
|
|
126
129
|
)
|
|
130
|
+
__metatable_labels__ = ["sdk-example"]
|
|
131
|
+
|
|
132
|
+
uid: Mapped[uuid.UUID] = mapped_column(
|
|
133
|
+
Uuid,
|
|
134
|
+
primary_key=True,
|
|
135
|
+
info={
|
|
136
|
+
"label": "Account UID",
|
|
137
|
+
"description": "Stable account identifier referenced by dependent tables.",
|
|
138
|
+
},
|
|
139
|
+
)
|
|
140
|
+
name: Mapped[str] = mapped_column(
|
|
141
|
+
String(255),
|
|
142
|
+
nullable=False,
|
|
143
|
+
info={
|
|
144
|
+
"label": "Name",
|
|
145
|
+
"description": "Display name used to identify the account in examples.",
|
|
146
|
+
},
|
|
147
|
+
)
|
|
127
148
|
|
|
128
149
|
|
|
129
|
-
account_meta_table = Account.register(
|
|
150
|
+
account_meta_table = Account.register()
|
|
130
151
|
```
|
|
131
152
|
|
|
132
|
-
|
|
133
|
-
|
|
153
|
+
Registration metadata belongs on the class. Do not pass description, labels,
|
|
154
|
+
provisioning, data-source UID, hash namespace, time-index fields, or storage
|
|
155
|
+
layout into `register()`.
|
|
134
156
|
|
|
135
157
|
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.
|
|
136
158
|
|
|
@@ -160,6 +182,11 @@ maps in the platform-managed path. Registration is the lifecycle path:
|
|
|
160
182
|
returned `MetaTable` in a local process registry keyed by `storage_hash`, and
|
|
161
183
|
uses the target `MetaTable.uid` in the child FK contract.
|
|
162
184
|
|
|
185
|
+
Do not require users to provide foreign-key names. `MetaTableForeignKey(...)`
|
|
186
|
+
accepts `name=...` only as an override; when omitted, the SDK derives a stable
|
|
187
|
+
PostgreSQL-safe contract name from the child table and source column after the
|
|
188
|
+
column is attached to the SQLAlchemy table.
|
|
189
|
+
|
|
163
190
|
Use this pattern:
|
|
164
191
|
|
|
165
192
|
```python
|
|
@@ -176,7 +203,7 @@ both the schema and the table's intention.
|
|
|
176
203
|
Example registration order:
|
|
177
204
|
|
|
178
205
|
```python
|
|
179
|
-
asset_meta_table = Asset.register(
|
|
206
|
+
asset_meta_table = Asset.register()
|
|
180
207
|
```
|
|
181
208
|
|
|
182
209
|
The child registration registers `Account` first if it has not already been
|
|
@@ -216,11 +243,13 @@ When reviewing an existing MetaTable workflow, look for:
|
|
|
216
243
|
|
|
217
244
|
- missing namespace or identifier
|
|
218
245
|
- missing `__metatable_description__`, or a description that only repeats column names instead of table intention
|
|
246
|
+
- mapped columns without `info.label` and `info.description`
|
|
219
247
|
- backend-managed models that do not inherit `PlatformManagedMetaTable`
|
|
220
248
|
- backend-managed examples that use namespace environment variables instead of a plain `sdk-examples` namespace
|
|
221
249
|
- duplicate schema sources outside SQLAlchemy table metadata
|
|
222
250
|
- external tables registered with unstable physical names
|
|
223
|
-
- platform-managed
|
|
251
|
+
- platform-managed examples that manually sequence parent registration instead
|
|
252
|
+
of relying on `MetaTableForeignKey(...)` recursive registration
|
|
224
253
|
- external child registrations that do not map foreign-key targets to registered parent `MetaTable.uid` values
|
|
225
254
|
- compiled SQL operations without complete table scope
|
|
226
255
|
- raw SQL that hardcodes stale physical names
|
|
@@ -232,6 +261,7 @@ Do not claim success until you have checked:
|
|
|
232
261
|
|
|
233
262
|
- the table contract matches the intended row contract
|
|
234
263
|
- the table has an intention-rich `__metatable_description__`
|
|
264
|
+
- every mapped column has an intention-rich `info.description`
|
|
235
265
|
- indexes are intentional
|
|
236
266
|
- foreign keys resolve to the correct dependency targets
|
|
237
267
|
- management mode is correct
|
|
@@ -242,7 +272,8 @@ Do not claim success until you have checked:
|
|
|
242
272
|
For related tables, also check:
|
|
243
273
|
|
|
244
274
|
- aliases are readable
|
|
245
|
-
- platform-managed
|
|
275
|
+
- platform-managed child registration recursively resolves parent
|
|
276
|
+
`MetaTableForeignKey(...)` targets
|
|
246
277
|
- external child registration requests map FK targets to the registered parent UIDs
|
|
247
278
|
- query results still match the expected response contract
|
|
248
279
|
|
{mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/meta_tables/data_nodes/build_operations.py
RENAMED
|
@@ -94,10 +94,15 @@ def _(value: type[Any]) -> Any:
|
|
|
94
94
|
|
|
95
95
|
time_index_metadata = value.get_time_index_metadata()
|
|
96
96
|
uid = getattr(time_index_metadata, "uid", None)
|
|
97
|
+
if uid in (None, ""):
|
|
98
|
+
value.register()
|
|
99
|
+
time_index_metadata = value.get_time_index_metadata()
|
|
100
|
+
uid = getattr(time_index_metadata, "uid", None)
|
|
101
|
+
|
|
97
102
|
if uid in (None, ""):
|
|
98
103
|
raise ValueError(
|
|
99
|
-
"PlatformTimeIndexMetaData config
|
|
100
|
-
"
|
|
104
|
+
"PlatformTimeIndexMetaData config value register() did not bind "
|
|
105
|
+
"TimeIndexMetaData metadata before hashing."
|
|
101
106
|
)
|
|
102
107
|
return {
|
|
103
108
|
"__type__": "platform_time_index_metadata",
|
{mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/meta_tables/data_nodes/data_nodes.py
RENAMED
|
@@ -29,7 +29,11 @@ from mainsequence.client.utils import META_TABLES_CONSTANTS as CONSTANTS
|
|
|
29
29
|
from mainsequence.instrumentation import tracer
|
|
30
30
|
from mainsequence.logconf import logger
|
|
31
31
|
from mainsequence.meta_tables import PlatformTimeIndexMetaData
|
|
32
|
-
from mainsequence.meta_tables.data_nodes.persist_managers import
|
|
32
|
+
from mainsequence.meta_tables.data_nodes.persist_managers import (
|
|
33
|
+
APIPersistManager,
|
|
34
|
+
PersistManager,
|
|
35
|
+
ensure_registered_storage_table,
|
|
36
|
+
)
|
|
33
37
|
|
|
34
38
|
from .models import BaseConfiguration
|
|
35
39
|
from .namespacing import current_hash_namespace
|
|
@@ -625,34 +629,15 @@ class DataNode(DataAccessMixin, ABC):
|
|
|
625
629
|
|
|
626
630
|
@storage_table.setter
|
|
627
631
|
def storage_table(self, value: type[PlatformTimeIndexMetaData]) -> None:
|
|
628
|
-
|
|
629
|
-
raise TypeError(
|
|
630
|
-
"DataNode storage_table is required and must be a "
|
|
631
|
-
"PlatformTimeIndexMetaData model class."
|
|
632
|
-
)
|
|
633
|
-
is_time_index_model_class = isinstance(value, type) and issubclass(
|
|
634
|
-
value, PlatformTimeIndexMetaData
|
|
635
|
-
)
|
|
636
|
-
if not is_time_index_model_class:
|
|
637
|
-
raise TypeError(
|
|
638
|
-
"DataNode storage_table must be a PlatformTimeIndexMetaData model class; "
|
|
639
|
-
f"got {type(value).__name__}."
|
|
640
|
-
)
|
|
641
|
-
if value.get_time_index_metadata() is None:
|
|
642
|
-
raise ValueError(
|
|
643
|
-
"DataNode storage_table must be registered before construction."
|
|
644
|
-
)
|
|
645
|
-
if value.get_meta_table_uid() in (None, ""):
|
|
646
|
-
raise ValueError("DataNode storage_table must provide a MetaTable UID.")
|
|
647
|
-
if value.get_data_source_uid() in (None, ""):
|
|
648
|
-
raise ValueError("DataNode storage_table must provide a data-source UID.")
|
|
649
|
-
self._storage_table = value
|
|
632
|
+
self._storage_table = ensure_registered_storage_table(value, context="DataNode")
|
|
650
633
|
|
|
651
634
|
@property
|
|
652
635
|
def storage_metadata(self) -> Any:
|
|
653
636
|
storage_metadata = self.storage_table.get_time_index_metadata()
|
|
654
637
|
if storage_metadata is None:
|
|
655
|
-
raise ValueError(
|
|
638
|
+
raise ValueError(
|
|
639
|
+
"DataNode storage_table registration metadata is unavailable after register()."
|
|
640
|
+
)
|
|
656
641
|
return storage_metadata
|
|
657
642
|
|
|
658
643
|
def _initialize_configuration(self, init_kwargs: dict) -> None:
|
{mainsequence-4.1.9 → mainsequence-4.1.11}/mainsequence/meta_tables/data_nodes/persist_managers.py
RENAMED
|
@@ -77,6 +77,40 @@ def get_data_node_source_code_git_hash(DataNodeClass: type[Any]) -> str:
|
|
|
77
77
|
return hash_object.hexdigest()
|
|
78
78
|
|
|
79
79
|
|
|
80
|
+
def ensure_registered_storage_table(
|
|
81
|
+
storage_table: type[PlatformTimeIndexMetaData],
|
|
82
|
+
*,
|
|
83
|
+
context: str,
|
|
84
|
+
) -> type[PlatformTimeIndexMetaData]:
|
|
85
|
+
if storage_table is None:
|
|
86
|
+
raise TypeError(
|
|
87
|
+
f"{context} storage_table is required and must be a "
|
|
88
|
+
"PlatformTimeIndexMetaData model class."
|
|
89
|
+
)
|
|
90
|
+
if not isinstance(storage_table, type) or not issubclass(
|
|
91
|
+
storage_table,
|
|
92
|
+
PlatformTimeIndexMetaData,
|
|
93
|
+
):
|
|
94
|
+
raise TypeError(
|
|
95
|
+
f"{context} storage_table must be a PlatformTimeIndexMetaData "
|
|
96
|
+
f"model class; got {type(storage_table).__name__}."
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
if storage_table.get_time_index_metadata() is None:
|
|
100
|
+
storage_table.register()
|
|
101
|
+
|
|
102
|
+
storage_metadata = storage_table.get_time_index_metadata()
|
|
103
|
+
if storage_metadata is None:
|
|
104
|
+
raise ValueError(
|
|
105
|
+
f"{context} storage_table.register() did not bind TimeIndexMetaData metadata."
|
|
106
|
+
)
|
|
107
|
+
if storage_table.get_meta_table_uid() in (None, ""):
|
|
108
|
+
raise ValueError(f"{context} storage_table must provide a MetaTable UID.")
|
|
109
|
+
if storage_table.get_data_source_uid() in (None, ""):
|
|
110
|
+
raise ValueError(f"{context} storage_table must provide a data-source UID.")
|
|
111
|
+
return storage_table
|
|
112
|
+
|
|
113
|
+
|
|
80
114
|
class BasePersistManager:
|
|
81
115
|
UPDATE_CLASS: ClassVar[type[Any] | None] = None
|
|
82
116
|
UPDATE_DETAILS_CLASS: ClassVar[type[Any] | None] = None
|
|
@@ -114,36 +148,15 @@ class BasePersistManager:
|
|
|
114
148
|
def _validate_storage_table(
|
|
115
149
|
storage_table: type[PlatformTimeIndexMetaData],
|
|
116
150
|
) -> type[PlatformTimeIndexMetaData]:
|
|
117
|
-
|
|
118
|
-
raise TypeError(
|
|
119
|
-
"PersistManager storage_table is required and must be a "
|
|
120
|
-
"PlatformTimeIndexMetaData model class."
|
|
121
|
-
)
|
|
122
|
-
if not isinstance(storage_table, type) or not issubclass(
|
|
123
|
-
storage_table,
|
|
124
|
-
PlatformTimeIndexMetaData,
|
|
125
|
-
):
|
|
126
|
-
raise TypeError(
|
|
127
|
-
"PersistManager storage_table must be a PlatformTimeIndexMetaData "
|
|
128
|
-
f"model class; got {type(storage_table).__name__}."
|
|
129
|
-
)
|
|
130
|
-
storage_metadata = storage_table.get_time_index_metadata()
|
|
131
|
-
if storage_metadata is None:
|
|
132
|
-
raise ValueError(
|
|
133
|
-
"PersistManager storage_table must be registered before "
|
|
134
|
-
"constructing the DataNode."
|
|
135
|
-
)
|
|
136
|
-
if storage_table.get_meta_table_uid() in (None, ""):
|
|
137
|
-
raise ValueError("PersistManager storage_table must provide a MetaTable UID.")
|
|
138
|
-
if storage_table.get_data_source_uid() in (None, ""):
|
|
139
|
-
raise ValueError("PersistManager storage_table must provide a data-source UID.")
|
|
140
|
-
return storage_table
|
|
151
|
+
return ensure_registered_storage_table(storage_table, context="PersistManager")
|
|
141
152
|
|
|
142
153
|
@property
|
|
143
154
|
def storage_metadata(self) -> Any:
|
|
144
155
|
storage_metadata = self.storage_table.get_time_index_metadata()
|
|
145
156
|
if storage_metadata is None:
|
|
146
|
-
raise ValueError(
|
|
157
|
+
raise ValueError(
|
|
158
|
+
"PersistManager storage_table registration metadata is unavailable after register()."
|
|
159
|
+
)
|
|
147
160
|
return storage_metadata
|
|
148
161
|
|
|
149
162
|
@property
|