@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
@@ -0,0 +1,382 @@
1
+ import copy
2
+ import json
3
+ import subprocess
4
+ import sys
5
+ from pathlib import Path
6
+ from unittest import mock
7
+
8
+ import pytest
9
+
10
+ sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
11
+ from _core import cmd_listui, cmd_operations, cmd_properties
12
+
13
+
14
+ @pytest.mark.parametrize("locale", ["", ".locales/zh"])
15
+ def test_menu_contract_is_discoverable_without_local_application_files(tmp_path, locale):
16
+ root = Path(__file__).resolve().parents[4]
17
+ cli = root / locale / "skills/cosmic-meta-api/scripts/cosmic_api.py"
18
+ result = subprocess.run(
19
+ [sys.executable, str(cli), "--stdout", "describeCommand", "configureDropdownMenu"],
20
+ cwd=tmp_path, capture_output=True, text=True, check=True,
21
+ )
22
+ action = json.loads(result.stdout)["action"]
23
+ assert action["command"] == "configureDropdownMenu"
24
+ assert {"append", "update", "order"} <= set(action["payloadSchema"]["properties"])
25
+ flags = {flag for argument in action["arguments"] for flag in argument["flags"]}
26
+ assert {"--form-name", "--element", "--input", "--app-root"} <= flags
27
+
28
+
29
+ # JavaBean projection used by getProperties, including LocaleString and derived values.
30
+ CURRENT = [
31
+ {"id": "id-a", "key": "a", "title": {"zh_CN": "设置"},
32
+ "operationKey": "op_a", "visible": "init,new", "index": 1,
33
+ "visibleValue": 9, "isv": "uhyl"},
34
+ {"id": "id-b", "key": "b", "title": {"zh_CN": "预览"},
35
+ "operationKey": "op_b", "index": 2},
36
+ ]
37
+
38
+
39
+ @pytest.fixture
40
+ def platform(monkeypatch):
41
+ state = {"items": copy.deepcopy(CURRENT), "writes": [], "operations": 0}
42
+ def read(*args, **kwargs):
43
+ return {"status": True, "data": {"dropdownItems": json.dumps(state["items"])}}
44
+ def load(*args, **kwargs):
45
+ if state.get("saved_document") is not None:
46
+ return {"status": True, "data": copy.deepcopy(state["saved_document"])}
47
+ native, locale_rows = [], []
48
+ for row in state["items"]:
49
+ item = cmd_properties._dropdown_authored_item(row)
50
+ if "Isv" in item:
51
+ item["IsvFlag"] = item.pop("Isv")
52
+ for field in ("Title", "SubTitle"):
53
+ value = item.get(field)
54
+ if not isinstance(value, dict):
55
+ continue
56
+ resource = f"app.form.button-id.DropdownItems.{item['Id']}.{field}"
57
+ locale_rows.append({resource: {"resId": resource, **value}})
58
+ item[field] = value.get("zh_CN") or next(iter(value.values()), "")
59
+ native.append(item)
60
+ return {"status": True, "data": {
61
+ "appNum": "app",
62
+ "formmeta": {"Key": "form", "Id": "form", "Language": "zh_CN", "IsvFlag": "uhyl",
63
+ "Items": [{"Key": "print", "Id": "button-id", "DropdownItems": native}]},
64
+ "entitymeta": {"Key": "form", "IsvFlag": "uhyl", "Rules": [{"Id": "keep-rule"}]},
65
+ "localeItems": {"form": locale_rows},
66
+ }}
67
+ def save(cfg, token, form, payload, **kwargs):
68
+ state["saved_document"] = copy.deepcopy(payload)
69
+ items = payload["formmeta"]["Items"][0]["DropdownItems"]
70
+ assert payload["entitymeta"]["Rules"] == [{"Id": "keep-rule"}]
71
+ state["writes"].append(copy.deepcopy(items))
72
+ locales = {key: value for row in payload["localeItems"]["form"]
73
+ for key, value in row.items()}
74
+ state["items"] = []
75
+ for item in items:
76
+ projected = {
77
+ key[:1].lower() + key[1:]: value
78
+ for key, value in cmd_properties._dropdown_authored_item(item).items()
79
+ }
80
+ for field in ("Title", "SubTitle"):
81
+ resource = next((value for key, value in locales.items()
82
+ if key.endswith(f".{item['Id']}.{field}")), None)
83
+ if resource:
84
+ projected[field[:1].lower() + field[1:]] = {
85
+ key: value for key, value in resource.items() if key != "resId"}
86
+ state["items"].append(projected)
87
+ return {"status": True, "data": {}}
88
+ def operations(*args, **kwargs):
89
+ state["operations"] += 1
90
+ return {"status": True, "data": [
91
+ {"operationKey": key, "operationType": "printpreview"}
92
+ for key in ("op_a", "op_b", "op_c")]}
93
+ monkeypatch.setattr(cmd_properties, "do_get_properties", read)
94
+ monkeypatch.setattr(cmd_listui, "do_load_full_metadata", load)
95
+ monkeypatch.setattr(cmd_listui, "do_save_full_metadata", save)
96
+ monkeypatch.setattr(cmd_properties, "do_get_property_schema", lambda *args: {
97
+ "status": True, "data": [{"propertyName": "DropdownItems", "editable": True}]})
98
+ monkeypatch.setattr(cmd_operations, "do_list_operations", operations)
99
+ return state
100
+
101
+
102
+ def edit(intent):
103
+ return cmd_properties.do_configure_dropdown_menu({}, "", "form", "print", intent)
104
+
105
+
106
+ def test_real_projection_combined_edit_is_one_save_and_preserves_ids(platform):
107
+ result = edit({
108
+ "update": [{"key": "设置", "subtitle": "打印配置"}],
109
+ "append": [{"key": "c", "title": "PDF", "operation": "op_c"}],
110
+ "moves": [{"key": "c", "before": "预览"}],
111
+ })
112
+ assert result["status"] is True
113
+ assert result["verification"]["verified"] is True
114
+ assert len(platform["writes"]) == 1
115
+ items = platform["writes"][0]
116
+ assert [item["Key"] for item in items] == ["a", "c", "b"]
117
+ assert items[0]["Id"] == "id-a"
118
+ assert items[2]["Id"] == "id-b"
119
+ assert items[0]["SubTitle"] == "打印配置"
120
+ assert items[2]["Title"] == "预览"
121
+ assert platform["items"][0]["isv"] == "uhyl"
122
+ assert platform["operations"] == 1
123
+
124
+
125
+ def test_target_resolution_accepts_unique_operation_and_exact_key_wins(platform):
126
+ result = edit({"update": [{"key": "op_a", "title": "打印设置"}]})
127
+ assert result["status"] is True
128
+ assert platform["writes"][0][0]["Key"] == "a"
129
+ assert platform["operations"] == 0
130
+
131
+
132
+ def test_expected_snapshot_includes_exact_locale_side_effects(platform):
133
+ platform["items"][0]["title"]["en_US"] = "Settings"
134
+ result = cmd_properties.do_configure_dropdown_menu({}, "", "form", "print", {
135
+ "update": [{"key": "a", "title": "打印参数设置", "subtitle": "选择参数和模板"}],
136
+ }, locale="zh_CN")
137
+ assert result["status"] is True
138
+ assert result["verification"]["verified"] is True
139
+ assert len(result["verification"]["changedLocalePaths"]) == 2
140
+ resources = platform["saved_document"]["localeItems"]["form"]
141
+ localized = {key: value for row in resources for key, value in row.items()}
142
+ title = next(value for key, value in localized.items() if key.endswith(".Title"))
143
+ subtitle = next(value for key, value in localized.items() if key.endswith(".SubTitle"))
144
+ assert title["zh_CN"] == "打印参数设置"
145
+ assert title["en_US"] == "Settings"
146
+ assert subtitle["zh_CN"] == "选择参数和模板"
147
+
148
+
149
+ def test_new_item_gets_owned_locale_resources(platform):
150
+ result = edit({"append": [{"key": "new", "title": "新菜单", "subtitle": "说明"}]})
151
+ assert result["status"] is True
152
+ resources = platform["saved_document"]["localeItems"]["form"]
153
+ created = {key: value for row in resources for key, value in row.items()
154
+ if ".DropdownItems." in key and value.get("zh_CN") in ("新菜单", "说明")}
155
+ assert len(created) == 2
156
+ assert all(key == value["resId"] for key, value in created.items())
157
+
158
+
159
+ def test_remove_keeps_unmentioned_item_and_avoids_operation_scan(platform):
160
+ result = edit({"remove": ["预览"]})
161
+ assert result["status"] is True
162
+ assert [item["Id"] for item in platform["writes"][0]] == ["id-a"]
163
+ assert platform["writes"][0][0]["IsvFlag"] == "uhyl"
164
+ assert platform["operations"] == 0
165
+
166
+
167
+ def test_noop_does_not_save_or_query_operations(platform):
168
+ result = edit({"update": [{"key": "a", "title": "设置"}]})
169
+ assert result["status"] is True
170
+ assert result["data"]["changed"] is False
171
+ assert not platform["writes"]
172
+ assert not platform["operations"]
173
+
174
+
175
+ @pytest.mark.parametrize("intent", [
176
+ {"clear": True}, {"replace": []}, {"clear": True, "append": []},
177
+ {"append": [{"key": "a", "title": "duplicate"}]},
178
+ {"moves": [{"key": "a", "before": "b", "after": "b"}]},
179
+ {"update": [{"key": "missing", "title": "X"}]},
180
+ {"order": ["a", "a"]},
181
+ {"update": [{"key": "a", "id": "replacement"}]},
182
+ {"update": [{"key": "a", "uploadable": True}]},
183
+ ])
184
+ def test_invalid_edits_never_write(platform, intent):
185
+ result = edit(intent)
186
+ assert result["status"] is False
187
+ assert result["writeAttempted"] is False
188
+ assert not platform["writes"]
189
+
190
+
191
+ def test_ambiguous_title_returns_candidates_without_write(platform):
192
+ platform["items"][1]["title"] = {"zh_CN": "设置"}
193
+ result = edit({"remove": ["设置"]})
194
+ assert result["errorCode"] == "DROPDOWN_MENU_TARGET_AMBIGUOUS"
195
+ assert len(result["data"]["candidates"]) == 2
196
+ assert not platform["writes"]
197
+
198
+
199
+ def test_missing_property_is_not_empty_menu(platform, monkeypatch):
200
+ monkeypatch.setattr(cmd_properties, "do_get_properties",
201
+ lambda *args: {"status": True, "data": {}})
202
+ result = edit({"append": [{"key": "c", "title": "C"}]})
203
+ assert result["errorCode"] == "DROPDOWN_MENU_READ_UNAVAILABLE"
204
+ assert not platform["writes"]
205
+
206
+
207
+ def test_upload_aliases_are_written_and_read_back(platform):
208
+ result = edit({"append": [{
209
+ "key": "upload", "title": "上传", "uploadable": True,
210
+ "uploadConfig": {"accept": "pdf", "maxFiles": 3, "maxFileSize": 1024, "multiple": True},
211
+ }]})
212
+ assert result["status"] is True
213
+ assert platform["writes"][0][-1]["UploadConfig"] == {
214
+ "Suffix": "pdf", "LimitCount": 3, "LimitSize": 1024, "Multiple": True,
215
+ }
216
+ assert platform["operations"] == 0
217
+
218
+
219
+ def test_upload_patch_retains_unmentioned_configuration(platform):
220
+ platform["items"][1].update(operationKey="", uploadable=True, uploadConfig={
221
+ "suffix": "pdf", "limitCount": 3, "limitSize": 1024, "multiple": True,
222
+ })
223
+ result = edit({"update": [{"key": "b", "uploadConfig": {"maxFiles": 5}}]})
224
+ assert result["status"] is True
225
+ assert platform["writes"][0][1]["UploadConfig"]["LimitSize"] == 1024
226
+ assert platform["writes"][0][1]["UploadConfig"]["LimitCount"] == 5
227
+
228
+
229
+ def test_unknown_upload_keys_stop_before_write(platform):
230
+ result = edit({"append": [{"key": "c", "uploadConfig": {"typo": 3}}]})
231
+ assert result["status"] is False
232
+ assert not platform["writes"]
233
+
234
+
235
+ @pytest.mark.parametrize("extra", [
236
+ {"isv": "another_vendor"},
237
+ {"newAuthoredProperty": "must retain"},
238
+ {"title": {"zh_CN": "设置", "en_US": "Settings"}},
239
+ ])
240
+ def test_original_vendor_languages_and_unknown_properties_are_retained(platform, extra):
241
+ platform["items"][0].update(extra)
242
+ result = edit({"moves": [{"key": "a", "after": "b"}]})
243
+ assert result["status"] is True
244
+ retained = next(item for item in platform["items"] if item["key"] == "a")
245
+ assert all(retained[key] == value for key, value in extra.items())
246
+ assert platform["writes"][0][1]["Id"] == "id-a"
247
+
248
+
249
+ def test_partial_order_keeps_unmentioned_tail(platform):
250
+ result = edit({"order": ["预览"]})
251
+ assert result["status"] is True
252
+ assert [item["key"] for item in platform["items"]] == ["b", "a"]
253
+
254
+
255
+ def test_empty_operation_cannot_serve_as_business_implementation(platform, monkeypatch):
256
+ monkeypatch.setattr(cmd_operations, "do_list_operations", lambda *args: {
257
+ "status": True, "data": [{"operationKey": "empty", "operationType": "donothing",
258
+ "pluginCount": 0, "opBizRuleCount": 0}]})
259
+ result = edit({"append": [{"key": "new", "operation": "empty"}]})
260
+ assert result["errorCode"] == "DROPDOWN_MENU_OPERATION_IMPLEMENTATION_REQUIRED"
261
+ assert not platform["writes"]
262
+
263
+
264
+ def test_same_value_upload_aliases_are_accepted():
265
+ assert cmd_properties._dropdown_upload_config({"suffix": "pdf", "accept": "pdf"}) == {"Suffix": "pdf"}
266
+
267
+
268
+ @pytest.mark.parametrize("encoded", [False, True])
269
+ def test_readback_extension_upload_fields_survive_title_and_option_edits(platform, monkeypatch, encoded):
270
+ config = {"Multiple": True, "Suffix": "pdf", "LimitCount": 5, "UnCompresspic": False,
271
+ "FutureOption": {"enabled": None}}
272
+ platform["items"][1].update(operationKey="", uploadable=True, uploadConfig={
273
+ "multiple": True, "suffix": "pdf", "limitCount": 5, "unCompresspic": False,
274
+ "futureOption": {"enabled": None},
275
+ })
276
+ load = cmd_listui.do_load_full_metadata
277
+ def native_load(*args, **kwargs):
278
+ result = load(*args, **kwargs)
279
+ if not platform.get("saved_document"):
280
+ result["data"]["formmeta"]["Items"][0]["DropdownItems"][1]["UploadConfig"] = (
281
+ json.dumps(config) if encoded else copy.deepcopy(config))
282
+ return result
283
+ monkeypatch.setattr(cmd_listui, "do_load_full_metadata", native_load)
284
+ result = edit({"update": [{"key": "b", "title": "上传模板", "uploadConfig": {"maxFiles": 6}}]})
285
+ assert result["status"] is True
286
+ actual = platform["writes"][0][1]["UploadConfig"]
287
+ assert isinstance(actual, str) == encoded
288
+ actual = json.loads(actual) if encoded else actual
289
+ assert actual == {**config, "LimitCount": 6}
290
+
291
+
292
+ def test_unknown_upload_input_is_still_rejected():
293
+ with pytest.raises(ValueError, match="UNKNOWN"):
294
+ cmd_properties._normalize_dropdown_item({"key": "new", "uploadConfig": {"futureOption": True}})
295
+
296
+
297
+ def test_item_title_can_resolve_parent_without_button_selector(platform):
298
+ result = cmd_properties.do_configure_dropdown_menu({}, "", "form", None, {
299
+ "update": [{"key": "设置", "title": "打印参数设置", "subtitle": "选择参数"}],
300
+ }, locale="zh_CN")
301
+ assert result["status"] is True
302
+ assert platform["writes"][0][0]["OperationKey"] == "op_a"
303
+ assert platform["writes"][0][0]["SubTitle"] == "选择参数"
304
+ assert platform["operations"] == 0
305
+
306
+
307
+ def test_ambiguous_menu_parent_returns_candidates_without_write(platform, monkeypatch):
308
+ load = cmd_listui.do_load_full_metadata
309
+ def ambiguous(*args, **kwargs):
310
+ result = load(*args, **kwargs)
311
+ items = result["data"]["formmeta"]["Items"]
312
+ items.append({**copy.deepcopy(items[0]), "Key": "other_button", "Id": "other-id"})
313
+ return result
314
+ monkeypatch.setattr(cmd_listui, "do_load_full_metadata", ambiguous)
315
+ result = cmd_properties.do_configure_dropdown_menu({}, "", "form", None, {
316
+ "update": [{"key": "设置", "title": "新名称"}],
317
+ })
318
+ assert result["errorCode"] == "DROPDOWN_MENU_PARENT_REQUIRED"
319
+ assert len(result["data"]["candidates"]) == 2
320
+ assert not platform["writes"]
321
+
322
+
323
+ @pytest.mark.parametrize("args,owner", [
324
+ (["queryFormsByApp", "--bad-option"], "describeCommand queryFormsByApp"),
325
+ (["nonexistentCommand"], "describeCommand <selected-command>"),
326
+ ])
327
+ def test_argument_errors_keep_discovery_bounded(tmp_path, args, owner):
328
+ cli = Path(__file__).resolve().parents[1] / "cosmic_api.py"
329
+ result = subprocess.run([sys.executable, str(cli), "--stdout", *args],
330
+ cwd=tmp_path, capture_output=True, text=True)
331
+ assert result.returncode == 2
332
+ receipt = json.loads(result.stdout)
333
+ assert receipt["recovery"]["describeCommand"] == owner
334
+ assert "choose from" not in receipt["message"]
335
+ assert len(receipt["message"]) < 500
336
+
337
+
338
+ def test_failed_save_never_retries_or_renames(platform, monkeypatch):
339
+ api = mock.Mock(return_value={"status": False, "errorCode": "META_SAVE_FAIL",
340
+ "message": "key collision"})
341
+ monkeypatch.setattr(cmd_listui, "do_save_full_metadata", api)
342
+ result = edit({"update": [{"key": "a", "subtitle": "X"}]})
343
+ assert result["status"] is False
344
+ assert result["recovery"]["automaticRetry"] is False
345
+ api.assert_called_once()
346
+ assert api.call_args.args[3]["formmeta"]["Items"][0]["DropdownItems"][0]["Key"] == "a"
347
+ assert platform["items"] == CURRENT
348
+
349
+
350
+ def test_readback_mismatch_is_not_success(platform, monkeypatch):
351
+ # API says saved, but independently read metadata still contains the old title.
352
+ monkeypatch.setattr(cmd_listui, "do_save_full_metadata",
353
+ lambda *args, **kwargs: {"status": True, "data": {}})
354
+ result = edit({"update": [{"key": "a", "title": "New"}]})
355
+ assert result["status"] is False
356
+ assert result["verification"]["verified"] is False
357
+ assert result["data"]["changed"] is None
358
+
359
+
360
+ def test_missing_operation_is_not_replaced_by_placeholder(platform):
361
+ result = edit({"append": [{"key": "c", "operation": "missing"}]})
362
+ assert result["errorCode"] == "DROPDOWN_MENU_OPERATION_NOT_FOUND"
363
+ assert not platform["writes"]
364
+
365
+
366
+ def test_generic_replacement_requires_semantic_edit(platform):
367
+ result = cmd_properties.do_set_complex_property({}, "", "form", "dropdownItems", [],
368
+ element_key="print")
369
+ assert result["errorCode"] == "DROPDOWN_MENU_EDIT_REQUIRED"
370
+ assert not platform["writes"]
371
+
372
+
373
+ def test_identity_comparison_is_case_sensitive():
374
+ assert cmd_properties._dropdown_mismatches([{"Key": "a"}], [{"Key": "A"}])
375
+
376
+
377
+ def test_property_discovery_routes_to_semantic_menu_edit():
378
+ result = cmd_properties._describe_one_property(
379
+ {"propertyName": "DropdownItems", "editable": True, "complexity": "complex"},
380
+ None, None)
381
+ assert result["inputFormat"] == "menuEditIntent"
382
+ assert "configureDropdownMenu" in result["guidance"]
@@ -2734,31 +2734,6 @@ def test_apply_form_grid_converts_fieldset_to_runtime_flex(
2734
2734
  assert "layoutProjection" not in result["data"]
2735
2735
 
2736
2736
 
2737
- def test_form_control_traits_match_harvested_platform_superclasses():
2738
- app_build = next(
2739
- parent for parent in pathlib.Path(__file__).resolve().parents
2740
- if parent.name == "app-build"
2741
- )
2742
- scripts_root = app_build / "scripts"
2743
- if str(scripts_root) not in sys.path:
2744
- sys.path.insert(0, str(scripts_root))
2745
- from workspace_layout import repository_root
2746
- repo = repository_root(app_build)
2747
- source = __import__("json").loads(
2748
- (repo / "tests/metadata-xml-corpus/contracts/element-superclasses.json")
2749
- .read_text(encoding="utf-8")
2750
- )
2751
- for type_name, traits in _FORM_CONTROL_TRAITS.items():
2752
- chain = []
2753
- current = type_name
2754
- seen = set()
2755
- while current in source and current not in seen:
2756
- seen.add(current)
2757
- current = source[current]
2758
- chain.append(current)
2759
- assert chain == traits["superclasses"]
2760
-
2761
-
2762
2737
  def test_compile_form_tabs_moves_complete_subtrees_and_leaves_attachment_outside():
2763
2738
  intent, evidence = compile_form_tabs_intent({
2764
2739
  "pages": [
@@ -28,6 +28,48 @@ def _minimal_bill_entity(*, biz_name: str, fields: list[dict]) -> dict:
28
28
 
29
29
 
30
30
  class FieldDecoratorTrimAndAliasTest(unittest.TestCase):
31
+ def test_explicit_names_are_checked_after_prefixing(self):
32
+ raw = {"entities": [{
33
+ **_minimal_bill_entity(biz_name="bill", fields=[]), "logicalName": "x" * 25,
34
+ }]}
35
+ with self.assertRaisesRegex(ValueError, "including developer prefix"):
36
+ field_decorator.decorate_artifact(raw, "acme", "acme_app", standard=False)
37
+ raw["entities"][0]["logicalName"] = "mybill"
38
+ raw["entities"][0]["fields"] = [
39
+ {"bizWord": "one", "logicalName": "same", "displayName": "一", "dataType": "TextField"},
40
+ {"bizWord": "two", "logicalName": "acme_same", "displayName": "二", "dataType": "TextField"},
41
+ ]
42
+ with self.assertRaisesRegex(ValueError, "duplicate fieldKey"):
43
+ field_decorator.decorate_artifact(raw, "acme", "acme_app", standard=False)
44
+
45
+ def test_explicit_logical_identifiers_are_namespaced_once_with_references(self):
46
+ for prefix in ("", "acme_"):
47
+ raw = {"entities": [{
48
+ **_minimal_bill_entity(biz_name="bill", fields=[{
49
+ "bizWord": "remark", "logicalName": prefix + "note",
50
+ "displayName": "备注", "dataType": "TextField",
51
+ }]), "logicalName": prefix + "mybill",
52
+ }], "propertySettings": [{"elementKey": prefix + "note", "settingType": "simple",
53
+ "properties": {"Name": "备注"}}],
54
+ "uiLayout": {"containers": [{"key": "panel", "children": [prefix + "note"]}]},
55
+ "rules": [{"ruleType": "formRule", "triggerField": prefix + "note",
56
+ "mountPoint": prefix + "mybill", "preCondition": prefix + "note" + " = '123'"}],
57
+ "operations": [{"operationKey": "save", "parameter": {
58
+ "fieldKey": prefix + "note", "formNumber": prefix + "mybill",
59
+ "message": prefix + "note", "entityId": "external-id"}}]}
60
+ decorated = field_decorator.decorate_artifact(raw, "acme", "acme_app", standard=False)
61
+ self.assertEqual(decorated["entities"][0]["entityKey"], "acme_mybill")
62
+ self.assertEqual(decorated["entities"][0]["fields"][0]["fieldKey"], "acme_note")
63
+ self.assertEqual(decorated["propertySettings"][0]["elementKey"], "acme_note")
64
+ self.assertEqual(decorated["uiLayout"]["containers"][0]["children"], ["acme_note"])
65
+ projection = field_decorator.make_entity_key_map(raw["entities"], "acme", standard=False)
66
+ self.assertEqual(projection[prefix + "mybill"], "acme_mybill")
67
+ self.assertEqual(decorated["rules"][0]["triggerField"], "acme_note")
68
+ self.assertEqual(decorated["rules"][0]["mountPoint"], "acme_mybill")
69
+ self.assertEqual(decorated["operations"][0]["parameter"]["fieldKey"], "acme_note")
70
+ self.assertEqual(decorated["operations"][0]["parameter"]["message"], prefix + "note")
71
+ self.assertEqual(decorated["operations"][0]["parameter"]["entityId"], "external-id")
72
+
31
73
  """decorate_artifact: trimming + alias mapping, ensuring rules/entryToolbar can be replaced."""
32
74
 
33
75
  def setUp(self) -> None:
@@ -88,7 +130,7 @@ class FieldDecoratorTrimAndAliasTest(unittest.TestCase):
88
130
  )
89
131
 
90
132
  def test_explicit_entity_identity_is_preserved_while_table_is_projected(self) -> None:
91
- logical_name = "AcmePromotionCouponV2026" # exact 24-character user identity
133
+ logical_name = "acme_PromotionCoupon2026" # prefixed identity within the entity budget
92
134
  raw = {
93
135
  "entities": [
94
136
  {
@@ -220,7 +262,7 @@ class FieldDecoratorTrimAndAliasTest(unittest.TestCase):
220
262
  self.assertEqual(253, action["RET"])
221
263
  self.assertEqual(1, action["Seq"])
222
264
 
223
- def test_call_field_change_is_rejected_by_authoring_compiler(self) -> None:
265
+ def test_call_field_change_is_serialized_by_authoring_compiler(self) -> None:
224
266
  raw = {
225
267
  "entities": [_minimal_bill_entity(
226
268
  biz_name="rule_bill",
@@ -231,10 +273,12 @@ class FieldDecoratorTrimAndAliasTest(unittest.TestCase):
231
273
  "trueActions": [{"actionType": "CallFieldChangeAction", "expression": "target"}],
232
274
  }],
233
275
  }
234
- with self.assertRaisesRegex(ValueError, "RULE_ACTION_CONTRACT_BLOCKED"):
235
- field_decorator.decorate_artifact(
236
- raw, isv="acme", app_number="acme_app", standard=False
237
- )
276
+ out = field_decorator.decorate_artifact(
277
+ raw, isv="acme", app_number="acme_app", standard=False
278
+ )
279
+ action = out["rules"][0]["trueActions"][0]
280
+ self.assertEqual("CallFieldChangeAction", action["ActionType"])
281
+ self.assertEqual(773, action["RET"])
238
282
 
239
283
  def test_bizword_projection_updates_dynamic_layout_children(self) -> None:
240
284
  long_bw = "next_followup_date"