mainsequence 4.0.16__tar.gz → 4.1.2__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 (135) hide show
  1. {mainsequence-4.0.16 → mainsequence-4.1.2}/PKG-INFO +9 -10
  2. {mainsequence-4.0.16 → mainsequence-4.1.2}/README.md +8 -8
  3. mainsequence-4.1.2/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md +380 -0
  4. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/cli/api.py +408 -96
  5. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/cli/cli.py +489 -27
  6. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/client/__init__.py +4 -1
  7. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/client/base.py +1 -2
  8. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/client/data_sources_interfaces/duckdb.py +11 -9
  9. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/client/data_sources_interfaces/sqlite.py +15 -7
  10. mainsequence-4.1.2/mainsequence/client/models_foundry.py +1064 -0
  11. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/client/models_helpers.py +2 -3
  12. mainsequence-4.0.16/mainsequence/client/models_tdag.py → mainsequence-4.1.2/mainsequence/client/models_metatables.py +3679 -4185
  13. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/client/utils.py +39 -30
  14. {mainsequence-4.0.16/mainsequence/tdag → mainsequence-4.1.2/mainsequence}/meta_tables/__init__.py +20 -1
  15. {mainsequence-4.0.16/mainsequence/tdag → mainsequence-4.1.2/mainsequence/meta_tables}/config.py +18 -27
  16. mainsequence-4.1.2/mainsequence/meta_tables/configuration_models.py +5 -0
  17. {mainsequence-4.0.16/mainsequence/tdag → mainsequence-4.1.2/mainsequence/meta_tables}/data_nodes/__init__.py +1 -2
  18. mainsequence-4.1.2/mainsequence/meta_tables/data_nodes/build_operations.py +476 -0
  19. {mainsequence-4.0.16/mainsequence/tdag → mainsequence-4.1.2/mainsequence/meta_tables}/data_nodes/data_nodes.py +194 -504
  20. mainsequence-4.1.2/mainsequence/meta_tables/data_nodes/models.py +106 -0
  21. {mainsequence-4.0.16/mainsequence/tdag → mainsequence-4.1.2/mainsequence/meta_tables}/data_nodes/namespacing.py +10 -7
  22. mainsequence-4.0.16/mainsequence/tdag/base_persist_managers.py → mainsequence-4.1.2/mainsequence/meta_tables/data_nodes/persist_managers.py +244 -230
  23. {mainsequence-4.0.16/mainsequence/tdag → mainsequence-4.1.2/mainsequence/meta_tables}/data_nodes/run_operations.py +142 -111
  24. {mainsequence-4.0.16/mainsequence/tdag → mainsequence-4.1.2/mainsequence}/meta_tables/hashing.py +5 -7
  25. {mainsequence-4.0.16/mainsequence/tdag → mainsequence-4.1.2/mainsequence/meta_tables}/pydantic_metadata.py +20 -31
  26. {mainsequence-4.0.16/mainsequence/tdag → mainsequence-4.1.2/mainsequence}/meta_tables/sqlalchemy_contracts.py +782 -16
  27. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence.egg-info/PKG-INFO +9 -10
  28. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence.egg-info/SOURCES.txt +19 -24
  29. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence.egg-info/requires.txt +0 -1
  30. {mainsequence-4.0.16 → mainsequence-4.1.2}/pyproject.toml +1 -2
  31. {mainsequence-4.0.16 → mainsequence-4.1.2}/tests/test_build_operations_hashing.py +58 -119
  32. {mainsequence-4.0.16 → mainsequence-4.1.2}/tests/test_cli.py +277 -76
  33. {mainsequence-4.0.16 → mainsequence-4.1.2}/tests/test_data_access_mixin_dimension_audit.py +37 -20
  34. mainsequence-4.1.2/tests/test_data_node_storage_dimension_queries.py +316 -0
  35. {mainsequence-4.0.16 → mainsequence-4.1.2}/tests/test_data_node_update_flow.py +95 -46
  36. {mainsequence-4.0.16 → mainsequence-4.1.2}/tests/test_dependency_extras.py +5 -2
  37. {mainsequence-4.0.16 → mainsequence-4.1.2}/tests/test_duckdb_interface_dimensions.py +10 -12
  38. {mainsequence-4.0.16 → mainsequence-4.1.2}/tests/test_filter_normalization.py +60 -45
  39. {mainsequence-4.0.16 → mainsequence-4.1.2}/tests/test_meta_tables_client_models.py +1 -1
  40. {mainsequence-4.0.16 → mainsequence-4.1.2}/tests/test_meta_tables_sqlalchemy_contracts.py +379 -8
  41. {mainsequence-4.0.16 → mainsequence-4.1.2}/tests/test_pod_project_resolution.py +70 -104
  42. {mainsequence-4.0.16 → mainsequence-4.1.2}/tests/test_project_batch_jobs_from_file.py +2 -3
  43. mainsequence-4.1.2/tests/test_run_configuration.py +796 -0
  44. {mainsequence-4.0.16 → mainsequence-4.1.2}/tests/test_secret_client_model.py +1 -1
  45. mainsequence-4.1.2/tests/test_source_table_configuration.py +233 -0
  46. {mainsequence-4.0.16 → mainsequence-4.1.2}/tests/test_sqlite_interface_dimensions.py +7 -7
  47. {mainsequence-4.0.16 → mainsequence-4.1.2}/tests/test_update_runner_uid_runtime.py +91 -8
  48. {mainsequence-4.0.16 → mainsequence-4.1.2}/tests/test_update_statistics.py +2 -2
  49. {mainsequence-4.0.16 → mainsequence-4.1.2}/tests/test_update_uid_guards.py +4 -4
  50. mainsequence-4.0.16/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md +0 -334
  51. mainsequence-4.0.16/mainsequence/client/models_metatables.py +0 -582
  52. mainsequence-4.0.16/mainsequence/tdag/__init__.py +0 -37
  53. mainsequence-4.0.16/mainsequence/tdag/configuration_models.py +0 -23
  54. mainsequence-4.0.16/mainsequence/tdag/data_nodes/build_operations.py +0 -797
  55. mainsequence-4.0.16/mainsequence/tdag/data_nodes/filters.py +0 -85
  56. mainsequence-4.0.16/mainsequence/tdag/data_nodes/models.py +0 -349
  57. mainsequence-4.0.16/mainsequence/tdag/data_nodes/persist_managers.py +0 -131
  58. mainsequence-4.0.16/mainsequence/tdag/filters.py +0 -201
  59. mainsequence-4.0.16/tests/test_data_node_search_join_filters.py +0 -209
  60. mainsequence-4.0.16/tests/test_data_node_storage_dimension_queries.py +0 -508
  61. mainsequence-4.0.16/tests/test_run_configuration.py +0 -413
  62. mainsequence-4.0.16/tests/test_source_table_configuration.py +0 -400
  63. {mainsequence-4.0.16 → mainsequence-4.1.2}/LICENSE +0 -0
  64. {mainsequence-4.0.16 → mainsequence-4.1.2}/agent_scaffold/AGENTS.md +0 -0
  65. {mainsequence-4.0.16 → mainsequence-4.1.2}/agent_scaffold/skills/a2a_communication/SKILL.md +0 -0
  66. {mainsequence-4.0.16 → mainsequence-4.1.2}/agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md +0 -0
  67. {mainsequence-4.0.16 → mainsequence-4.1.2}/agent_scaffold/skills/command_center/adapter_from_api/SKILL.md +0 -0
  68. {mainsequence-4.0.16 → mainsequence-4.1.2}/agent_scaffold/skills/command_center/api_mock_prototyping/SKILL.md +0 -0
  69. {mainsequence-4.0.16 → mainsequence-4.1.2}/agent_scaffold/skills/command_center/app_components/SKILL.md +0 -0
  70. {mainsequence-4.0.16 → mainsequence-4.1.2}/agent_scaffold/skills/command_center/connections/SKILL.md +0 -0
  71. {mainsequence-4.0.16 → mainsequence-4.1.2}/agent_scaffold/skills/command_center/workspace_analysis/SKILL.md +0 -0
  72. {mainsequence-4.0.16 → mainsequence-4.1.2}/agent_scaffold/skills/command_center/workspace_builder/SKILL.md +0 -0
  73. {mainsequence-4.0.16 → mainsequence-4.1.2}/agent_scaffold/skills/command_center/workspace_design/SKILL.md +0 -0
  74. {mainsequence-4.0.16 → mainsequence-4.1.2}/agent_scaffold/skills/dashboards/streamlit/SKILL.md +0 -0
  75. {mainsequence-4.0.16 → mainsequence-4.1.2}/agent_scaffold/skills/data_access/exploration/SKILL.md +0 -0
  76. {mainsequence-4.0.16 → mainsequence-4.1.2}/agent_scaffold/skills/data_publishing/meta_tables/SKILL.md +0 -0
  77. {mainsequence-4.0.16 → mainsequence-4.1.2}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md +0 -0
  78. {mainsequence-4.0.16 → mainsequence-4.1.2}/agent_scaffold/skills/ms-markets/SKILL.md +0 -0
  79. {mainsequence-4.0.16 → mainsequence-4.1.2}/agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md +0 -0
  80. {mainsequence-4.0.16 → mainsequence-4.1.2}/agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md +0 -0
  81. {mainsequence-4.0.16 → mainsequence-4.1.2}/agent_scaffold/skills/project_builder/SKILL.md +0 -0
  82. {mainsequence-4.0.16 → mainsequence-4.1.2}/agent_scaffold/skills/project_to_agent/SKILL.md +0 -0
  83. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/__init__.py +0 -0
  84. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/__main__.py +0 -0
  85. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/bootstrap.py +0 -0
  86. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/cli/__init__.py +0 -0
  87. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/cli/browser_auth.py +0 -0
  88. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/cli/config.py +0 -0
  89. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/cli/docker_utils.py +0 -0
  90. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/cli/doctor.py +0 -0
  91. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/cli/local_ops.py +0 -0
  92. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/cli/model_filters.py +0 -0
  93. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/cli/project_status.py +0 -0
  94. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/cli/pydantic_cli.py +0 -0
  95. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/cli/sdk_utils.py +0 -0
  96. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/cli/ssh_utils.py +0 -0
  97. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/cli/ui.py +0 -0
  98. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/client/agent_runtime_models.py +0 -0
  99. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/client/client.py +0 -0
  100. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/client/command_center/__init__.py +0 -0
  101. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/client/command_center/app_component.py +0 -0
  102. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/client/command_center/connections.py +0 -0
  103. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/client/command_center/data_models.py +0 -0
  104. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/client/command_center/workspace.py +0 -0
  105. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/client/command_center/workspace_snapshot.py +0 -0
  106. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/client/data_sources_interfaces/__init__.py +0 -0
  107. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/client/dtype_codec.py +0 -0
  108. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/client/exceptions.py +0 -0
  109. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/client/fastapi/__init__.py +0 -0
  110. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/client/fastapi/auth.py +0 -0
  111. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/client/models_user.py +0 -0
  112. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/compute_validation.py +0 -0
  113. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/defaults.py +0 -0
  114. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/instrumentation/__init__.py +0 -0
  115. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/instrumentation/utils.py +0 -0
  116. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/logconf.py +0 -0
  117. {mainsequence-4.0.16/mainsequence/tdag → mainsequence-4.1.2/mainsequence/meta_tables}/__main__.py +0 -0
  118. {mainsequence-4.0.16/mainsequence/tdag → mainsequence-4.1.2/mainsequence}/meta_tables/compiled_sql.py +0 -0
  119. {mainsequence-4.0.16/mainsequence/tdag → mainsequence-4.1.2/mainsequence/meta_tables}/data_nodes/utils.py +0 -0
  120. {mainsequence-4.0.16/mainsequence/tdag → mainsequence-4.1.2/mainsequence/meta_tables}/future_registry.py +0 -0
  121. {mainsequence-4.0.16/mainsequence/tdag → mainsequence-4.1.2/mainsequence/meta_tables}/utils.py +0 -0
  122. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence/runtime_flags.py +0 -0
  123. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence.egg-info/dependency_links.txt +0 -0
  124. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence.egg-info/entry_points.txt +0 -0
  125. {mainsequence-4.0.16 → mainsequence-4.1.2}/mainsequence.egg-info/top_level.txt +0 -0
  126. {mainsequence-4.0.16 → mainsequence-4.1.2}/setup.cfg +0 -0
  127. {mainsequence-4.0.16 → mainsequence-4.1.2}/tests/test_auth_precedence.py +0 -0
  128. {mainsequence-4.0.16 → mainsequence-4.1.2}/tests/test_cli_browser_auth.py +0 -0
  129. {mainsequence-4.0.16 → mainsequence-4.1.2}/tests/test_client.py +0 -0
  130. {mainsequence-4.0.16 → mainsequence-4.1.2}/tests/test_command_center_app_component_models.py +0 -0
  131. {mainsequence-4.0.16 → mainsequence-4.1.2}/tests/test_command_center_data_models.py +0 -0
  132. {mainsequence-4.0.16 → mainsequence-4.1.2}/tests/test_command_center_models.py +0 -0
  133. {mainsequence-4.0.16 → mainsequence-4.1.2}/tests/test_logconf.py +0 -0
  134. {mainsequence-4.0.16 → mainsequence-4.1.2}/tests/test_models_user_request_bound_auth.py +0 -0
  135. {mainsequence-4.0.16 → mainsequence-4.1.2}/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.16
