@shundoo-ai/dsh-cosmic 1.0.21 → 1.0.22

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 (218) hide show
  1. package/package.json +1 -1
  2. package/skills/cosmic-build/LOCAL-PATCHES.md +43 -14
  3. package/skills/cosmic-build/SKILL.md +24 -9
  4. package/skills/cosmic-build/examples/java-rules/CosmicReportAggregation.java +26 -17
  5. package/skills/cosmic-build/examples/java-rules/CosmicReportFilters.java +96 -2
  6. package/skills/cosmic-build/knowledge/cosmic/arch/security/security_standard.md +4 -4
  7. package/skills/cosmic-build/knowledge/cosmic/platform/allimport.md +2 -1
  8. package/skills/cosmic-build/knowledge/cosmic/platform/core/dynamic-object-collection.md +6 -4
  9. package/skills/cosmic-build/knowledge/cosmic/platform/core/model.md +9 -9
  10. package/skills/cosmic-build/knowledge/cosmic/platform/core/view.md +5 -5
  11. package/skills/cosmic-build/knowledge/cosmic/platform/form-plugin.md +8 -4
  12. package/skills/cosmic-build/knowledge/cosmic/platform/list-plugin.md +5 -3
  13. package/skills/cosmic-build/knowledge/cosmic/platform/print-plugin-guide.md +1 -0
  14. package/skills/cosmic-build/knowledge/cosmic/platform/report-plugin.md +269 -57
  15. package/skills/cosmic-build/knowledge/cosmic/platform/reportlistdata-plugin.md +130 -23
  16. package/skills/cosmic-build/knowledge/cosmic/platform/reporttreedata-plugin.md +48 -28
  17. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/_index.md +12 -3
  18. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/common/attachment-name-process.md +17 -13
  19. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/common/dynamic-update-field.md +4 -4
  20. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/filter-grid/filter-grid-set-value.md +1 -1
  21. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/list/filter-basedatafield.md +2 -3
  22. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/list/filter-container-init.md +0 -1
  23. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/list/filter-popup.md +0 -1
  24. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/list/filter-value-parse.md +5 -6
  25. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/list/list-show-form.md +1 -1
  26. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/list/query-field-dynamic.md +0 -7
  27. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/data-access/business-data/delete-service.md +24 -29
  28. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/data-access/ksql/db-batch.md +4 -0
  29. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/data-access/ksql/db-direct-access.md +4 -0
  30. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/data-access/qfilter/entity-object-access.md +8 -3
  31. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/data-access/qfilter/qfilter-compose.md +6 -5
  32. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/event/after-bind-data-set-visible.md +5 -1
  33. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/event/click-return-data.md +2 -2
  34. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/event/closed-call-back-set-value.md +2 -2
  35. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/event/register-listener.md +1 -1
  36. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/event/set-filter-tab-org.md +3 -0
  37. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/infra/dispatch-service/microservice-dev.md +18 -11
  38. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/infra/dist-lock/dist-lock-basic.md +3 -6
  39. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/infra/mq/mq-subscribe.md +5 -1
  40. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/infra/thread-pool/async-operation.md +10 -6
  41. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/infra/thread-pool/big-task-async.md +4 -2
  42. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/infra/thread-pool/schedule-task.md +9 -7
  43. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/operation/operation-interactive.md +33 -34
  44. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/operation/operation-plugin-events.md +30 -22
  45. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-async-query.md +133 -0
  46. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-batch-query.md +26 -22
  47. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-chart.md +112 -0
  48. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-dataset-assembly.md +200 -0
  49. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-head-filter.md +177 -0
  50. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-hyperlink-drill.md +216 -0
  51. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-list-data-query.md +12 -12
  52. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-merge-column.md +44 -6
  53. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-print-data.md +163 -0
  54. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-row-data-toolbar.md +188 -0
  55. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-subtotal-total.md +139 -0
  56. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-tree-data-query.md +108 -12
  57. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-tree-list.md +181 -0
  58. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/system-service/report-plugin-interface.md +3 -0
  59. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/tree/tree-basedata-cascade-confirm.md +14 -11
  60. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/tree/tree-list-before-item-validate.md +1 -1
  61. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/tree/tree-list-build-filter.md +1 -1
  62. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/tree/tree-list-multi-level-filter.md +11 -11
  63. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/tree/tree-list-refresh-node.md +22 -15
  64. package/skills/cosmic-build/knowledge/cosmic/platform/sdk/sdk-algo.md +2 -2
  65. package/skills/cosmic-build/knowledge/cosmic/platform/sdk/sdk-entity-model.md +3 -4
  66. package/skills/cosmic-build/knowledge/cosmic/platform/sdk/sdk-exception.md +9 -5
  67. package/skills/cosmic-build/knowledge/cosmic/platform/sdk/sdk-orm-access.md +4 -0
  68. package/skills/cosmic-build/knowledge/cosmic/platform/sdk/sdk-request-context.md +7 -3
  69. package/skills/cosmic-build/knowledge/cosmic/platform/standards.md +28 -3
  70. package/skills/cosmic-build/references/compile-guide.md +36 -1
  71. package/skills/cosmic-build/references/dcs-code-rules.md +180 -0
  72. package/skills/cosmic-build/references/delivery.md +16 -2
  73. package/skills/cosmic-build/references/execution-contract.md +15 -3
  74. package/skills/cosmic-build/references/java-plugin-delivery.md +21 -11
  75. package/skills/cosmic-build/references/plugin-handoff-input.md +82 -24
  76. package/skills/cosmic-build/references/plugin-runtime-routing.md +23 -6
  77. package/skills/cosmic-build/references/report-filter-contract.md +53 -15
  78. package/skills/cosmic-build/references/sdk-inspection.md +4 -1
  79. package/skills/cosmic-build/references/type-registry.json +26 -17
  80. package/skills/cosmic-build/schemas/data-model-authoring.schema.json +1 -1
  81. package/skills/cosmic-build/scripts/_http_client.py +3 -0
  82. package/skills/cosmic-build/scripts/compile_plugin_source.py +251 -42
  83. package/skills/cosmic-build/scripts/configure_list.py +112 -0
  84. package/skills/cosmic-build/scripts/dcs-rule-catalog.json +799 -0
  85. package/skills/cosmic-build/scripts/dcs_code_gate.py +1976 -0
  86. package/skills/cosmic-build/scripts/execution_binding.py +25 -0
  87. package/skills/cosmic-build/scripts/platform_api_contract.py +352 -7
  88. package/skills/cosmic-build/scripts/plugin_source_gate.py +9 -1
  89. package/skills/cosmic-build/scripts/plugin_target_sdk.py +12 -8
  90. package/skills/cosmic-build/scripts/prepare_plugin_handoff.py +488 -88
  91. package/skills/cosmic-build/scripts/query_plugin_handoff.py +207 -20
  92. package/skills/cosmic-build/scripts/register_plugins.py +27 -10
  93. package/skills/cosmic-build/scripts/render_output_cards.py +57 -20
  94. package/skills/cosmic-build/scripts/resolve_output_path.py +60 -26
  95. package/skills/cosmic-build/scripts/run_java_tests.py +162 -0
  96. package/skills/cosmic-build/scripts/sync_generated_locale_assets.py +28 -0
  97. package/skills/cosmic-build/scripts/test/fixtures/ReportSdkCheck.java +1 -22
  98. package/skills/cosmic-build/scripts/test/fixtures/list_column_property_declarations.json +887 -0
  99. package/skills/cosmic-build/scripts/test/fixtures/operation-entry-field-prepare.java +78 -0
  100. package/skills/cosmic-build/scripts/test/test_add_field_required_contract.py +12 -0
  101. package/skills/cosmic-build/scripts/test/test_batch5_drift_governance.py +23 -2
  102. package/skills/cosmic-build/scripts/test/test_compile_plugin_source.py +287 -4
  103. package/skills/cosmic-build/scripts/test/test_configure_list_delivery.py +132 -0
  104. package/skills/cosmic-build/scripts/test/test_cosmic_sdk_examples.py +58 -1
  105. package/skills/cosmic-build/scripts/test/test_data_model_contract.py +19 -0
  106. package/skills/cosmic-build/scripts/test/test_dcs_code_gate.py +901 -0
  107. package/skills/cosmic-build/scripts/test/test_delivery_stage_boundary.py +1 -1
  108. package/skills/cosmic-build/scripts/test/test_direct_cli_architecture.py +2 -0
  109. package/skills/cosmic-build/scripts/test/test_http_client.py +6 -0
  110. package/skills/cosmic-build/scripts/test/test_java_toolchain_feedback.py +206 -0
  111. package/skills/cosmic-build/scripts/test/test_kd_api.py +21 -0
  112. package/skills/cosmic-build/scripts/test/test_list_configuration_closure.py +184 -0
  113. package/skills/cosmic-build/scripts/test/test_mcp_cli.py +139 -2
  114. package/skills/cosmic-build/scripts/test/test_menu_creation_closure.py +16 -0
  115. package/skills/cosmic-build/scripts/test/test_modeling_input_recovery.py +55 -1
  116. package/skills/cosmic-build/scripts/test/test_open_world_cli_catalog.py +1 -1
  117. package/skills/cosmic-build/scripts/test/test_operation_field_preparation.py +84 -0
  118. package/skills/cosmic-build/scripts/test/test_platform_api_contract.py +322 -1
  119. package/skills/cosmic-build/scripts/test/test_plugin_handoff.py +951 -4
  120. package/skills/cosmic-build/scripts/test/test_plugin_source_gate.py +30 -2
  121. package/skills/cosmic-build/scripts/test/test_plugin_target_sdk.py +21 -0
  122. package/skills/cosmic-build/scripts/test/test_property_reroute.py +165 -0
  123. package/skills/cosmic-build/scripts/test/test_property_schema_first.py +35 -0
  124. package/skills/cosmic-build/scripts/test/test_query_plugin_handoff.py +221 -0
  125. package/skills/cosmic-build/scripts/test/test_register_plugins.py +76 -4
  126. package/skills/cosmic-build/scripts/test/test_render_output_cards.py +75 -3
  127. package/skills/cosmic-build/scripts/test/test_report_business_contract.py +27 -5
  128. package/skills/cosmic-build/scripts/test/test_report_doc_contract_sync.py +103 -11
  129. package/skills/cosmic-build/scripts/test/test_report_plugin_source_gate.py +46 -16
  130. package/skills/cosmic-build/scripts/test/test_report_properties.py +74 -15
  131. package/skills/cosmic-build/scripts/test/test_report_query_data_source.py +3 -1
  132. package/skills/cosmic-build/scripts/test/test_report_sdk_optimization.py +1 -2
  133. package/skills/cosmic-build/scripts/test/test_test_environment.py +76 -1
  134. package/skills/cosmic-build/scripts/test_environment.py +67 -1
  135. package/skills/cosmic-build/skills/cosmic-coding-impl/SKILL.md +52 -10
  136. package/skills/cosmic-build/skills/cosmic-meta-api/apis/ai_meta.md +16 -3
  137. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/report-write-contracts.json +59 -26
  138. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/rule-domain.json +1 -1
  139. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/schema/report-write.schema.json +75 -18
  140. package/skills/cosmic-build/skills/cosmic-meta-api/modules/pdm/scripts/pdm_auto.py +39 -0
  141. package/skills/cosmic-build/skills/cosmic-meta-api/modules/pdm/scripts/pdm_client.py +97 -10
  142. package/skills/cosmic-build/skills/cosmic-meta-api/modules/verify/SKILL.md +10 -12
  143. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_apps.py +3 -1
  144. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_formactions.py +38 -29
  145. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_listui.py +400 -233
  146. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_metaops.py +87 -45
  147. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_properties.py +42 -15
  148. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_rules.py +135 -5
  149. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/config.py +1 -1
  150. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/execution_errors.py +1 -1
  151. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/formatters.py +17 -11
  152. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/property_reroute.py +15 -5
  153. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/report_ops.py +1429 -238
  154. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/validators.py +56 -28
  155. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/cosmic_api.py +380 -134
  156. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/field_decorator.py +1 -1
  157. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/meta_export.py +12 -11
  158. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/meta_verifier.py +42 -0
  159. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_add_field_placement_closure.py +8 -8
  160. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_build_configuration_readback.py +94 -0
  161. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_build_receipt_resume.py +47 -1
  162. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_metadata_unchanged_fail_closed.py +87 -0
  163. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_dym_evidence.py +23 -23
  164. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_entity_integrity.py +279 -4
  165. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_field_guard.py +1 -1
  166. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_plugin_contract.py +509 -32
  167. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_readback_derivation.py +178 -1
  168. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_recreate_plan.py +125 -10
  169. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_serve_contract.py +208 -0
  170. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_typed_filters.py +259 -50
  171. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_rule_inventory_perf.py +29 -0
  172. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_rule_write_closure.py +20 -0
  173. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_run_batch_post_build_recovery.py +18 -0
  174. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_run_batch_verification_evidence.py +35 -0
  175. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_run_identity_guard.py +25 -0
  176. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_time_range_rule.py +98 -0
  177. package/skills/cosmic-build/skills/cosmic-meta-knowledge/domain-model/report-model.md +20 -9
  178. package/skills/cosmic-build/skills/cosmic-meta-knowledge/generation/report-modeling-guide.md +55 -25
  179. package/skills/cosmic-build/skills/cosmic-meta-knowledge/property-guides/report-column-properties.md +15 -39
  180. package/skills/cosmic-build/skills/cosmic-meta-modeling/SKILL.md +19 -17
  181. package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/report/SKILL.md +82 -38
  182. package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/report/examples/golden-example-report-batch.md +64 -19
  183. package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/report/examples/golden-example-report-plugin.md +32 -20
  184. package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/report/examples/golden-example-report-tree.md +78 -31
  185. package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/report/report-artifact-schema.json +2 -1
  186. package/skills/cosmic-build/skills/cosmic-meta-modeling/references/authoring-companion-fields.md +3 -0
  187. package/skills/cosmic-build/skills/cosmic-meta-modeling/references/authoring-list-configuration.md +2 -0
  188. package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/data_model_contract.py +3 -1
  189. package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/data_model_emit.py +27 -4
  190. package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/report_validator.py +99 -44
  191. package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/test_report_examples.py +31 -4
  192. package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/test_report_validator.py +129 -29
  193. package/skills/cosmic-build/skills/cosmic-meta-modifier/SKILL.md +6 -2
  194. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/operation/validation.md +2 -0
  195. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/property/SKILL.md +1 -1
  196. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/report-op/SKILL.md +104 -25
  197. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/SKILL.md +9 -0
  198. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/verify/usage-guide.md +2 -2
  199. package/skills/cosmic-build/skills/cosmic-meta-modifier/references/list-filter-modification.md +39 -12
  200. package/skills/cosmic-build/skills/mcp-server/SKILL.md +39 -24
  201. package/skills/cosmic-build/skills/mcp-server/scripts/mcp_cli.py +20 -6
  202. package/skills/cosmic-dev-best-practices/SKILL.md +1 -1
  203. package/skills/cosmic-dev-best-practices/references/README.md +2 -2
  204. package/skills/cosmic-dev-best-practices/references/basedata-dev-convention.md +2 -2
  205. package/skills/cosmic-dev-best-practices/references/bill-dev-convention.md +4 -4
  206. package/skills/cosmic-dev-best-practices/references/design-document-convention.md +2 -2
  207. package/skills/cosmic-dev-best-practices/references/design-examples/basedata-design-example.md +4 -4
  208. package/skills/cosmic-dev-best-practices/references/design-examples/bill-design-example.md +13 -13
  209. package/skills/cosmic-dev-best-practices/references/elements/CustomerField.md +1 -1
  210. package/skills/cosmic-dev-best-practices/references/elements/README.md +1 -1
  211. package/skills/cosmic-dev-best-practices/references/elements/SupplierField.md +1 -1
  212. package/skills/cosmic-dev-best-practices/references/elements/common-field-property-conventions.md +109 -13
  213. package/skills/cosmic-dev-best-practices/references/layout-control-convention.md +1 -1
  214. package/skills/erp-analysis/references/delivery-contract.md +2 -1
  215. package/skills/erp-analysis/templates/AGENTS.template.md +3 -1
  216. package/skills/cosmic-build/assets/platform-report-filters/TypedReportFilterFields.java +0 -169
  217. package/skills/cosmic-build/assets/platform-report-filters/platform.patch +0 -98
  218. package/skills/cosmic-build/scripts/apply_report_filter_extension.py +0 -39
