@shundoo-ai/dsh-cosmic 1.0.19 → 1.0.21

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 (245) hide show
  1. package/cordis.patch.yml +5 -5
  2. package/package.json +1 -1
  3. package/skills/cosmic-build/CHANGELOG.md +4 -0
  4. package/skills/cosmic-build/LOCAL-PATCHES.md +91 -40
  5. package/skills/cosmic-build/SKILL.md +46 -13
  6. package/skills/cosmic-build/assets/platform-report-filters/TypedReportFilterFields.java +169 -0
  7. package/skills/cosmic-build/assets/platform-report-filters/platform.patch +98 -0
  8. package/skills/cosmic-build/assets/report-algo-benchmark/ReportAlgoBenchmark.java +48 -0
  9. package/skills/cosmic-build/assets/report-algo-benchmark/ReportBenchmarkLogFactory.java +7 -0
  10. package/skills/cosmic-build/examples/java-rules/CosmicEntryQuery.java +26 -0
  11. package/skills/cosmic-build/examples/java-rules/CosmicReportAggregation.java +35 -0
  12. package/skills/cosmic-build/examples/java-rules/CosmicReportFilters.java +77 -0
  13. package/skills/cosmic-build/examples/java-rules/CosmicTimeAdapter.java +25 -0
  14. package/skills/cosmic-build/examples/java-rules/CosmicTimeAdapterCheck.java +26 -0
  15. package/skills/cosmic-build/knowledge/cosmic/platform/api/algo/GroupbyDataSet.md +47 -0
  16. package/skills/cosmic-build/knowledge/cosmic/platform/api/entity/report/FilterInfo.md +12 -12
  17. package/skills/cosmic-build/knowledge/cosmic/platform/api/entity/report/FilterItemInfo.md +2 -0
  18. package/skills/cosmic-build/knowledge/cosmic/platform/form-plugin.md +2 -4
  19. package/skills/cosmic-build/knowledge/cosmic/platform/java-type-contracts.md +11 -2
  20. package/skills/cosmic-build/knowledge/cosmic/platform/list-plugin.md +18 -14
  21. package/skills/cosmic-build/knowledge/cosmic/platform/operation-plugin.md +4 -4
  22. package/skills/cosmic-build/knowledge/cosmic/platform/reportlistdata-plugin.md +17 -9
  23. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/operation/cross-entity-aggregate-writeback.md +2 -0
  24. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/operation/pre-post-operation.md +15 -7
  25. package/skills/cosmic-build/knowledge/cosmic/platform/sdk/sdk-algo.md +24 -68
  26. package/skills/cosmic-build/knowledge/cosmic/platform/workflow-plugin.md +2 -7
  27. package/skills/cosmic-build/references/application-context.md +35 -1
  28. package/skills/cosmic-build/references/common-reference.md +1 -1
  29. package/skills/cosmic-build/references/compile-guide.md +55 -68
  30. package/skills/cosmic-build/references/delivery.md +59 -7
  31. package/skills/cosmic-build/references/execution-contract.md +38 -0
  32. package/skills/cosmic-build/references/form-discovery.md +7 -1
  33. package/skills/cosmic-build/references/java-plugin-delivery.md +32 -162
  34. package/skills/cosmic-build/references/operation-dispatch.md +17 -0
  35. package/skills/cosmic-build/references/plugin-handoff-input.md +131 -0
  36. package/skills/cosmic-build/references/plugin-runtime-routing.md +1 -1
  37. package/skills/cosmic-build/references/report-filter-contract.md +78 -0
  38. package/skills/cosmic-build/references/sdk-inspection.md +23 -0
  39. package/skills/cosmic-build/schemas/data-model-authoring.schema.json +2 -3
  40. package/skills/cosmic-build/scripts/_http_client.py +55 -55
  41. package/skills/cosmic-build/scripts/appconfig_load.py +2 -2
  42. package/skills/cosmic-build/scripts/apply_report_filter_extension.py +39 -0
  43. package/skills/cosmic-build/scripts/bootstrap_cosmic_context.py +37 -2
  44. package/skills/cosmic-build/scripts/compile_plugin_source.py +279 -156
  45. package/skills/cosmic-build/scripts/execution_binding.py +40 -0
  46. package/skills/cosmic-build/scripts/inspect_platform_sdk.py +169 -0
  47. package/skills/cosmic-build/scripts/kd_api.py +23 -22
  48. package/skills/cosmic-build/scripts/kd_auth.py +1 -1
  49. package/skills/cosmic-build/scripts/kd_env_loader.py +19 -10
  50. package/skills/cosmic-build/scripts/kd_token_cache.py +2 -2
  51. package/skills/cosmic-build/scripts/kingscript_sdk_sync.py +350 -0
  52. package/skills/cosmic-build/scripts/kingscript_source_gate.py +11 -8
  53. package/skills/cosmic-build/scripts/platform_api_contract.py +246 -11
  54. package/skills/cosmic-build/scripts/plugin_corpus/build_index.py +403 -0
  55. package/skills/cosmic-build/scripts/plugin_corpus/jvm_classfile.py +281 -0
  56. package/skills/cosmic-build/scripts/plugin_corpus/measure_api_usage.py +446 -0
  57. package/skills/cosmic-build/scripts/plugin_source_gate.py +48 -7
  58. package/skills/cosmic-build/scripts/plugin_target_sdk.py +45 -0
  59. package/skills/cosmic-build/scripts/prepare_plugin_handoff.py +493 -47
  60. package/skills/cosmic-build/scripts/preview_url.py +35 -6
  61. package/skills/cosmic-build/scripts/query_plugin_handoff.py +250 -0
  62. package/skills/cosmic-build/scripts/register_plugins.py +28 -2
  63. package/skills/cosmic-build/scripts/render_output_cards.py +42 -9
  64. package/skills/cosmic-build/scripts/report_algo_benchmark.py +46 -0
  65. package/skills/cosmic-build/scripts/run.py +2 -2
  66. package/skills/cosmic-build/scripts/sync_generated_locale_assets.py +43 -0
  67. package/skills/cosmic-build/scripts/test/fixtures/ReportSdkCheck.java +64 -0
  68. package/skills/cosmic-build/scripts/test/fixtures/java_context_booking_native.json +541 -0
  69. package/skills/cosmic-build/scripts/test/test_add_rule_business_contract.py +85 -14
  70. package/skills/cosmic-build/scripts/test/test_batch5_drift_governance.py +12 -3
  71. package/skills/cosmic-build/scripts/test/test_build_meta_order.py +0 -16
  72. package/skills/cosmic-build/scripts/test/test_cli_continuation.py +87 -0
  73. package/skills/cosmic-build/scripts/test/test_compile_plugin_source.py +224 -3
  74. package/skills/cosmic-build/scripts/test/test_control_authoring.py +123 -4
  75. package/skills/cosmic-build/scripts/test/test_cosmic_sdk_examples.py +141 -0
  76. package/skills/cosmic-build/scripts/test/test_data_model_contract.py +30 -5
  77. package/skills/cosmic-build/scripts/test/test_delivery_stage_boundary.py +2 -10
  78. package/skills/cosmic-build/scripts/test/test_dym_sync_background.py +11 -0
  79. package/skills/cosmic-build/scripts/test/test_dynamic_form_metadata_only_contract.py +59 -1
  80. package/skills/cosmic-build/scripts/test/test_guide_skeleton_contract.py +87 -43
  81. package/skills/cosmic-build/scripts/test/test_inspect_platform_sdk.py +71 -0
  82. package/skills/cosmic-build/scripts/test/test_kd_api.py +67 -19
  83. package/skills/cosmic-build/scripts/test/test_kingscript_sdk_sync.py +221 -0
  84. package/skills/cosmic-build/scripts/test/test_kingscript_source_gate.py +10 -0
  85. package/skills/cosmic-build/scripts/test/test_list_configuration_closure.py +18 -0
  86. package/skills/cosmic-build/scripts/test/test_long_session_regressions.py +116 -0
  87. package/skills/cosmic-build/scripts/test/test_mcp_cli.py +524 -0
  88. package/skills/cosmic-build/scripts/test/test_modeling_input_recovery.py +141 -0
  89. package/skills/cosmic-build/scripts/test/test_open_world_cli_catalog.py +36 -2
  90. package/skills/cosmic-build/scripts/test/test_platform_api_contract.py +161 -1
  91. package/skills/cosmic-build/scripts/test/test_plugin_business_contract.py +91 -0
  92. package/skills/cosmic-build/scripts/test/test_plugin_corpus.py +195 -0
  93. package/skills/cosmic-build/scripts/test/test_plugin_handoff.py +346 -1
  94. package/skills/cosmic-build/scripts/test/test_plugin_target_sdk.py +70 -0
  95. package/skills/cosmic-build/scripts/test/test_preview_url.py +44 -1
  96. package/skills/cosmic-build/scripts/test/test_property_batch.py +22 -0
  97. package/skills/cosmic-build/scripts/test/test_property_input_discovery.py +13 -11
  98. package/skills/cosmic-build/scripts/test/test_query_plugin_handoff.py +160 -0
  99. package/skills/cosmic-build/scripts/test/test_register_plugins.py +31 -1
  100. package/skills/cosmic-build/scripts/test/test_render_output_cards.py +50 -4
  101. package/skills/cosmic-build/scripts/test/test_report_business_contract.py +27 -0
  102. package/skills/cosmic-build/scripts/test/test_report_doc_contract_sync.py +5 -4
  103. package/skills/cosmic-build/scripts/test/test_report_sdk_optimization.py +36 -0
  104. package/skills/cosmic-build/scripts/test/test_rule_batch_evidence.py +1 -1
  105. package/skills/cosmic-build/scripts/test/test_rule_domain_contract.py +20 -0
  106. package/skills/cosmic-build/scripts/test/test_rule_function_formula.py +16 -6
  107. package/skills/cosmic-build/scripts/test/test_skill_entry_contract.py +1 -0
  108. package/skills/cosmic-build/scripts/test/test_test_environment.py +45 -0
  109. package/skills/cosmic-build/scripts/test/test_verify_plugin_loading.py +79 -0
  110. package/skills/cosmic-build/scripts/test_environment.py +49 -0
  111. package/skills/cosmic-build/scripts/verify_plugin_loading.py +71 -0
  112. package/skills/cosmic-build/skills/cosmic-coding-impl/SKILL.md +86 -162
  113. package/skills/cosmic-build/skills/cosmic-coding-script/ATTRIBUTION.md +1 -1
  114. package/skills/cosmic-build/skills/cosmic-coding-script/SKILL.md +45 -14
  115. package/skills/cosmic-build/skills/cosmic-coding-script/references/kwc-controller-authoring.md +6 -0
  116. package/skills/cosmic-build/skills/cosmic-coding-script/references/sdk/README.md +29 -19
  117. package/skills/cosmic-build/skills/cosmic-coding-script/references/sdk/docs//350/204/232/346/234/254SDK.md +3 -4
  118. package/skills/cosmic-build/skills/cosmic-coding-script/references/sdk/indexes/package-index.md +1 -1
  119. package/skills/cosmic-build/skills/cosmic-coding-script/references/sdk/strategy.md +27 -18
  120. package/skills/cosmic-build/skills/cosmic-meta-api/SKILL.md +11 -17
  121. package/skills/cosmic-build/skills/cosmic-meta-api/apis/ai_meta.md +1 -1
  122. package/skills/cosmic-build/skills/cosmic-meta-api/apis/app_menu.md +1 -1
  123. package/skills/cosmic-build/skills/cosmic-meta-api/apis/meta_log.md +1 -1
  124. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/control-authoring.json +45 -4
  125. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/form-control-traits.json +1 -1
  126. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/layout-ui.json +18 -0
  127. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/menu-publish.json +8 -2
  128. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/property-batch.json +7 -0
  129. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/property-input-index.json +3 -3
  130. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/report-write-contracts.json +1 -0
  131. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/rule-domain.json +37 -5
  132. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/rule-event-contract.json +24 -3
  133. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/run-build-meta.json +61 -12
  134. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/schema/dropdown-menu.schema.json +69 -0
  135. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/schema/report-write.schema.json +2 -1
  136. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/schema/rule-set.schema.json +1 -0
  137. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/schema/rule-write.schema.json +14 -7
  138. package/skills/cosmic-build/skills/cosmic-meta-api/docs/api-summary.md +2 -2
  139. package/skills/cosmic-build/skills/cosmic-meta-api/modules/app-action/SKILL.md +6 -3
  140. package/skills/cosmic-build/skills/cosmic-meta-api/modules/develop/SKILL.md +1 -2
  141. package/skills/cosmic-build/skills/cosmic-meta-api/modules/export/SKILL.md +5 -6
  142. package/skills/cosmic-build/skills/cosmic-meta-api/modules/init/SKILL.md +13 -36
  143. package/skills/cosmic-build/skills/cosmic-meta-api/modules/pdm/SKILL.md +8 -9
  144. package/skills/cosmic-build/skills/cosmic-meta-api/modules/pdm/scripts/config.json.example +5 -15
  145. package/skills/cosmic-build/skills/cosmic-meta-api/modules/reset/SKILL.md +6 -6
  146. package/skills/cosmic-build/skills/cosmic-meta-api/modules/usage/SKILL.md +20 -148
  147. package/skills/cosmic-build/skills/cosmic-meta-api/modules/verify/SKILL.md +1 -1
  148. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/build_receipts.py +171 -0
  149. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_apps.py +25 -6
  150. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_controls.py +55 -21
  151. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_fields.py +10 -3
  152. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_formactions.py +63 -6
  153. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_forms.py +59 -2
  154. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_listui.py +8 -0
  155. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_metaops.py +36 -1
  156. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_plugins.py +115 -90
  157. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_properties.py +666 -4
  158. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_rules.py +1415 -87
  159. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/commands.py +6 -1
  160. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/config.py +28 -15
  161. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/execution_errors.py +40 -0
  162. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/formatters.py +32 -5
  163. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/property_policy_contract.py +6 -0
  164. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/report_ops.py +113 -17
  165. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/reset.py +3 -3
  166. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/rule_contract.py +107 -26
  167. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/build_meta_order.py +50 -49
  168. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/cosmic_api.py +856 -150
  169. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/field_decorator.py +47 -14
  170. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_build_receipt_resume.py +374 -0
  171. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_cli_failure_contract.py +24 -0
  172. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_creation_identity_boundary.py +4 -2
  173. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_dropdown_menu_contract.py +382 -0
  174. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_existing_form_ui.py +0 -25
  175. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_field_decorator.py +50 -6
  176. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_functional_group_resolution.py +83 -16
  177. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_identity_template_resolution.py +103 -0
  178. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_plugin_contract.py +28 -2
  179. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_readback_derivation.py +22 -0
  180. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_typed_filters.py +65 -0
  181. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_rule_write_closure.py +1041 -15
  182. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_run_batch_post_build_recovery.py +65 -0
  183. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_run_batch_verification_evidence.py +17 -0
  184. package/skills/cosmic-build/skills/cosmic-meta-knowledge/generation/api-param-checklist.md +1 -1
  185. package/skills/cosmic-build/skills/cosmic-meta-knowledge/generation/rule-modeling-guide.md +7 -4
  186. package/skills/cosmic-build/skills/cosmic-meta-modeling/SKILL.md +61 -13
  187. package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/app-menu/SKILL.md +8 -0
  188. package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/formtemplate/SKILL.md +5 -0
  189. package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/formtemplate/dynamic-form-artifact-schema.md +5 -0
  190. package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/report/SKILL.md +21 -9
  191. package/skills/cosmic-build/skills/cosmic-meta-modeling/references/authoring-entity-graphs.md +1 -1
  192. package/skills/cosmic-build/skills/cosmic-meta-modeling/references/authoring-storage-constraints.md +5 -3
  193. package/skills/cosmic-build/skills/cosmic-meta-modeling/references/authoring-ui-composition.md +6 -3
  194. package/skills/cosmic-build/skills/cosmic-meta-modeling/references/creation-recovery.md +13 -0
  195. package/skills/cosmic-build/skills/cosmic-meta-modeling/references/parse-core.md +3 -0
  196. package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/data_model_contract.py +6 -1
  197. package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/data_model_emit.py +135 -87
  198. package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/field_type_registry.py +11 -1
  199. package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/isv_resolver.py +1 -3
  200. package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/report_validator.py +29 -0
  201. package/skills/cosmic-build/skills/cosmic-meta-modifier/SKILL.md +4 -2
  202. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/context/SKILL.md +2 -2
  203. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/execute/SKILL.md +1 -1
  204. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/operation/SKILL.md +5 -4
  205. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/operation/operation-write.md +6 -2
  206. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/operation/validation.md +2 -0
  207. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/property/SKILL.md +20 -5
  208. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/property/complex-apis.md +1 -1
  209. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/property/policy-grammar.md +4 -1
  210. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/report-op/SKILL.md +9 -0
  211. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/SKILL.md +61 -4
  212. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/raw-rule-authoring.md +3 -1
  213. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/rule-actions.md +2 -2
  214. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/rule-reference.md +6 -6
  215. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/state.md +1 -1
  216. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/unit/SKILL.md +6 -3
  217. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/verify/usage-guide.md +4 -4
  218. package/skills/cosmic-build/skills/cosmic-meta-modifier/references/list-filter-modification.md +4 -1
  219. package/skills/cosmic-build/skills/cosmic-meta-ui/SKILL.md +9 -1
  220. package/skills/cosmic-build/skills/cosmic-meta-ui/modules/button/SKILL.md +3 -0
  221. package/skills/cosmic-build/skills/mcp-server/SKILL.md +210 -0
  222. package/skills/cosmic-build/skills/mcp-server/scripts/mcp_cli.py +431 -0
  223. package/skills/cosmic-build/skills/openapi-gen/SKILL.md +3 -3
  224. package/skills/cosmic-build/skills/openapi-gen/scripts/api/gen_runner.py +2 -2
  225. package/skills/cosmic-dev-best-practices/references/bill-dev-convention.md +19 -9
  226. package/skills/cosmic-dev-best-practices/references/cosmic-meta-api.md +14 -11
  227. package/skills/cosmic-dev-best-practices/references/design-examples/basedata-design-example.md +1 -1
  228. package/skills/cosmic-dev-best-practices/references/design-examples/bill-design-example.md +1 -1
  229. package/skills/cosmic-dev-best-practices/references/elements/AssistantField.md +1 -1
  230. package/skills/cosmic-dev-best-practices/references/elements/BasedataField.md +2 -2
  231. package/skills/cosmic-dev-best-practices/references/elements/BasedataPropField.md +3 -3
  232. package/skills/cosmic-dev-best-practices/references/elements/CreaterField.md +2 -2
  233. package/skills/cosmic-dev-best-practices/references/elements/CurrencyField.md +2 -2
  234. package/skills/cosmic-dev-best-practices/references/elements/CustomerField.md +2 -2
  235. package/skills/cosmic-dev-best-practices/references/elements/FlexField.md +1 -1
  236. package/skills/cosmic-dev-best-practices/references/elements/MaterielField.md +2 -2
  237. package/skills/cosmic-dev-best-practices/references/elements/OrgField.md +2 -2
  238. package/skills/cosmic-dev-best-practices/references/elements/SupplierField.md +2 -2
  239. package/skills/cosmic-dev-best-practices/references/elements/UnitField.md +1 -1
  240. package/skills/cosmic-dev-best-practices/references/elements/UserField.md +3 -3
  241. package/skills/cosmic-dev-best-practices/references/elements/common-field-property-conventions.md +16 -11
  242. package/skills/cosmic-dev-best-practices/references/layout-control-convention.md +5 -4
  243. package/skills/erp-analysis/references/delivery-contract.md +4 -2
  244. package/skills/erp-analysis/templates/AGENTS.template.md +1 -1
  245. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/.dsh/cosmic/.kd/config.json +0 -21