3
+ Version: 4.1.2
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
@@ -55,7 +55,6 @@ Description-Content-Type: text/markdown
55
55
  License-File: LICENSE
56
56
  Requires-Dist: cachetools
57
57
  Requires-Dist: click>=8.3.0
58
- Requires-Dist: cloudpickle
59
58
  Requires-Dist: concurrent-log-handler
60
59
  Requires-Dist: numpy
61
60
  Requires-Dist: opentelemetry-api
@@ -93,7 +92,7 @@ The Main Sequence Python SDK is the client and development toolkit for the Main
93
92
 
94
93
  The Main Sequence platform allows you to:
95
94
 
96
- 1. rapidly build and deploy data products and data workflows as a unified API with a normalized structure through `DataNode`s
95
+ 1. rapidly build and deploy data products and data workflows as a unified API with normalized table contracts through `MetaTable`s and `DataNode`s
97
96
  2. rapidly deploy RBAC-enabled dashboards on the platform
98
97
  3. rapidly deploy agents using the Google Agent SDK
99
98
 
@@ -114,7 +113,7 @@ This repository contains the SDK and the documentation used to build and operate
114
113
 
115
114
  Main package areas:
116
115
 
117
- - `mainsequence.tdag`: data orchestration, `DataNode`s, update workflows, and persistence
116
+ - `mainsequence.meta_tables`: `MetaTable`s, `DataNode`s, update workflows, and persistence
118
117
  - `mainsequence.client`: API client models for projects, jobs, data node storages, sharing, and platform resources