@@ -12,9 +12,15 @@ as setReportFilters.
12
12
  These tests pin the fail-closed guard: the CLI refuses before touching the platform.
13
13
  """
14
14
 
15
+ import copy
16
+ import io
17
+ import json
15
18
  import os
16
19
  import sys
20
+ import tempfile
21
+ import urllib.error
17
22
  import unittest
23
+ from pathlib import Path
18
24
  from unittest import mock
19
25
 
20
26
  sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
@@ -74,8 +80,6 @@ class ReportEntityGuardTest(unittest.TestCase):
74
80
  for label, action in (
75
81
  ("filters", lambda: report_ops.set_report_filters(
76
82
  {}, None, "FID", FILTERS, form_number="kdtest_rpt")),
77
- ("sorts", lambda: report_ops.set_report_sorts(
78
- {}, None, "FID", SORTS, form_number="kdtest_rpt")),
79
83
  ("plugin", lambda: report_ops.register_report_plugin(
80
84
  {}, None, "FID", PLUGIN, form_number="kdtest_rpt")),
81
85
  ):
@@ -83,6 +87,13 @@ class ReportEntityGuardTest(unittest.TestCase):
83
87
  self.assertIsNone(error, f"{label}: {error}")
84
88
  self.assertEqual(1, writes, label)
85
89
 
90
+ def test_sorts_are_refused_even_when_the_entity_is_healthy(self):
91
+ for fields in (["sup_number", "sup_name"], ["sup_number", "sup_number"]):
92
+ error, writes = self._run(fields, lambda: report_ops.set_report_sorts(
93
+ {}, None, "FID", SORTS, form_number="kdtest_rpt"))
94
+ self.assertIn("REPORT_SORTS_API_UNSAFE", str(error))
95
+ self.assertEqual(writes, 0)
96
+
86
97
  # ── second column write: refuse before corrupting ───────────────────────
87
98
  def test_second_column_write_is_refused_without_touching_the_platform(self):
88
99
  error, writes = self._run(
@@ -107,6 +118,7 @@ class ReportEntityGuardTest(unittest.TestCase):
107
118
  {}, None, "FID", COLUMNS, form_number="kdtest_rpt"))
108
119
  message = str(error)
109
120
  self.assertIn("addReportColumns", message)
121
+ self.assertIn("removeReportColumn", message)
110
122
  self.assertIn("planReportRecreate", message)
111
123
  self.assertIn("Do not add the column in the designer", message)
112
124
 
@@ -132,8 +144,6 @@ class ReportEntityGuardTest(unittest.TestCase):
132
144
  {}, None, "FID", COLUMNS, form_number="kdtest_rpt")),
133
145
  ("filters", lambda: report_ops.set_report_filters(
134
146
  {}, None, "FID", FILTERS, form_number="kdtest_rpt")),
135
- ("sorts", lambda: report_ops.set_report_sorts(
136
- {}, None, "FID", SORTS, form_number="kdtest_rpt")),
137
147
  ("plugin", lambda: report_ops.register_report_plugin(
138
148
  {}, None, "FID", PLUGIN, form_number="kdtest_rpt")),
139
149
  ):
@@ -282,5 +292,270 @@ class ReportWriteFailureHonestyTest(unittest.TestCase):
282
292
  self.assertIn("setReportColumns failed: boom", str(ctx.exception))
283
293
 
284
294
 
295
+ class ReportColumnRemovalTest(unittest.TestCase):
296
+ """真实删除契约和旧后端失败边界共用同一实现,不调用线上接口。"""
297
+
298
+ def column(self, key):
299
+ return {"kind": "column", "fieldKey": key, "dateIndex": key,
300
+ "fieldType": "text", "caption": key}
301
+
302
+ def schema(self, columns):
303
+ return {"formNumber": "kdtest_rpt", "reportColumns": columns,
304
+ "reportFilters": [], "reportSorts": [{"fieldKey": "keep", "direction": "asc"}],
305
+ "reportPlugins": [], "readBackAvailable": True, "readBackSource": "canonical"}
306
+
307
+ def receipt(self, key="drop"):
308
+ return {"status": True, "errorCode": "0", "data": {"success": True, "errorCode": "0",
309
+ "data": {"fieldKey": key, "fieldId": "FIELD", "action": "removed"}}}
310
+
311
+ def failed(self, message, code="META_SAVE_FAIL"):
312
+ return {"status": False, "errorCode": "META_SAVE_FAIL", "message": message,
313
+ "data": {"success": False, "errorCode": code, "message": message}}
314
+
315
+ def run_removal(self, root, response=None, after=None):
316
+ import cosmic_api
317
+ before = self.schema([self.column("drop"), self.column("keep")])
318
+ projection = after if after is not None else self.schema([self.column("keep")])
319
+ calls = []
320
+ def api(cfg, token, command, payload, method="GET"):
321
+ calls.append((command, payload, method))
322
+ if method == "POST":
323
+ if isinstance(response, Exception):
324
+ raise response
325
+ return response if response is not None else self.receipt()
326
+ if any(call[2] == "POST" for call in calls):
327
+ if isinstance(projection, Exception):
328
+ raise projection
329
+ return {"data": {"formNumber": "kdtest_rpt", "reportSchema": projection}}
330
+ return {"data": {"formNumber": "kdtest_rpt", "reportSchema": before}}
331
+ with mock.patch.object(report_ops, "_api_call", side_effect=api), \
332
+ mock.patch.object(cosmic_api, "_resolve_owned_report_target", return_value={"formId": "FID"}):
333
+ result = cosmic_api._dispatch_serve({}, "fixture", "removeReportColumn",
334
+ {"appRoot": root, "formNumber": "kdtest_rpt", "fieldKey": "drop"})
335
+ return result, calls
336
+
337
+ def baseline(self, root):
338
+ before = self.schema([self.column("drop"), self.column("keep")])
339
+ artifact = {"reportColumns": copy.deepcopy(before["reportColumns"]),
340
+ "reportSorts": [{"fieldKey": "drop", "direction": "desc"}, *before["reportSorts"]],
341
+ "reportFilters": [{"fieldPath": "flt_drop", "displayName": "Drop", "compare": "="}],
342
+ "reportPlugins": [{"className": "kd.example.report.Query", "type": "Java"}],
343
+ "reportProperties": {"columns": {"drop": {"Width": "80"}, "keep": {"Width": "100"}}}}
344
+ artifact["reportColumns"][1]["presentation"] = {"kind": "basedata", "entityId": "bos_user", "displayProp": "name"}
345
+ report_ops.write_report_authoring_state(root, "kdtest_rpt", "FID", artifact, before)
346
+ return artifact, before
347
+
348
+ def test_success_is_one_real_remove_post_with_independent_readback(self):
349
+ with tempfile.TemporaryDirectory() as root:
350
+ result, calls = self.run_removal(root)
351
+ self.assertTrue(result["status"])
352
+ self.assertTrue(result["verification"]["verified"])
353
+ self.assertTrue(result["data"]["platformMutationApplied"])
354
+ self.assertFalse(result["data"]["runtimeVerified"])
355
+ self.assertFalse(result["verification"]["entityFieldCleanup"]["verified"])
356
+ self.assertEqual(result["data"]["reportAuthoringState"]["status"], "incomplete")
357
+ writes = [call for call in calls if call[2] == "POST"]
358
+ self.assertEqual(writes, [("setComplexProperty", {"formNumber": "kdtest_rpt",
359
+ "complexType": "ReportColumns.remove", "data": {"fieldKey": "drop"}}, "POST")])
360
+ self.assertEqual(calls[-1][0], "getFormSchema")
361
+
362
+ def test_old_branch_and_http_route_preserve_complete_baseline(self):
363
+ from _core.execution_errors import attach_execution_error
364
+ responses = [self.failed("不支持的复杂属性类型: ReportColumns.remove。"),
365
+ self.failed("Unsupported complex property type: ReportColumns.remove."),
366
+ {"status": False, "errorCode": "404", "message": "cannot found openapi"},
367
+ *[urllib.error.HTTPError("https://example.invalid/setComplexProperty", code,
368
+ "Missing route", {}, io.BytesIO(b"not found")) for code in (404, 405, 501)]]
369
+ for response in responses:
370
+ with self.subTest(response=response), tempfile.TemporaryDirectory() as root:
371
+ self.baseline(root)
372
+ path = report_ops.report_authoring_path(root, "kdtest_rpt")
373
+ original = path.read_bytes()
374
+ result, calls = self.run_removal(root, response)
375
+ self.assertEqual(path.read_bytes(), original)
376
+ self.assertFalse(result["status"])
377
+ self.assertEqual(result["errorCode"], "REPORT_COLUMN_DELETE_API_UNAVAILABLE")
378
+ categorized = attach_execution_error(result)
379
+ self.assertEqual(categorized["errorCategory"], "capability_limit")
380
+ self.assertTrue(categorized["capabilityLimitConfirmed"])
381
+ self.assertFalse(result["data"]["platformMutationApplied"])
382
+ self.assertNotIn("platformMutationPossible", result["data"])
383
+ self.assertEqual(sum(call[2] == "POST" for call in calls), 1)
384
+ self.assertNotIn("reportAuthoringState", result["data"])
385
+
386
+ def test_save_permission_and_transient_failures_are_not_capability_missing(self):
387
+ responses = [self.failed("save rejected"), self.failed("permission denied", "403"),
388
+ self.failed("NullPointerException"), {"status": False, "errorCode": "404", "message": "column not found"},
389
+ TimeoutError("gateway"), urllib.error.HTTPError("https://example.invalid", 503, "Unavailable", {}, None)]
390
+ for response in responses:
391
+ with self.subTest(response=response), tempfile.TemporaryDirectory() as root:
392
+ self.baseline(root)
393
+ result, calls = self.run_removal(root, response)
394
+ self.assertFalse(result["status"])
395
+ self.assertNotEqual(result["errorCode"], "REPORT_COLUMN_DELETE_API_UNAVAILABLE")
396
+ self.assertTrue(result["data"]["platformMutationPossible"])
397
+ self.assertNotIn("platformMutationApplied", result["data"])
398
+ state = json.loads(report_ops.report_authoring_path(root, "kdtest_rpt").read_text())
399
+ self.assertEqual(state["status"], "incomplete")
400
+ self.assertEqual(sum(call[2] == "POST" for call in calls), 1)
401
+
402
+ def test_conflicting_wrappers_and_http_bodies_do_not_mask_failures(self):
403
+ unsupported = "不支持的复杂属性类型: ReportColumns.remove。"
404
+ conflict = {**self.failed("save rejected"), "message": unsupported}
405
+ success_error = self.receipt()
406
+ success_error["data"]["errorCode"] = "META_SAVE_FAIL"
407
+ broken_body = mock.Mock()
408
+ broken_body.read.side_effect = OSError("body unavailable")
409
+ responses = [conflict, success_error,
410
+ {**self.failed(unsupported, "403"), "message": unsupported},
411
+ urllib.error.HTTPError("https://example.invalid", 404, "Not Found", {},
412
+ io.BytesIO(json.dumps(self.failed("save rejected")).encode())),
413
+ urllib.error.HTTPError("https://example.invalid", 404, "Not Found", {},
414
+ io.BytesIO(json.dumps(self.receipt()).encode())),
415
+ urllib.error.HTTPError("https://example.invalid", 404, "Not Found", {}, broken_body)]
416
+ for response in responses:
417
+ with self.subTest(response=response), tempfile.TemporaryDirectory() as root:
418
+ result, calls = self.run_removal(root, response)
419
+ self.assertFalse(result["status"])
420
+ self.assertNotEqual(result["errorCode"], "REPORT_COLUMN_DELETE_API_UNAVAILABLE")
421
+ self.assertTrue(result["data"]["platformMutationPossible"])
422
+ self.assertEqual(sum(call[2] == "POST" for call in calls), 1)
423
+
424
+ def test_explicit_business_rejection_is_not_capability_missing_or_mutation(self):
425
+ for code in ("PARAM_INVALID", "PROPERTY_ELEMENT_NOT_FOUND", "ELEMENT_NOT_FOUND"):
426
+ with self.subTest(code=code), tempfile.TemporaryDirectory() as root:
427
+ self.baseline(root)
428
+ with self.assertRaisesRegex(RuntimeError, "REPORT_COLUMN_DELETE_REJECTED") as error:
429
+ self.run_removal(root, self.failed("target rejected", code))
430
+ self.assertNotIsInstance(error.exception, report_ops.ReportMutationError)
431
+ self.assertEqual(json.loads(report_ops.report_authoring_path(root, "kdtest_rpt").read_text())["status"], "current")
432
+
433
+ def test_success_receipt_followed_by_bad_readback_invalidates_without_replay(self):
434
+ for after in (TimeoutError("read failed"), {}, self.schema([self.column("drop"), self.column("keep")]),
435
+ self.schema([self.column("other")])):
436
+ with self.subTest(after=after), tempfile.TemporaryDirectory() as root:
437
+ self.baseline(root)
438
+ result, calls = self.run_removal(root, after=after)
439
+ self.assertFalse(result["status"])
440
+ self.assertTrue(result["data"]["platformMutationApplied"])
441
+ self.assertFalse(result["verification"]["verified"])
442
+ self.assertEqual(result["data"]["reportAuthoringState"]["status"], "incomplete")
443
+ self.assertEqual(sum(call[2] == "POST" for call in calls), 1)
444
+
445
+ def test_canonical_missing_sections_are_not_empty_state_evidence(self):
446
+ before = self.schema([self.column("drop"), self.column("keep")])
447
+ expected = report_ops.plan_report_column_removal(before, "drop")
448
+ for missing in ("reportColumns", "reportFilters", "reportSorts", "reportPlugins"):
449
+ with self.subTest(missing=missing):
450
+ canonical = self.schema([self.column("keep")])
451
+ canonical.pop(missing)
452
+ with mock.patch.object(report_ops, "_get_schema", return_value={
453
+ "data": {"formNumber": "kdtest_rpt", "reportSchema": canonical}}):
454
+ after = report_ops.get_report_schema({}, None, "FID")
455
+ self.assertIn(missing, after["unverifiableSections"])
456
+ result = report_ops.report_readback_verification("columnsRemove", expected, after)
457
+ if missing == "reportColumns":
458
+ self.assertFalse(result["verified"])
459
+ with self.assertRaisesRegex(ValueError, "REPORT_READBACK_UNAVAILABLE"):
460
+ report_ops.plan_report_column_removal(after, "drop")
461
+ else:
462
+ self.assertTrue(result["verified"])
463
+ self.assertIn(missing, result["unverifiedProperties"])
464
+ with tempfile.TemporaryDirectory() as root:
465
+ result, calls = self.run_removal(root, after={"readBackAvailable": True})
466
+ self.assertFalse(result["status"])
467
+ self.assertTrue(result["data"]["platformMutationApplied"])
468
+ self.assertEqual(sum(call[2] == "POST" for call in calls), 1)
469
+
470
+ def test_wrong_receipt_is_applied_evidence_not_verified_deletion(self):
471
+ with tempfile.TemporaryDirectory() as root:
472
+ result, calls = self.run_removal(root, self.receipt("other"))
473
+ self.assertFalse(result["status"])
474
+ self.assertEqual(result["errorCode"], "REPORT_COLUMN_DELETE_RECEIPT_INVALID")
475
+ self.assertTrue(result["data"]["platformMutationApplied"])
476
+ self.assertEqual(sum(call[2] == "POST" for call in calls), 1)
477
+
478
+ def test_nested_leaf_preserves_parent_sibling_order_and_other_properties(self):
479
+ before = self.schema([{"kind": "group", "fieldKey": "group", "caption": "Group", "children": [
480
+ {**self.column("drop"), "index": 0}, {**self.column("keep"), "index": 1, "width": "120"}]}])
481
+ after = copy.deepcopy(before)
482
+ after["reportColumns"][0]["children"].pop(0)
483
+ after["reportColumns"][0]["children"][0]["index"] = 0
484
+ expected = report_ops.plan_report_column_removal(before, "drop")
485
+ self.assertTrue(report_ops.report_readback_verification("columnsRemove", expected, after)["verified"])
486
+ after["reportColumns"][0]["children"][0]["width"] = "80"
487
+ self.assertFalse(report_ops.report_readback_verification("columnsRemove", expected, after)["verified"])
488
+
489
+ def test_sort_cleanup_is_verified_when_observable_and_not_invented_when_missing(self):
490
+ before = self.schema([self.column("drop"), self.column("keep")])
491
+ before["reportSorts"].append({"fieldKey": "drop", "direction": "desc"})
492
+ expected = report_ops.plan_report_column_removal(before, "drop")
493
+ after = self.schema([self.column("keep")])
494
+ self.assertTrue(report_ops.report_readback_verification("columnsRemove", expected, after)["verified"])
495
+ after["reportSorts"] = before["reportSorts"]
496
+ self.assertFalse(report_ops.report_readback_verification("columnsRemove", expected, after)["verified"])
497
+ after["unverifiableSections"] = ["reportSorts"]
498
+ result = report_ops.report_readback_verification("columnsRemove", expected, after)
499
+ self.assertTrue(result["verified"])
500
+ self.assertIn("reportSorts", result["unverifiedProperties"])
501
+
502
+ def test_absent_group_ambiguous_and_malformed_targets_fail_before_write(self):
503
+ schemas = [self.schema([self.column("keep")]), self.schema([{"kind": "group", "fieldKey": "drop", "children": []}]),
504
+ self.schema([self.column("drop"), self.column("drop")]), self.schema([None]), {"readBackAvailable": False},
505
+ {**self.schema([self.column("drop")]), "formNumber": "another_report"}]
506
+ for schema in schemas:
507
+ with self.subTest(schema=schema), mock.patch.object(report_ops, "_api_call") as api:
508
+ with self.assertRaises(ValueError):
509
+ report_ops.plan_report_column_removal(schema, "drop", form_number="kdtest_rpt")
510
+ api.assert_not_called()
511
+
512
+ def test_authoring_withdraws_only_deleted_column_sort_and_properties(self):
513
+ with tempfile.TemporaryDirectory() as root:
514
+ artifact, before = self.baseline(root)
515
+ after = self.schema([self.column("keep")])
516
+ result = report_ops.sync_report_authoring_state(root, "kdtest_rpt", "FID", "columnsRemove",
517
+ {"fieldKey": "drop", "before": before}, before, after, {"verified": True})
518
+ compiled = json.loads(Path(result["contractPath"]).read_text(encoding="utf-8"))
519
+ current = json.loads(Path(result["artifactPath"]).read_text(encoding="utf-8"))
520
+ self.assertEqual(compiled["dataSetColumns"], ["keep"])
521
+ self.assertEqual(compiled["defaultSorts"], [{"fieldKey": "keep", "dateIndex": "keep", "direction": "asc"}])
522
+ self.assertFalse(compiled["defaultSortPolicy"]["runtimeVerified"])
523
+ self.assertEqual(current["reportSorts"], [{"fieldKey": "keep", "direction": "asc"}])
524
+ self.assertEqual(current["reportProperties"]["columns"], {"keep": {"Width": "100"}})
525
+ self.assertEqual(current["reportColumns"][0]["presentation"], artifact["reportColumns"][1]["presentation"])
526
+ self.assertEqual(current["reportFilters"], artifact["reportFilters"])
527
+ self.assertEqual(current["reportPlugins"], artifact["reportPlugins"])
528
+
529
+ def test_historical_precision_dependency_still_blocks_deletion(self):
530
+ with tempfile.TemporaryDirectory() as root:
531
+ artifact, before = self.baseline(root)
532
+ artifact["reportColumns"][0]["fieldType"] = "bigint"
533
+ artifact["reportColumns"][1]["fieldType"] = "decimal"
534
+ artifact["reportColumns"][1]["presentation"] = {"kind": "amount", "currencyField": "drop"}
535
+ with self.assertRaisesRegex(ValueError, "REPORT_PRECISION_REFERENCE_UNVERIFIED"):
536
+ report_ops.normalize_report_columns_input(artifact["reportColumns"])
537
+ # 模拟门禁上线前已存在的历史夹具;不调用当前 writer 将危险输入标成新成功状态。
538
+ path = report_ops.report_authoring_path(root, "kdtest_rpt")
539
+ historical = json.loads(path.read_text(encoding="utf-8"))
540
+ historical["artifact"] = artifact
541
+ path.write_text(json.dumps(historical), encoding="utf-8")
542
+ original = path.read_bytes()
543
+ with mock.patch.object(report_ops, "_api_call") as http:
544
+ with self.assertRaisesRegex(ValueError, "REPORT_COLUMN_IN_USE"):
545
+ report_ops.plan_report_column_removal(before, "drop", root, "kdtest_rpt", "FID")
546
+ http.assert_not_called()
547
+ self.assertEqual(path.read_bytes(), original)
548
+
549
+ def test_empty_remaining_tree_or_group_cannot_become_current_artifact(self):
550
+ for columns in ([self.column("drop")], [{"kind": "group", "fieldKey": "g", "caption": "Group", "children": [self.column("drop")]}]):
551
+ with self.subTest(columns=columns), tempfile.TemporaryDirectory() as root:
552
+ before = self.schema(columns)
553
+ report_ops.write_report_authoring_state(root, "kdtest_rpt", "FID", {"reportColumns": columns}, before)
554
+ after = {**before, "reportColumns": report_ops._without_report_column(columns, "drop")}
555
+ result = report_ops.sync_report_authoring_state(root, "kdtest_rpt", "FID", "columnsRemove",
556
+ {"fieldKey": "drop"}, before, after, {"verified": True})
557
+ self.assertEqual(result["status"], "incomplete")
558
+
559
+
285
560
  if __name__ == "__main__":
286
561
  unittest.main(verbosity=2)
@@ -66,7 +66,7 @@ def _add_field(target, quick=None):
66
66
  "fieldType": "TextField"},
67
67
  }
68
68
  result = cmd_formactions.do_add_field(
69
- {}, None, "kdtest_ljbb_rpt", "Creator Email", "creator_email",
69
+ {}, None, "kdtest_ljbb_rpt", "Creator Email", "creator_email", "TextField",
70
70
  app_root="/tmp/kdtest_guaner",
71
71
  )
72
72
  return result, quickfield.call_count