@@ -73,7 +73,7 @@ def test_every_screenshot_control_has_one_published_status() -> None:
73
73
  assert not released & deferred
74
74
  assert not specialized & deferred
75
75
  assert released | specialized | deferred == SCREENSHOT_TYPES
76
- assert len(released) == 25
76
+ assert len(released) == 26
77
77
 
78
78
 
79
79
  def test_aliases_compile_to_deterministic_keys_and_closed_properties() -> None:
@@ -104,6 +104,99 @@ def test_aliases_compile_to_deterministic_keys_and_closed_properties() -> None:
104
104
  "parent": "基本信息",
105
105
  "controls": [{"type": "按钮", "name": "普通按钮"}],
106
106
  })["controls"][0]["type"] == "ButtonAp"
107
+ assert cmd_controls.validate_control_batch_input({
108
+ "parent": "uhyl_test09",
109
+ "controls": [{"type": "通用过滤", "name": "通用过滤"}],
110
+ })["controls"][0]["type"] == "FilterGridAp"
111
+ assert cmd_controls.validate_control_batch_input({
112
+ "parent": "uhyl_test09",
113
+ "controls": [{"type": "Common Filter", "name": "Common Filter"}],
114
+ })["controls"][0]["type"] == "FilterGridAp"
115
+
116
+
117
+ @pytest.mark.parametrize("locale", ["", ".locales/zh"])
118
+ def test_command_description_contains_complete_control_input(locale) -> None:
119
+ cli = ROOT / locale / "skills/cosmic-meta-api/scripts/cosmic_api.py"
120
+ completed = subprocess.run(
121
+ [sys.executable, str(cli), "--stdout", "describeCommand", "addControls"],
122
+ capture_output=True, text=True, check=True,
123
+ )
124
+ schema = json.loads(completed.stdout)["action"]["payloadSchema"]
125
+
126
+ assert schema["required"] == ["controls"]
127
+ assert schema["$defs"]["control"]["required"] == ["type", "name"]
128
+ assert schema["$defs"]["panel"]["required"] == ["name"]
129
+ assert "live form root" in schema["properties"]["parent"]["description"]
130
+ assert set(schema["x-controlTypes"]) == set(cmd_controls.control_catalog()["data"]["releasedTypes"])
131
+ assert "通用过滤" in schema["x-controlTypes"]["FilterGridAp"]["aliases"]
132
+ assert schema["x-commonProperties"]["width"]["type"] == "string"
133
+ assert schema["x-controlTypes"]["LabelAp"]["properties"]["autoTextWrap"]["type"] == "boolean"
134
+
135
+
136
+ @pytest.mark.parametrize("intent, missing_fields", [
137
+ ({"panel": {}, "controls": [{}, {"type": "LabelAp"}]},
138
+ ["controls[0].type", "controls[0].name", "controls[1].name", "panel.name"]),
139
+ ({"panel": {}}, ["controls", "panel.name"]),
140
+ ])
141
+ def test_cli_reports_all_missing_fields_before_authentication(intent, missing_fields) -> None:
142
+ completed = subprocess.run(
143
+ [sys.executable, str(CLI), "--stdout", "addControls", "--formNumber", "demo",
144
+ "--input", json.dumps(intent)],
145
+ capture_output=True, text=True, check=False,
146
+ )
147
+ result = json.loads(completed.stdout)
148
+
149
+ assert completed.returncode != 0
150
+ assert result["errorCode"] == "CONTROL_INPUT_INVALID"
151
+ assert result["recovery"]["missingFields"] == missing_fields
152
+ assert result["recovery"]["describeCommand"] == "describeCommand addControls"
153
+ assert "[auth]" not in completed.stderr
154
+
155
+
156
+ @pytest.mark.parametrize("control_type", sorted(cmd_controls.control_catalog()["data"]["releasedTypes"]))
157
+ def test_default_root_creation_and_exact_key_retry(control_type) -> None:
158
+ before = _schema()
159
+ before["data"]["formLayout"]["children"].append({
160
+ "key": "manual_control", "name": "Control", "type": control_type,
161
+ })
162
+ after = json.loads(json.dumps(before))
163
+ after["data"]["formLayout"]["children"].append({
164
+ "key": "new_control", "name": "Control", "type": control_type,
165
+ })
166
+ intent = {"controls": [{"key": "new_control", "type": control_type, "name": "Control"}]}
167
+ with (
168
+ mock.patch.object(cmd_controls, "do_get_form_schema", side_effect=[before, after, after, after]) as reader,
169
+ mock.patch.object(cmd_controls, "do_modify_meta", return_value={"status": True}) as writer,
170
+ ):
171
+ created = cmd_controls.do_add_controls({}, "token", "FORM-ID", "demo", intent)
172
+ retried = cmd_controls.do_add_controls({}, "token", "FORM-ID", "demo", intent)
173
+
174
+ writer.assert_called_once()
175
+ assert writer.call_args.args[3][0]["target"]["parentScope"] == "demo"
176
+ assert reader.call_count == 4
177
+ assert created["data"]["createdCount"] == 1
178
+ assert created["data"]["changed"] is True
179
+ assert retried["data"]["alreadyPresentCount"] == 1
180
+ assert retried["data"]["changed"] is False
181
+ assert created["verification"]["verified"] is True
182
+ assert retried["verification"]["verified"] is True
183
+
184
+
185
+ @pytest.mark.parametrize("parent", [None, "unknown_container"])
186
+ def test_unresolved_parent_never_falls_back_to_another_container(parent) -> None:
187
+ before = _schema()
188
+ if parent is None:
189
+ before["data"]["formLayout"].pop("key")
190
+ with (
191
+ mock.patch.object(cmd_controls, "do_get_form_schema", return_value=before),
192
+ mock.patch.object(cmd_controls, "do_modify_meta") as writer,
193
+ ):
194
+ with pytest.raises(ValueError, match="CONTROL_PARENT_UNRESOLVED"):
195
+ cmd_controls.do_add_controls({}, "token", "FORM-ID", "demo", {
196
+ **({"parent": parent} if parent is not None else {}),
197
+ "controls": [{"type": "LabelAp", "name": "Control"}],
198
+ })
199
+ writer.assert_not_called()
107
200
 