119
118
  - `mainsequence.cli`: the `mainsequence` command-line interface
120
119
 
@@ -137,10 +136,10 @@ Recommended entry points:
137
136
 
138
137
  - Tutorial:
139
138
  - [Setting a Project (CLI)](docs/tutorial/setting_a_project.md)
140
- - [Creating a Data Node](docs/tutorial/creating_a_simple_data_node.md)
141
139
  - [Working With MetaTables](docs/tutorial/working_with_meta_tables.md)
142
- - [Create Your First API](docs/tutorial/create_your_first_api.md)
140
+ - [Creating a Data Node](docs/tutorial/creating_a_simple_data_node.md)
143
141
  - [Role-Based Access Control](docs/tutorial/role_based_access_control.md)
142
+ - [Create Your First API](docs/tutorial/create_your_first_api.md)
144
143
  - [Turn Your Project Into an Agent](docs/tutorial/project_to_agent.md)
145
144
  - Knowledge:
146
145
  - [Data Nodes](docs/knowledge/data_nodes.md)
@@ -189,10 +188,10 @@ mainsequence project build_local_venv --path .
189
188
 
190
189
  From there, the normal learning path is:
191
190
 
192
- 1. create your first `DataNode`
193
- 2. model app-facing relational data with backend-managed `MetaTable`s when needed
194
- 3. add an API or another application surface
195
- 4. understand sharing and RBAC
191
+ 1. model your first canonical table with a backend-managed `MetaTable`
192
+ 2. create your first `DataNode` as an opinionated MetaTable-backed update workflow
193
+ 3. understand sharing and RBAC for published tables
194
+ 4. add an API or another application surface
196
195
  5. schedule jobs
197
196
  6. build dashboards or downstream consumers
198
197
  7. package the project as an agent-facing surface when the repository is ready
@@ -13,7 +13,7 @@ The Main Sequence Python SDK is the client and development toolkit for the Main
13
13
 
14
14
  The Main Sequence platform allows you to:
15
15
 
16
- 1. rapidly build and deploy data products and data workflows as a unified API with a normalized structure through `DataNode`s
16
+ 1. rapidly build and deploy data products and data workflows as a unified API with normalized table contracts through `MetaTable`s and `DataNode`s
17
17
  2. rapidly deploy RBAC-enabled dashboards on the platform
18
18
  3. rapidly deploy agents using the Google Agent SDK
19
19
 
@@ -34,7 +34,7 @@ This repository contains the SDK and the documentation used to build and operate
34
34
 
35
35
  Main package areas:
36
36
 
37
- - `mainsequence.tdag`: data orchestration, `DataNode`s, update workflows, and persistence
37
+ - `mainsequence.meta_tables`: `MetaTable`s, `DataNode`s, update workflows, and persistence
38
38
  - `mainsequence.client`: API client models for projects, jobs, data node storages, sharing, and platform resources
39
39
  - `mainsequence.cli`: the `mainsequence` command-line interface
40
40
 
@@ -57,10 +57,10 @@ Recommended entry points:
57
57
 