108
201
 
109
202
  @pytest.mark.parametrize(
@@ -247,10 +340,35 @@ def test_existing_key_conflict_blocks_the_complete_batch() -> None:
247
340
  writer.assert_not_called()
248
341
 
249
342
 
250
- def test_titled_panel_and_palette_button_compile_as_one_verified_batch() -> None:
343
+ def test_common_filter_tile_uses_form_control_writer_not_list_filters() -> None:
344
+ before = {"status": True, "data": {"formLayout": {
345
+ "key": "uhyl_test09", "type": "FormAp", "name": "test09", "children": [],
346
+ }}}
347
+ after = {"status": True, "data": {"formLayout": {
348
+ "key": "uhyl_test09", "type": "FormAp", "name": "test09", "children": [
349
+ {"key": "ctl_filter", "type": "FilterGridAp", "name": "通用过滤"},
350
+ ],
351
+ }}}
352
+ with (
353
+ mock.patch.object(cmd_controls, "do_get_form_schema", side_effect=[before, after]),
354
+ mock.patch.object(cmd_controls, "do_modify_meta", return_value={"status": True, "errorCode": "0"}) as writer,
355
+ ):
356
+ result = cmd_controls.do_add_controls({}, "token", "FORM-ID", "uhyl_test09", {
357
+ "parent": "uhyl_test09",
358
+ "controls": [{"key": "ctl_filter", "type": "通用过滤", "name": "通用过滤"}],
359
+ })
360
+
361
+ assert result["verification"] == {"verified": True, "controlCount": 1}
362
+ operation = writer.call_args.args[3][0]
363
+ assert operation["target"]["parentScope"] == "uhyl_test09"
364
+ assert operation["value"]["controlType"] == "FilterGridAp"
365
+
366
+
367
+ @pytest.mark.parametrize("parent", [None, "Content"])
368
+ def test_titled_panel_and_palette_button_compile_as_one_verified_batch(parent) -> None:
251
369
  before = _schema()
252
370
  after = _schema()
253
- content = after["data"]["formLayout"]["children"][0]
371
+ content = after["data"]["formLayout"] if parent is None else after["data"]["formLayout"]["children"][0]
254
372
  content["children"].append({
255
373
  "key": "panel_general", "name": "识别控件", "type": "FlexPanelAp",
256
374
  "children": [
@@ -316,7 +434,7 @@ def test_titled_panel_and_palette_button_compile_as_one_verified_batch() -> None
316
434
  {}, "token", "FORM-ID", "demo", {
317
435
  "panel": {
318
436
  "key": "panel_general", "name": "识别控件",
319
- "parent": "Content",
437
+ **({"parent": parent} if parent is not None else {}),
320
438
  },
321
439
  "controls": [
322
440
  {"key": "ctl_button", "type": "按钮", "name": "按钮"},
@@ -328,6 +446,7 @@ def test_titled_panel_and_palette_button_compile_as_one_verified_batch() -> None
328
446
  assert result["status"] is True
329
447
  assert result["verification"] == {"verified": True, "controlCount": 2}
330
448
  assert result["data"]["panel"]["verified"] is True
449
+ assert result["data"]["panel"]["parent"] == ("demo" if parent is None else "contentpanel")
331
450
  assert result["data"]["panel"]["presentation"] == "auto"
332
451
  assert result["data"]["runtimeVerification"] == {
333
452
  "performed": False,
@@ -0,0 +1,141 @@
1
+ """Compile the Cosmic SDK examples and run the time adapter's value assertions."""
2
+
3
+ import os
4
+ import shutil
5
+ import shlex
6
+ import subprocess
7
+ from pathlib import Path
8
+
9
+ import pytest
10
+
11
+
12
+ EXAMPLE = Path(__file__).resolve().parents[2] / "examples" / "java-rules"
13
+ SDK_JAR = os.environ.get("COSMIC_SDK_DATAENTITY_JAR", "")
14
+ QUERY_CLASSPATH = os.environ.get("COSMIC_SDK_QUERY_CLASSPATH", "")
15
+ OPERATION_CLASSPATH = os.environ.get("COSMIC_SDK_OPERATION_CLASSPATH", "")
16
+
17
+
18
+ @pytest.mark.skipif(not shutil.which("javac") or not shutil.which("java") or not Path(SDK_JAR).is_file(),
19
+ reason="target SDK JAR or JDK unavailable")
20
+ def test_cosmic_time_adapter_compiles_against_target_sdk_and_rejects_bad_values(tmp_path):
21
+ subprocess.run([
22
+ "javac", "-source", "8", "-target", "8", "-cp", SDK_JAR, "-d", str(tmp_path),
23
+ str(EXAMPLE / "CosmicTimeAdapter.java"),
24
+ str(EXAMPLE / "CosmicTimeAdapterCheck.java"),
25
+ ], check=True, capture_output=True, text=True)
26
+ result = subprocess.run(
27
+ ["java", "-cp", os.pathsep.join([str(tmp_path), SDK_JAR]), "CosmicTimeAdapterCheck"],
28
+ check=True, capture_output=True, text=True,
29
+ )
30
+ assert result.stdout.strip() == "CosmicTimeAdapter: 6 cases passed"
31
+
32
+
33
+ @pytest.mark.skipif(not shutil.which("javac") or not QUERY_CLASSPATH,
34
+ reason="target query SDK classpath unavailable")
35
+ def test_cosmic_entry_query_compiles_against_the_target_sdk(tmp_path):
36
+ subprocess.run([
37
+ "javac", "-source", "8", "-target", "8", "-cp", QUERY_CLASSPATH, "-d", str(tmp_path),
38
+ str(EXAMPLE / "CosmicEntryQuery.java"),
39
+ ], check=True, capture_output=True, text=True)
40
+ assert (tmp_path / "CosmicEntryQuery.class").is_file()
41
+
42
+
43
+ def _compile_report_filter_check(tmp_path, example=EXAMPLE):
44
+ check = tmp_path / "ReportFilterCheck.java"
45
+ check.write_text('''
46
+ import kd.bos.entity.report.FilterInfo;
47
+ import kd.bos.dataentity.entity.DynamicObject;
48
+ import kd.bos.dataentity.entity.DynamicObjectCollection;
49
+ import kd.bos.dataentity.metadata.dynamicobject.DynamicObjectType;
50
+ import java.util.Arrays;
51
+ import java.util.Date;
52
+ public class ReportFilterCheck {
53
+ static void reject(Runnable call) {
54
+ try { call.run(); } catch (IllegalArgumentException expected) { return; }
55
+ throw new AssertionError("invalid value accepted");
56
+ }
57
+ public static void main(String[] args) {
58
+ FilterInfo filter = new FilterInfo();
59
+ filter.addFilterItem("shfz_mode", "0");
60
+ if (!"0".equals(CosmicReportFilters.value(filter, "flt_mode", "shfz_mode"))) throw new AssertionError("zero enum");
61
+ if (CosmicReportFilters.value(filter, "unknown") != null) throw new AssertionError("missing key");
62
+ filter.addFilterItem("flt_mode", "2");
63
+ reject(() -> CosmicReportFilters.value(filter, "flt_mode", "shfz_mode"));
64
+ Date start = new Date(1000L);
65
+ filter.addFilterItem("flt_date_startdate", start, ">=");
66
+ if (CosmicReportFilters.value(filter, "flt_date") != null) throw new AssertionError("unsplit key");
67
+ if (!start.equals(CosmicReportFilters.date(filter, "flt_date_startdate"))) throw new AssertionError("split Date");
68
+ if (!start.equals(filter.getValue("flt_date_startdate"))) throw new AssertionError("getValue delegates to item");
69
+ if (CosmicReportFilters.date(filter, "flt_date_enddate") != null) throw new AssertionError("open end");
70
+ filter.addFilterItem("bad_date", "2026-10-01");
71
+ reject(() -> CosmicReportFilters.date(filter, "bad_date"));
72
+ reject(() -> CosmicReportFilters.numbers(10L));
73
+ if (!CosmicReportFilters.numbers(null).isEmpty()) throw new AssertionError("empty F7");
74
+ if (args.length == 0) { System.out.println("report filter SDK checks passed"); return; }
75
+ DynamicObjectType type = new DynamicObjectType("supplier");
76
+ type.registerProperty("number", String.class, null, false);
77
+ type.registerProperty("id", Long.class, null, false);
78
+ DynamicObject first = new DynamicObject(type);
79
+ first.set("number", "0012"); first.set("id", 10L);
80
+ DynamicObject second = new DynamicObject(type);
81
+ second.set("number", "0012"); second.set("id", 20L);
82
+ if (!CosmicReportFilters.numbers(first).equals(Arrays.asList("0012"))) throw new AssertionError("single F7");
83
+ DynamicObjectCollection values = new DynamicObjectCollection(type, null);
84
+ values.add(first); values.add(second);
85
+ if (!CosmicReportFilters.numbers(values).equals(Arrays.asList("0012"))) throw new AssertionError("assigned ids differ");
86
+ second.set("number", "0034");
87
+ if (!CosmicReportFilters.numbers(values).equals(Arrays.asList("0012", "0034"))) throw new AssertionError("multi F7");
88
+ second.set("number", ""); reject(() -> CosmicReportFilters.numbers(values));
89
+ System.out.println("report filter SDK checks passed");
90
+ }
91
+ }
92
+ ''')
93
+ subprocess.run(["javac", "-source", "8", "-target", "8", "-cp", QUERY_CLASSPATH, "-d", str(tmp_path),
94
+ str(example / "CosmicReportFilters.java"), str(check)], check=True, capture_output=True, text=True)
95
+
96
+
97
+ @pytest.mark.skipif(not shutil.which("javac") or not QUERY_CLASSPATH,
98
+ reason="target query SDK classpath unavailable")
99
+ @pytest.mark.parametrize("example", [EXAMPLE, EXAMPLE.parents[1] / ".locales" / "zh" / "examples" / "java-rules"])
100
+ def test_report_filter_adapter_against_real_sdk_objects(tmp_path, example):
101
+ _compile_report_filter_check(tmp_path, example)
102
+ result = subprocess.run(["java", "-cp", os.pathsep.join([str(tmp_path), QUERY_CLASSPATH]), "ReportFilterCheck"],
103
+ cwd=tmp_path, timeout=20, check=True, capture_output=True, text=True)
104
+ assert result.stdout.strip() == "report filter SDK checks passed"
105
+
106
+
107
+ @pytest.mark.skipif(not shutil.which("javac") or not QUERY_CLASSPATH
108
+ or not os.environ.get("COSMIC_SDK_RUNTIME_JAVA_OPTIONS"),
109
+ reason="F7 metadata runtime needs the authorized target configuration/logging service")
110
+ def test_report_f7_records_with_target_runtime_configuration(tmp_path):
111
+ _compile_report_filter_check(tmp_path)
112
+ result = subprocess.run(["java", *shlex.split(os.environ["COSMIC_SDK_RUNTIME_JAVA_OPTIONS"]), "-cp",
113
+ os.pathsep.join([str(tmp_path), QUERY_CLASSPATH]), "ReportFilterCheck", "f7"],
114
+ cwd=tmp_path, timeout=20, check=True, capture_output=True, text=True)
115
+ assert "report filter SDK checks passed" in result.stdout
116
+
117
+
118
+ @pytest.mark.skipif(not shutil.which("javac") or not OPERATION_CLASSPATH,
119
+ reason="target operation SDK classpath unavailable")
120
+ def test_operation_receivers_expose_their_own_keys_in_the_target_sdk(tmp_path):
121
+ source = tmp_path / "OperationAccessorCheck.java"
122
+ source.write_text('''
123
+ import kd.bos.entity.plugin.args.BeginOperationTransactionArgs;
124
+ import kd.bos.entity.validate.AbstractValidator;
125
+ public class OperationAccessorCheck {
126
+ public static void main(String[] args) {
127
+ BeginOperationTransactionArgs event = new BeginOperationTransactionArgs("test.confirm", null);
128
+ if (!"test.confirm".equals(event.getOperationKey())) throw new AssertionError("event key");
129
+ AbstractValidator validator = new AbstractValidator() { public void validate() {} };
130
+ validator.setOperateKey("test.validate");
131
+ if (!"test.validate".equals(validator.getOperateKey())) throw new AssertionError("validator key");
132
+ if ("test.confirm".equals(validator.getOperateKey())) throw new AssertionError("receiver keys mixed");
133
+ System.out.println("operation accessor contracts passed");
134
+ }
135
+ }
136
+ ''')
137
+ subprocess.run(["javac", "-source", "8", "-target", "8", "-cp", OPERATION_CLASSPATH,
138
+ "-d", str(tmp_path), str(source)], check=True, capture_output=True, text=True)
139
+ result = subprocess.run(["java", "-cp", os.pathsep.join([str(tmp_path), OPERATION_CLASSPATH]),
140
+ "OperationAccessorCheck"], cwd=tmp_path, timeout=15, check=True, capture_output=True, text=True)
141
+ assert result.stdout.strip() == "operation accessor contracts passed"
@@ -180,6 +180,17 @@ def test_section_singleton_row_requires_explicit_full_line() -> None:
180
180
  ]
181
181
 
182
182
 
183
+ @pytest.mark.parametrize("row", [
184
+ {"fields": ["Description"], "fullLine": True},
185
+ {"field": "Description", "fullLine": True},
186
+ ])
187
+ def test_ui_composition_rejects_row_level_full_line_with_nested_shape(row: dict) -> None:
188
+ with pytest.raises(ValueError, match=r'fullLine inside a fields\[\] item'):
189
+ _normalize_ui_composition_contract({
190
+ "sections": [{"caption": "Details", "rows": [row]}],
191
+ })
192
+
193
+
183
194
  def test_ui_composition_flattens_one_anonymous_tab_section() -> None:
184
195
  authored = {
185
196
  "tabs": [{
@@ -350,6 +361,17 @@ def test_dynamic_template_conflict_fails_without_rewriting_to_blank(
350
361
  assert any("DYN-CROSS-TPL" in error for error in logs.errors)
351
362
 
352
363
 
364
+ def test_authoring_rejects_base_owned_bill_reference_before_emit():
365
+ data = _valid_authoring()
366
+ root = data["artifact"]["entities"][0]
367
+ root["type"] = "BaseEntity"
368
+ root["templateKey"] = "bos_basetpl"
369
+ root["fields"].append({"bizWord": "bill", "displayName": "单据", "dataType": "RefBillField",
370
+ "reference": {"mode": "platform", "formNumber": "sample_bill"}})
371
+ issues = validate_authoring_contract(data, known_field_types={"ComboField", "TextField", "RefBillField"})
372
+ assert any(issue.code == "REF_BILL_OWNER_UNSUPPORTED" for issue in issues)
373
+
374
+
353
375
  def _valid_authoring() -> dict:
354
376
  return {
355
377
  "artifact": {
@@ -1685,14 +1707,17 @@ def test_direct_basedata_delegated_authoring_contract() -> None:
1685
1707
  ) == []
1686
1708
 
1687
1709
 
1688
- def test_authoring_reports_stable_path_and_code() -> None:
1710
+ def test_authoring_allows_omitted_default_template_but_rejects_empty_explicit_value() -> None:
1689
1711
  data = _valid_authoring()
1690
1712
  del data["artifact"]["entities"][0]["templateKey"]
1691
- issues = validate_authoring_contract(data)
1713
+ assert not any(
1714
+ issue.path == "$.artifact.entities[0].templateKey"
1715
+ for issue in validate_authoring_contract(data)
1716
+ )
1717
+ data["artifact"]["entities"][0]["templateKey"] = ""
1692
1718
  assert any(
1693
- issue.code == "DM_SCHEMA_REQUIRED"
1694
- and issue.path == "$.artifact.entities[0].templateKey"
1695
- for issue in issues
1719
+ issue.path == "$.artifact.entities[0].templateKey"
1720
+ for issue in validate_authoring_contract(data)
1696
1721
  )
1697
1722
 
1698
1723
 
@@ -33,7 +33,7 @@ def test_execution_skills_preserve_the_terminal_menu_boundary() -> None:
33
33
  assert "Do not configure a menu or render artifact cards here" in java
34
34
 
35
35
 
36
- def test_plugin_delivery_uses_the_package_root_and_clean_schema_files() -> None:
36
+ def test_plugin_delivery_uses_the_package_root() -> None:
37
37
  for relative in (
38
38
  "references/java-plugin-delivery.md",
39
39
  ".locales/zh/references/java-plugin-delivery.md",
@@ -44,14 +44,6 @@ def test_plugin_delivery_uses_the_package_root_and_clean_schema_files() -> None:
44
44
  assert "APP_BUILD_ROOT" in text
45
45
  assert "{ROOT_DIR}" not in text
46
46
 
47
- for relative in (
48
- "references/java-plugin-delivery.md",
49
- ".locales/zh/references/java-plugin-delivery.md",
50
- ):
51
- text = _read(relative)
52
- assert '--output "{entity.schema.json}" getFormSchema' in text
53
- assert '--output "{operations.json}" listOperations' in text
54
- assert "--stdout getFormSchema" not in text
55
47
 
56
48
 
57
49
  def test_chinese_delivery_reference_is_mirrored() -> None:
@@ -63,7 +55,7 @@ def test_chinese_delivery_reference_is_mirrored() -> None:
63
55
 
64
56
  assert "references/delivery.md" in root
65
57
  assert "只有所选元数据入口无法表达" in root
66
- assert "每个本轮目标分别传一次" in delivery
58
+ assert "每个本轮可交付目标分别传一次" in delivery
67
59
  assert "最后一次写入后执行一次" in delivery
68
60
  assert "只出现在工具结果中不代表已经" in delivery
69
61
  assert "原样放进最终答复" in delivery
@@ -43,3 +43,14 @@ def test_worker_refuses_changed_app_identity(tmp_path):
43
43
  with mock.patch("render_output_cards.sync_existing_dym_at_session_close") as sync:
44
44
  assert run_request(pending["requestPath"])["status"] == "failed"
45
45
  sync.assert_not_called()
46
+
47
+
48
+ def test_worker_passes_all_current_forms_to_sync(tmp_path):
49
+ (tmp_path / "archive.json").write_text(json.dumps({"appId": "APP"}))
50
+ pending = enqueue_dym_sync(str(tmp_path), ["created", "changed", "created"], {"appId": "APP"})
51
+ with mock.patch("render_output_cards.sync_existing_dym_at_session_close",
52
+ return_value={"status": "synced", "formNumbers": ["created", "changed"]}) as sync:
53
+ result = run_request(pending["requestPath"])
54
+ assert result["status"] == "succeeded"
55
+ assert result["formNumbers"] == ["created", "changed"]
56
+ assert sync.call_args.args[1] == ["created", "changed"]
@@ -3,6 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import copy
6
+ import json
6
7
  import runpy
7
8
  import sys
8
9
  from pathlib import Path
@@ -20,6 +21,63 @@ from field_decorator import decorate_artifact # noqa: E402
20
21
  from dynamic_form_validator import validate_dynamic_form # noqa: E402
21
22
 
22
23
 
24
+ def test_dynamic_reference_targets_use_the_entity_resolver_and_preserve_field_types():
25
+ from cosmic_api import _resolve_dynamic_reference_fields
26
+ queries = []
27
+ def query(selector):
28
+ queries.append(selector)
29
+ return [{"formNumber": "uhyl_drug", "formName": "药品档案", "modelType": "BaseFormModel"}]
30
+ artifact = _artifact()
31
+ artifact["entities"][0]["fields"] = [
32
+ {"bizWord": "imm", "displayName": "疫苗/药品", "dataType": "BasedataField",
33
+ "reference": {"mode": "platform", "formNumber": "uhyl_drug"}},
34
+ {"bizWord": "drugs", "dataType": "MulBasedataField", "prdRef": "uhyl_drug"},
35
+ ]
36
+ assert _resolve_dynamic_reference_fields(artifact, isv="uhyl", query_forms=query, entity_exists=lambda _: False) is None
37
+ fields = artifact["entities"][0]["fields"]
38
+ assert [field["basedataNumber"] for field in fields] == ["uhyl_drug", "uhyl_drug"]
39
+ assert [field["dataType"] for field in fields] == ["BasedataField", "MulBasedataField"]
40
+ assert queries == ["uhyl_drug"]
41
+
42
+
43
+ def test_unresolved_dynamic_reference_stops_before_creation_without_removing_the_field():
44
+ from cosmic_api import _resolve_dynamic_reference_fields
45
+ artifact = _artifact()
46
+ artifact["entities"][0]["fields"] = [{"bizWord": "imm", "dataType": "BasedataField", "prdRef": "不存在的药品"}]
47
+ failure = _resolve_dynamic_reference_fields(artifact, query_forms=lambda _: [], entity_exists=lambda _: False)
48
+ assert failure["writeAttempted"] is False
49
+ assert failure["errorCode"] == "DYNAMIC_REFERENCE_TARGET_UNRESOLVED"
50
+ assert failure["data"]["unresolvedReferences"][0]["selector"] == "不存在的药品"
51
+ assert artifact["entities"][0]["fields"][0]["dataType"] == "BasedataField"
52
+
53
+
54
+ def test_retained_dynamic_creation_rejects_a_changed_artifact_before_auth_or_another_build(tmp_path, monkeypatch):
55
+ from unittest import mock
56
+ import cosmic_api
57
+ source = tmp_path / "page.json"
58
+ source.write_text(json.dumps(_artifact()), encoding="utf-8")
59
+ retained = {"status": False, "metadataRetained": True, "writeAttempted": True,
60
+ "data": {"entityResults": [{"formId": "retained-id", "formNumber": "uhyl_task_register"}]}}
61
+ cosmic_api._dynamic_creation_receipt(str(tmp_path), str(source), retained)
62
+ assert cosmic_api._dynamic_creation_receipt(str(tmp_path), str(tmp_path / "another-page.json")) is None
63
+ assert cosmic_api._dynamic_creation_receipt(str(tmp_path), str(source))["data"]["entityResults"][0]["formId"] == "retained-id"
64
+ source.write_text(json.dumps({**_artifact(), "propertySettings": []}), encoding="utf-8")
65
+ monkeypatch.setattr(sys, "argv", ["cosmic_api.py", "--stdout", "run", "--app-root", str(tmp_path), "--artifact", str(source)])
66
+ with mock.patch.object(cosmic_api, "load_config", return_value={}), \
67
+ mock.patch.object(cosmic_api, "_retarget_runtime_dir"), \
68
+ mock.patch.object(cosmic_api, "pjson") as emit, \
69
+ mock.patch.object(cosmic_api, "do_auth") as auth, \
70
+ mock.patch.object(cosmic_api, "do_build_meta") as build:
71
+ assert cosmic_api._main_impl() == 3
72
+ failure = emit.call_args.args[0]
73
+ assert failure["errorCode"] == "DYNAMIC_FORM_RECOVERY_REQUIRED"
74
+ assert failure["writeAttempted"] is False
75
+ assert failure["metadataRetained"] is True
76
+ assert failure["reconciliation"][0]["arguments"][-2:] == ["--formId", "retained-id"]
77
+ auth.assert_not_called()
78
+ build.assert_not_called()
79
+
80
+
23
81
  def _artifact() -> dict:
24
82
  return {
25
83
  "status": "REVIEW_PENDING",
@@ -150,7 +208,7 @@ def test_dynamic_explicit_form_number_over_limit_requires_user_input() -> None:
150
208
 
151
209
  def test_dynamic_explicit_form_number_at_limit_is_preserved() -> None:
152
210
  artifact = _artifact()
153
- exact = "x" * 36
211
+ exact = "uhyl_" + "x" * 31
154
212
  artifact["entities"][0]["logicalName"] = exact
155
213
 
156
214
  for locale_root in (APP_BUILD, APP_BUILD / ".locales/zh"):