58
58
  - Tutorial:
59
59
  - [Setting a Project (CLI)](docs/tutorial/setting_a_project.md)
60
- - [Creating a Data Node](docs/tutorial/creating_a_simple_data_node.md)
61
60
  - [Working With MetaTables](docs/tutorial/working_with_meta_tables.md)
62
- - [Create Your First API](docs/tutorial/create_your_first_api.md)
61
+ - [Creating a Data Node](docs/tutorial/creating_a_simple_data_node.md)
63
62
  - [Role-Based Access Control](docs/tutorial/role_based_access_control.md)
63
+ - [Create Your First API](docs/tutorial/create_your_first_api.md)
64
64
  - [Turn Your Project Into an Agent](docs/tutorial/project_to_agent.md)
65
65
  - Knowledge:
66
66
  - [Data Nodes](docs/knowledge/data_nodes.md)
@@ -109,10 +109,10 @@ mainsequence project build_local_venv --path .
109
109
 
110
110
  From there, the normal learning path is:
111
111
 
112
- 1. create your first `DataNode`
113
- 2. model app-facing relational data with backend-managed `MetaTable`s when needed
114
- 3. add an API or another application surface
115
- 4. understand sharing and RBAC
112
+ 1. model your first canonical table with a backend-managed `MetaTable`
113
+ 2. create your first `DataNode` as an opinionated MetaTable-backed update workflow
114
+ 3. understand sharing and RBAC for published tables
115
+ 4. add an API or another application surface
116
116
  5. schedule jobs
117
117
  6. build dashboards or downstream consumers
118
118
  7. package the project as an agent-facing surface when the repository is ready
@@ -0,0 +1,380 @@
1
+ ---
2
+ name: mainsequence-data-nodes
3
+ description: Use this skill when the task is about producing, changing, validating, or reviewing Main Sequence DataNode update processes. This skill owns DataNode update configuration, dependencies, update logic, hashing, namespaces, and validation against a PlatformTimeIndexMetaData storage contract. It does not own generic MetaTable governance, API route contracts, scheduling, sharing policy, or storage registration internals.
4
+ ---
5
+
6
+ # Main Sequence Data Nodes
7
+
8
+ ## Overview
9
+
10
+ Use this skill when the task changes a DataNode producer.
11
+
12
+ A DataNode is an update process. It is not the canonical storage model. Storage
13
+ is defined by a registered `PlatformTimeIndexMetaData` SQLAlchemy model.
14
+
15
+ Canonical workflow:
16
+
17
+ 1. Define a `PlatformTimeIndexMetaData` storage class.
18
+ 2. Register or bind that storage class before constructing the DataNode.
19
+ 3. Construct the DataNode with `config=...` and `storage_table=StorageClass`.
20
+ 4. Return a DataFrame from `update()` that matches the storage class contract.
21
+
22
+ ## This Skill Can Do
23
+
24
+ - create a new `DataNode` update process
25
+ - modify an existing `DataNode` update process
26
+ - review whether a DataNode change affects update identity or table contract
27
+ - define or refactor `DataNodeConfiguration`
28
+ - classify config fields into update identity and hash-excluded descriptive metadata
29
+ - implement or review:
30
+ - `dependencies()`
31
+ - `update()`
32
+ - `prepare_update_statistics()`
33
+ - `get_asset_list()` when the update is asset scoped
34
+ - design single-index or `(time_index, unique_identifier)` DataFrame outputs
35
+ - validate output shape against a `PlatformTimeIndexMetaData` storage contract
36
+ - define explicit `hash_namespace(...)` validation strategy
37
+ - write or review DataNode smoke tests
38
+ - decide whether a consumer should use `APIDataNode`
39
+
40
+ ## This Skill Must Not Claim
41
+
42
+ This skill must not claim ownership of:
43
+
44
+ - generic MetaTable registration or governed operation semantics
45
+ - storage creation inside `DataNode` or `PersistManager`
46
+ - HTTP route design or FastAPI response contracts
47
+ - workspace/widget layout payloads
48
+ - job creation, scheduling, image pinning, or release creation
49
+ - RBAC or sharing policy
50
+ - domain strategy semantics
51
+
52
+ If the task depends on one of those areas, route it explicitly instead of guessing.
53
+
54
+ ## Route Adjacent Work
55
+
56
+ - MetaTables and storage contracts:
57
+ `.agents/skills/mainsequence/data_publishing/meta_tables/SKILL.md`
58
+ - APIs and FastAPI:
59
+ `.agents/skills/mainsequence/application_surfaces/api_surfaces/SKILL.md`
60
+ - Command Center workspaces:
61
+ `.agents/skills/mainsequence/command_center/workspace_builder/SKILL.md`
62
+ - AppComponents and custom forms:
63
+ `.agents/skills/mainsequence/command_center/app_components/SKILL.md`
64
+ - Jobs, images, resources, and releases:
65
+ `.agents/skills/mainsequence/platform_operations/orchestration_and_releases/SKILL.md`
66
+ - RBAC and sharing:
67
+ `.agents/skills/mainsequence/platform_operations/access_control_and_sharing/SKILL.md`
68
+
69
+ ## Read First
70
+
71
+ 1. `docs/knowledge/data_nodes.md`
72
+ 2. `docs/knowledge/meta_tables/sqlalchemy.md`
73
+
74
+ ## Inputs This Skill Needs
75
+
76
+ Before changing code, collect or infer:
77
+
78
+ - dataset meaning
79
+ - registered `PlatformTimeIndexMetaData` storage class or the class to create
80
+ - expected time index and identity index shape
81
+ - expected columns and dtypes from the storage class
82
+ - upstream dependencies
83
+ - whether the update is asset scoped
84
+ - first-run or backfill bounds
85
+ - whether the change must preserve the existing table contract
86
+
87
+ If one of these is unknown and changes the contract, stop and resolve it before
88
+ implementation.
89
+
90
+ ## Required Decisions
91
+
92
+ For every non-trivial DataNode task, make these decisions explicitly:
93
+
94
+ 1. Is this a new dataset or the same dataset?
95
+ 2. Is this change storage-contract work or update-process work?
96
+ 3. Is the storage class already registered or should the MetaTable skill handle it?
97
+ 4. Is the node single-index or MultiIndex?
98
+ 5. Does the first validation run happen under an explicit `hash_namespace(...)`?
99
+
100
+ ## Build Rules
101
+
102
+ ### 1. Treat Storage As A PlatformTimeIndexMetaData Contract
103
+
104
+ The following are storage-contract decisions:
105
+
106
+ - table namespace and identifier
107
+ - SQLAlchemy columns and dtypes
108
+ - time index name
109
+ - identity index names
110
+ - foreign keys
111
+ - table description and labels
112
+
113
+ Do not put those concerns in `DataNodeConfiguration`.
114
+
115
+ Minimal pattern:
116
+
117
+ ```python
118
+ import datetime
119
+
120
+ from sqlalchemy import DateTime, Float, MetaData
121
+ from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
122
+
123
+ from mainsequence.meta_tables import PlatformTimeIndexMetaData
124
+
125
+
126
+ class Base(DeclarativeBase):
127
+ metadata = MetaData()
128
+
129
+
130
+ class PricesTable(PlatformTimeIndexMetaData, Base):
131
+ __metatable_namespace__ = "<domain_namespace>"
132
+ __metatable_identifier__ = "<table_identifier>"
133
+ __time_index_name__ = "time_index"
134
+ __index_names__ = ["time_index", "unique_identifier"]
135
+
136
+ time_index: Mapped[datetime.datetime] = mapped_column(
137
+ DateTime(timezone=True),
138
+ nullable=False,
139
+ )
140
+ unique_identifier: Mapped[str] = mapped_column(nullable=False)
141
+ close: Mapped[float] = mapped_column(Float, nullable=False)
142
+ ```
143
+
144
+ Register or bind storage before constructing the DataNode:
145
+
146
+ ```python
147
+ PricesTable.register(data_source_uid=data_source_uid)
148
+ ```
149
+
150
+ or:
151
+
152
+ ```python
153
+ PricesTable.bind_meta_table(existing_meta_table)
154
+ ```
155
+
156
+ ### 2. Keep DataNode As Update Logic
157
+
158
+ The DataNode constructor should accept:
159
+
160
+ - a `DataNodeConfiguration`
161
+ - a registered `storage_table: type[PlatformTimeIndexMetaData]`
162
+ - optional `hash_namespace`
163
+
164
+ The constructor `storage_table` is the output storage contract. Keep it out of
165
+ `DataNodeConfiguration`.
166
+
167
+ If the DataNode needs to select another DataNode's storage table as a
168
+ dependency, put that dependency storage reference in the config as
169
+ `type[PlatformTimeIndexMetaData]`. Do not add an extra constructor argument for
170
+ dependency storage tables. Config values of this type are hashed by the bound
171
+ `TimeIndexMetaData.uid` from `StorageClass.__time_index_metadata__`, so the class
172
+ must be registered or bound before DataNode construction.
173
+
174
+ Do not accept `test_node`. It has been removed. Use explicit
175
+ `hash_namespace(...)` or `hash_namespace="..."`.
176
+
177
+ Pattern:
178
+
179
+ ```python
180
+ from mainsequence.meta_tables import DataNode, DataNodeConfiguration
181
+ from mainsequence.meta_tables import PlatformTimeIndexMetaData
182
+
183
+
184
+ class PricesConfig(DataNodeConfiguration):
185
+ shard_id: str
186
+
187
+
188
+ class PricesUpdate(DataNode):
189
+ def __init__(
190
+ self,
191
+ config: PricesConfig,
192
+ storage_table: type[PlatformTimeIndexMetaData],
193
+ *,
194
+ hash_namespace: str | None = None,
195
+ ):
196
+ self.config = config
197
+ super().__init__(
198
+ config=config,
199
+ storage_table=storage_table,
200
+ hash_namespace=hash_namespace,
201
+ )
202
+
203
+ def dependencies(self):
204
+ return {}
205
+
206
+ def update(self):
207
+ ...
208
+ ```
209
+
210
+ ### 3. Configuration Is Update-Scoped By Default
211
+
212
+ Every `DataNodeConfiguration` field participates in `update_hash` by default.
213
+
214
+ Do not use:
215
+
216
+ - `json_schema_extra={"update_only": True}`
217
+ - `json_schema_extra={"runtime_only": True}`
218
+ - `json_schema_extra={"ignore_from_storage_hash": True}`
219
+ - `_ARGS_IGNORE_IN_STORAGE_HASH`
220
+
221
+ Those are removed. The only supported opt-out is:
222
+
223
+ ```python
224
+ Field(..., json_schema_extra={"hash_excluded": True})
225
+ ```
226
+
227
+ Use `hash_excluded` only for descriptive metadata that must not affect update
228
+ identity. If a field changes output values, dependencies, source choice, or
229
+ updater scope, it must remain a normal config field.
230
+
231
+ ### 4. `hash_namespace` Is Isolation Only
232
+
233
+ Use explicit `hash_namespace(...)` for:
234
+
235
+ - namespaced tests
236
+ - isolated experimentation
237
+ - shared-backend safety
238
+
239
+ Do not use namespace to encode business meaning.
240
+
241
+ Do not use `test_node=True`; it is not supported.
242
+
243
+ ### 5. `update()` Should Be Incremental By Default
244
+
245
+ Use `UpdateStatistics`.
246
+
247
+ Do not fetch or return full history every run unless there is a documented
248
+ reason.
249
+
250
+ ### 6. `time_index` Must Be Nanosecond UTC
251
+
252
+ Every non-empty DataFrame returned by `update()` must have its first index
253
+ level named `time_index` with dtype exactly `datetime64[ns, UTC]`.
254
+
255
+ Do not rely on `pd.Timestamp.now("UTC").normalize()` or
256
+ `pd.Timestamp.now("UTC").floor(...)` alone. Some pandas versions preserve
257
+ microsecond resolution and produce `datetime64[us, UTC]`, which the DataNode
258
+ runtime rejects.
259
+
260
+ For a single-index DataFrame, construct the index with an explicit dtype:
261
+
262
+ ```python
263
+ time_index = pd.Timestamp.now("UTC").normalize()
264
+ return pd.DataFrame(
265
+ {"close": [100.0]},
266
+ index=pd.DatetimeIndex(
267
+ [time_index],
268
+ name="time_index",
269
+ dtype="datetime64[ns, UTC]",
270
+ ),
271
+ )
272
+ ```
273
+
274
+ For a MultiIndex DataFrame, construct the time level separately:
275
+
276
+ ```python
277
+ time_index = pd.DatetimeIndex(
278
+ [row[0] for row in rows],
279
+ dtype="datetime64[ns, UTC]",
280
+ )
281
+ index = pd.MultiIndex.from_arrays(
282
+ [
283
+ time_index,
284
+ [row[1] for row in rows],
285
+ ],
286
+ names=["time_index", "unique_identifier"],
287
+ )
288
+ ```
289
+
290
+ The validator error to prevent is:
291
+
292
+ ```text
293
+ Time index must be datetime64[ns, UTC]
294
+ ```
295
+
296
+ ### 7. Dependencies Must Be Deterministic
297
+
298
+ Dependencies belong in constructor setup and `dependencies()`.
299
+
300
+ Dependency storage-table selection belongs in `DataNodeConfiguration`, because
301
+ changing it changes the dependency graph and update identity.
302
+
303
+ Do not construct dependency graphs dynamically inside `update()`.
304
+
305
+ ### 8. Asset-Scoped Updates Must Be Explicit
306
+
307
+ If the node emits `(time_index, unique_identifier)`:
308
+
309
+ - `unique_identifier` should represent an Asset identity
310
+ - asset scope should live in update configuration when it affects the updater
311
+ - `get_asset_list()` must reflect the effective updater asset scope when the
312
+ workflow uses that hook
313
+ - missing assets should be resolved or registered by the relevant workflow
314
+
315
+ ### 9. Foreign Keys Belong To The Storage Contract
316
+
317
+ For new code, model foreign keys on the `PlatformTimeIndexMetaData` storage
318
+ class or route the storage-contract work to the MetaTable skill.
319
+
320
+ Do not add DataNode configuration fields just to mutate storage metadata.
321
+
322
+ ### 10. Metadata Belongs To Storage
323
+
324
+ Production-quality table identifiers, descriptions, labels, column docs, and
325
+ foreign-key metadata belong to the storage class/MetaTable registration path.
326
+
327
+ Do not use `DataNodeMetaData` or `RecordDefinition` as the canonical schema
328
+ surface for new DataNode work.
329
+
330
+ ## Review Rules
331
+
332
+ When reviewing an existing DataNode, look for:
333
+
334
+ - output storage contract hidden in `DataNodeConfiguration`
335
+ - dependency storage table passed as an ad hoc constructor argument
336
+ - old `RecordDefinition` or `DataNodeMetaData` schema patterns
337
+ - `update_only`, `runtime_only`, or `ignore_from_storage_hash`
338
+ - `test_node=True`
339
+ - missing explicit `storage_table`
340
+ - accidental storage registration inside the DataNode
341
+ - wrong meaning/scope/hash-excluded split
342
+ - misuse of `hash_namespace`
343
+ - non-incremental `update()` behavior
344
+ - hidden dependency creation inside `update()`
345
+ - invalid asset-indexed output shape
346
+ - `time_index` dtype that is not exactly `datetime64[ns, UTC]`
347
+ - DataFrame columns that do not match the `PlatformTimeIndexMetaData` class
348
+
349
+ ## Validation Checklist
350
+
351
+ Do not claim success until you have checked:
352
+
353
+ - the relevant docs were read first
354
+ - storage is a registered or bound `PlatformTimeIndexMetaData` class
355
+ - the DataNode constructor requires `storage_table`
356
+ - dependency storage-table references live in config and are registered or bound
357
+ - config fields are updater-scoped by default
358
+ - no removed hash metadata markers remain
359
+ - no `test_node` usage remains
360
+ - `dependencies()` is deterministic
361
+ - `update()` is incremental
362
+ - the DataFrame shape matches the storage class
363
+ - non-empty outputs have first index level `time_index` with dtype `datetime64[ns, UTC]`
364
+ - the first validation run uses explicit `hash_namespace(...)` when it touches a shared backend
365
+
366
+ For asset-scoped updates, also check:
367
+
368
+ - `get_asset_list()` is correct when used
369
+ - no duplicate `(time_index, unique_identifier)` rows are emitted
370
+ - assets exist or are registered idempotently when needed
371
+
372
+ ## This Skill Must Stop And Escalate When
373
+
374
+ - the change may break an existing published table contract and the versioning decision is unclear
375
+ - the intended storage class or MetaTable registration path is unclear
376
+ - the node needs asset identities but the asset-resolution strategy is unclear
377
+ - the task is actually an API, MetaTable, orchestration, or sharing problem
378
+ - docs, skill instructions, and code disagree on hashing or runtime behavior
379
+
380
+ Do not guess through contract changes.