@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
@@ -877,7 +877,7 @@ class _PostBuildWriteError(ValueError):
877
877
 
878
878
 
879
879
  def _attach_post_build_phase_failure(summary, payload, *, phase, app_root,
880
- artifact_path, pending_write=None):
880
+ artifact_path, pending_write=None, verification=None):
881
881
  """A successful root save survives a later write or verification failure."""
882
882
  targets = [
883
883
  {"formId": row["formId"],
@@ -910,6 +910,15 @@ def _attach_post_build_phase_failure(summary, payload, *, phase, app_root,
910
910
  "allowedNextActions": ["getFormSchema", "getProperties", "listElements"],
911
911
  "modelInstruction": "Preserve the saved form and the original artifact. Inspect the exact retained target before a narrow existing-form repair; never rerun emit, runBatch or buildMeta for this artifact. Reconcile every failed or unknown post-write before retrying. The batch results use ok and phase, not success.",
912
912
  })
913
+ issues = (verification or {}).get("issues") or []
914
+ counts = (verification or {}).get("summary") or {}
915
+ if phase == "verify" and issues and counts.get("failed") == len(issues) and not any(counts.get(key) for key in ("errors", "warnings", "skipped")) and all(item.get("status") == "fail" and item.get("type") in
916
+ {"property_equals", "combo_options_equal", "unit_binding_equals"} for item in issues):
917
+ summary.update({"retryAdvice": "repair_observed_configuration", "reconciliation": [],
918
+ "allowedNextActions": ["existing_form_writer", "report_blocker"],
919
+ "modelInstruction": "Native readback already isolated the configuration differences. Reuse verification.issues (expected and actual) and verifiedTargets; repair only those named differences through their existing-form writers. Do not rediscover the form, export DYM or replay successful writes. If the requested stored values are unsupported, report that exact boundary instead of substituting values."})
920
+ summary["repairPlans"][0].update({"observedIssues": issues,
921
+ "precondition": "Use the existing independent readback; successful configuration is retained. Re-read only if another write changes the target or a narrow repair remains unknown."})
913
922
  return summary
914
923
 
915
924
 
@@ -1034,8 +1043,8 @@ def _apply_form_composition_failure_contract(
1034
1043
  def _compact_verified_targets(report):
1035
1044
  """Expose successful read-back assertions without forcing a second query.
1036
1045
 
1037
- The full verifier report stays internal. Callers only need the assertion
1038
- type and exact target to prove that authored entities and fields landed.
1046
+ Include the checked property and values so a successful receipt can be reused
1047
+ without guessing what property_equals meant or exporting another snapshot.
1039
1048
  """
1040
1049
  evidence = []
1041
1050
  for result in getattr(report, "results", []) or []:
@@ -1045,7 +1054,15 @@ def _compact_verified_targets(report):
1045
1054
  assertion_type = str(getattr(spec, "type", "") or "").strip()
1046
1055
  target = str(getattr(spec, "target", "") or "").strip()
1047
1056
  if assertion_type and target:
1048
- evidence.append({"type": assertion_type, "target": target})
1057
+ item = {"type": assertion_type, "target": target}
1058
+ prop = (getattr(spec, "metadata", None) or {}).get("property")
1059
+ if prop:
1060
+ item["property"] = prop
1061
+ if hasattr(spec, "expected"):
1062
+ item["expected"] = spec.expected
1063
+ if hasattr(result, "actual"):
1064
+ item["actual"] = result.actual
1065
+ evidence.append(item)
1049
1066
  return evidence
1050
1067
 
1051
1068
 
@@ -1358,32 +1375,33 @@ def _resolve_template_number(args_template, artifact_path, manifest):
1358
1375
  submit BD missing template number as a bill, causing BillNoField error and masking upstream emit issues.
1359
1376
  Caller must check return value and exit with error when None.
1360
1377
  """
1361
- if args_template is not None:
1362
- return str(args_template).strip()
1363
- tn = _read_artifact_template(artifact_path)
1364
- if tn is not None:
1365
- return tn
1366
- tn = _manifest_template_for(manifest, artifact_path)
1367
- if tn is not None:
1368
- return tn
1369
- return None
1378
+ tn = args_template if args_template is not None else _read_artifact_template(artifact_path)
1379
+ if tn is None:
1380
+ tn = _manifest_template_for(manifest, artifact_path)
1381
+ if tn is None:
1382
+ return None
1383
+ value = str(tn).strip()
1384
+ return "blankPage" if value.casefold() in {"blank", "blankpage"} else value
1370
1385
 
1371
1386
 
1372
- def _artifact_supports_blank_template(artifact):
1373
- """Return true only for a dynamic-form root that intentionally uses blank."""
1387
+ def _artifact_supports_blank_template(artifact, template=""):
1388
+ """Permit an explicit blank bill while rejecting an accidentally missing template."""
1374
1389
  if not isinstance(artifact, dict):
1375
1390
  return False
1376
1391
  roots = [
1377
1392
  entity for entity in artifact.get("entities") or []
1378
1393
  if isinstance(entity, dict) and not entity.get("parentEntityId")
1379
1394
  ]
1380
- return len(roots) == 1 and str(roots[0].get("type") or "").strip() == "MainEntity"
1395
+ if len(roots) != 1:
1396
+ return False
1397
+ root_type = str(roots[0].get("type") or "").strip()
1398
+ return root_type == "MainEntity" or (root_type == "BillEntity" and template == "blankPage")
1381
1399
 
1382
1400
 
1383
- def _artifact_file_supports_blank_template(artifact_path):
1401
+ def _artifact_file_supports_blank_template(artifact_path, template=""):
1384
1402
  try:
1385
1403
  with open(artifact_path, "r", encoding="utf-8-sig") as f:
1386
- return _artifact_supports_blank_template(json.load(f))
1404
+ return _artifact_supports_blank_template(json.load(f), template)
1387
1405
  except (OSError, json.JSONDecodeError):
1388
1406
  return False
1389
1407
 
@@ -1974,32 +1992,128 @@ def _resolve_owned_report_target(cfg, token, args, command):
1974
1992
  return row
1975
1993
 
1976
1994
 
1977
- def _attach_report_readback(cfg, token, form_id, section, expected, result_data):
1978
- schema = do_get_report_schema(cfg, token, form_id=form_id)
1979
- if section == "filters" and any(item.get("source") for item in expected):
1980
- from _core.report_ops import report_filter_source_expectations
1981
- schema["filterSourceExpectations"] = report_filter_source_expectations(cfg, token, expected)
1995
+ def _remove_owned_report_column(cfg, token, args):
1996
+ """CLI 与 serve 共用同一归属、预检和回读闭环。"""
1997
+ field_key = normalize_report_column_removal_input({"fieldKey": args.fieldKey})["fieldKey"]
1998
+ if not getattr(args, "app_root", None) or not getattr(args, "formNumber", None):
1999
+ raise ValueError("REPORT_INPUT_INVALID: removeReportColumn requires appRoot and exact formNumber")
2000
+ if getattr(args, "dry_run", False):
2001
+ return do_remove_report_column(cfg, None, args.formId or "", field_key,
2002
+ dry_run=True, form_number=args.formNumber)
2003
+ target = _resolve_owned_report_target(cfg, token, args, "removeReportColumn")
2004
+ before = do_get_report_schema(cfg, token, form_id=target["formId"])
2005
+ expected = plan_report_column_removal(before, field_key, args.app_root, args.formNumber, target["formId"])
2006
+ result = do_remove_report_column(cfg, token, target["formId"], field_key, form_number=args.formNumber)
2007
+ if not _is_api_success(result):
2008
+ return result
2009
+ return _attach_report_readback(cfg, token, target["formId"], "columnsRemove", expected,
2010
+ result["data"], app_root=args.app_root, schema_before=before)
2011
+
2012
+
2013
+ def _attach_report_readback(cfg, token, form_id, section, expected, result_data,
2014
+ filter_editors=None, app_root=None, schema_before=None):
2015
+ try:
2016
+ schema = do_get_report_schema(cfg, token, form_id=form_id)
2017
+ if not isinstance(schema, dict):
2018
+ raise TypeError("Report read-back must be an object")
2019
+ except Exception as exc:
2020
+ # 写入已成功,回读失败不能诱导宿主重放写请求。
2021
+ schema = {"readBackAvailable": False, "readBackSource": "unavailable",
2022
+ "readBackError": type(exc).__name__}
2023
+
2024
+ def settle(result):
2025
+ result["data"]["platformMutationApplied"] = True
2026
+ if schema.get("readBackError"):
2027
+ result["verification"]["errorType"] = schema["readBackError"]
2028
+ if app_root:
2029
+ try:
2030
+ result["data"]["reportAuthoringState"] = sync_report_authoring_state(
2031
+ app_root, result_data["formNumber"], form_id, section, expected,
2032
+ schema_before or {}, schema, result["verification"],
2033
+ result["data"].get("filterEditors"))
2034
+ except (OSError, ValueError, KeyError, TypeError) as exc:
2035
+ result["data"]["reportAuthoringState"] = {"status": "failed", "errorType": type(exc).__name__,
2036
+ "platformMutationApplied": True, "reason": "Repair local state from current metadata; do not replay the platform write"}
2037
+ return result
2038
+ if section == "filters" and filter_editors:
2039
+ # What the write's receipt showed about each typed editor; the read-back settles the rest.
2040
+ schema["filterEditorOutcome"] = filter_editors
1982
2041
  if not schema.get("readBackAvailable"):
1983
2042
  # No canonical read-back on this deployment; the write already returned platform
1984
- # success, so report succeeded-but-unverified, not a mismatch.
1985
- return {
1986
- "status": True,
1987
- "errorCode": "0",
1988
- "message": None,
1989
- "data": result_data,
2043
+ # success, so report succeeded-but-unverified, not a mismatch — unless the platform's own
2044
+ # receipt already showed a filter editor other than the requested or legacy one.
2045
+ receipt_mismatch = (section == "columnsRemove" or
2046
+ bool(filter_editors) and filter_editors.get("typedFilterSupport") == "mismatch")
2047
+ data = dict(result_data)
2048
+ if filter_editors:
2049
+ data["filterEditors"] = filter_editors
2050
+ return settle({
2051
+ "status": not receipt_mismatch,
2052
+ "errorCode": "REPORT_READBACK_MISMATCH" if receipt_mismatch else "0",
2053
+ "message": ("Deletion receipt succeeded but independent read-back is unavailable; do not replay"
2054
+ if section == "columnsRemove" else "平台回执显示过滤控件类型与请求不一致" if receipt_mismatch else None),
2055
+ "data": data,
1990
2056
  "verification": {"verified": False, "reason": "canonical read-back unavailable"},
1991
- }
1992
- verification = report_readback_verification(section, expected, schema)
2057
+ })
2058
+ try:
2059
+ verification = report_readback_verification(section, expected, schema)
2060
+ except (ValueError, KeyError, TypeError, AttributeError) as exc:
2061
+ verification = {"verified": False, "reason": "Invalid report read-back; reconcile before coding",
2062
+ "errorType": type(exc).__name__}
2063
+ data = dict(result_data)
2064
+ editors = verification.get("filterEditors") or filter_editors
2065
+ if section == "filters" and editors:
2066
+ data["filterEditors"] = editors
1993
2067
  result = {
1994
2068
  "status": bool(verification.get("verified")),
1995
2069
  "errorCode": "0" if verification.get("verified") else "REPORT_READBACK_MISMATCH",
1996
2070
  "message": None if verification.get("verified") else "报表写入回执成功,但独立回读与请求不一致",
1997
- "data": result_data,
2071
+ "data": data,
1998
2072
  "verification": verification,
1999
2073
  }
2074
+ return settle(result)
2075
+
2076
+
2077
+ _REPORT_STATE_WRITES = {"setReportColumns", "addReportColumns", "removeReportColumn", "setReportFilters", "setReportSorts",
2078
+ "registerReportPlugin", "setReportProperties", "setReportQueryDataSource", "setReportFilterFieldConfig",
2079
+ "setReportDefaultHiddenFields", "setReportRowBackColor", "registerReportPluginExt"}
2080
+
2081
+
2082
+ def _invalidate_unmerged_report_state(result, command, app_root, form_number, dry_run=False):
2083
+ """其他写入入口只失效旧需求,不从不完整回执补造插件契约。"""
2084
+ if dry_run or command not in _REPORT_STATE_WRITES or not app_root or not form_number:
2085
+ return result
2086
+ data = result.get("data") if isinstance(result, dict) else None
2087
+ if isinstance(data, dict) and data.get("reportAuthoringState"):
2088
+ return result
2089
+ applied = _serve_result_success(result) or (isinstance(data, dict) and data.get("platformMutationApplied") is True)
2090
+ possible = isinstance(data, dict) and data.get("platformMutationPossible") is True
2091
+ if not applied and not possible:
2092
+ return result
2093
+ evidence = {"platformMutationApplied": True} if applied else {"platformMutationPossible": True}
2094
+ try:
2095
+ state = invalidate_report_authoring_state(app_root, form_number,
2096
+ f"{command} crossed the report write boundary; reconcile the complete artifact before coding; do not replay the write",
2097
+ mutation_applied=True if applied else None)
2098
+ except (OSError, ValueError) as exc:
2099
+ state = {"status": "failed", "errorType": type(exc).__name__, **evidence,
2100
+ "reason": "Repair local state; do not replay the platform write"}
2101
+ if isinstance(result, dict):
2102
+ if not isinstance(data, dict):
2103
+ result["data"] = data = {}
2104
+ data["reportAuthoringState"] = state
2000
2105
  return result
2001
2106
 
2002
2107
 
2108
+ def _report_mutation_failure(exc):
2109
+ """只接收报表写入边界异常,输入或身份预检失败不会触发失效。"""
2110
+ code, message = _exception_error_code(exc)
2111
+ return {"status": False, "errorCode": code, "message": message, "writeAttempted": True,
2112
+ "data": {**({"platformMutationApplied": True} if exc.mutation_applied is True else
2113
+ {"platformMutationPossible": True}),
2114
+ "runtimeVerified": False, "retryAdvice": "Re-read and reconcile; do not replay the write"}}
2115
+
2116
+
2003
2117
  def _json_diff_paths(before, after, path=""):
2004
2118
  """Return exact JSON Pointer leaf/subtree paths changed by a replacement."""
2005
2119
  if type(before) is not type(after):
@@ -2929,12 +3043,12 @@ from _core.formatters import ( # noqa: F401
2929
3043
  _print_forms, _print_schema, _print_templates,
2930
3044
  _print_form_layout_tree, _print_panels,
2931
3045
  _print_find_results, _print_biz_hierarchy,
2932
- pjson, _transform_build_meta_result, save_run_batch_result,
3046
+ pjson, _transform_build_meta_result, save_run_batch_result, result_summary,
2933
3047
  set_output_file, set_stdout_mode, set_current_command, _is_en,
2934
3048
  )
2935
3049
 
2936
3050
 
2937
- from execution_binding import execution_binding, validate_execution_binding
3051
+ from execution_binding import execution_binding, validate_execution_binding, bound_entrypoint
2938
3052
  from _core.execution_errors import attach_execution_error
2939
3053
 
2940
3054
  _FORMATTER_PJSON = pjson
@@ -3034,6 +3148,10 @@ def _is_machine_failure(result):
3034
3148
  def pjson(obj, force_stdout=False):
3035
3149
  """Emit one structured result and make semantic failure affect the exit code."""
3036
3150
  global _CLI_RESULT_EMITTED
3151
+ if _CLI_DELIVERY_ARGS is not None:
3152
+ obj = _invalidate_unmerged_report_state(obj, getattr(_CLI_DELIVERY_ARGS, "cmd", None),
3153
+ getattr(_CLI_DELIVERY_ARGS, "app_root", None), getattr(_CLI_DELIVERY_ARGS, "formNumber", None),
3154
+ getattr(_CLI_DELIVERY_ARGS, "dry_run", False))
3037
3155
  obj = attach_execution_error(_attach_verified_form_delivery(obj, _CLI_DELIVERY_ARGS))
3038
3156
  _FORMATTER_PJSON(obj, force_stdout=force_stdout)
3039
3157
  _CLI_RESULT_EMITTED = True
@@ -3072,20 +3190,21 @@ def _exception_error_code(exc):
3072
3190
  return "CLI_EXECUTION_ERROR", text or exc.__class__.__name__
3073
3191
 
3074
3192
 
3075
- def _emit_machine_error(error_code, message, recovery=None):
3193
+ def _emit_machine_error(error_code, message, recovery=None, data=None):
3076
3194
  global _CLI_RESULT_EMITTED
3077
3195
  _FORMATTER_PJSON(
3078
3196
  {
3079
3197
  "status": False,
3080
3198
  "errorCode": str(error_code or "CLI_EXECUTION_ERROR"),
3081
3199
  "message": str(message or "Command failed"),
3082
- "data": None,
3200
+ "data": data,
3083
3201
  "errorCategory": attach_execution_error({"status": False, "errorCode": error_code})["errorCategory"],
3084
3202
  "capabilityLimitConfirmed": error_code == "FULL_METADATA_API_UNAVAILABLE",
3085
3203
  "executionBinding": execution_binding(),
3086
3204
  **({"writeAttempted": False} if error_code in {
3087
3205
  "SKILL_SOURCE_MISMATCH", "SKILL_REVISION_MISMATCH", "ENVIRONMENT_BINDING_MISMATCH",
3088
3206
  "CONDITIONAL_CARRY_SOURCE_PROPERTY_UNAVAILABLE",
3207
+ "REPORT_SORTS_API_UNSAFE",
3089
3208
  } else {}),
3090
3209
  **({"recovery": recovery} if recovery else {}),
3091
3210
  },
@@ -3100,8 +3219,12 @@ from _core.reset import ( # noqa: F401
3100
3219
  )
3101
3220
 
3102
3221
  from _core.report_ops import ( # noqa: F401
3222
+ ReportMutationError,
3103
3223
  set_report_columns as do_set_report_columns,
3104
3224
  add_report_columns as do_add_report_columns,
3225
+ remove_report_column as do_remove_report_column,
3226
+ normalize_report_column_removal_input,
3227
+ plan_report_column_removal,
3105
3228
  plan_report_column_append,
3106
3229
  set_report_filters as do_set_report_filters,
3107
3230
  set_report_sorts as do_set_report_sorts,
@@ -3122,6 +3245,7 @@ from _core.report_ops import ( # noqa: F401
3122
3245
  normalize_report_properties_artifact,
3123
3246
  report_properties_artifact_summary,
3124
3247
  report_readback_verification,
3248
+ resolve_filter_editors,
3125
3249
  validate_report_references,
3126
3250
  assert_report_filter_keys_free,
3127
3251
  collect_report_column_keys,
@@ -3129,7 +3253,12 @@ from _core.report_ops import ( # noqa: F401
3129
3253
  attach_dym_evidence,
3130
3254
  filters_need_dym_evidence,
3131
3255
  compile_report_plugin_contract,
3256
+ compile_report_default_sorts,
3257
+ REPORT_SORTS_API_UNSAFE,
3132
3258
  write_report_plugin_contract,
3259
+ write_report_authoring_state,
3260
+ sync_report_authoring_state,
3261
+ invalidate_report_authoring_state,
3133
3262
  plan_report_recreate,
3134
3263
  write_report_recreate_backup,
3135
3264
  )
@@ -3325,6 +3454,15 @@ def _verify_listcolumn_landed(cfg, token, form_id, field_key, result):
3325
3454
  # ─── Serve mode ─────────────────────────────────────────────────────────────────
3326
3455
 
3327
3456
  def _dispatch_serve(cfg, token, cmd, cmd_args):
3457
+ try:
3458
+ result = _dispatch_serve_command(cfg, token, cmd, cmd_args)
3459
+ except ReportMutationError as exc:
3460
+ result = _report_mutation_failure(exc)
3461
+ return _invalidate_unmerged_report_state(result, cmd, cmd_args.get("appRoot"),
3462
+ cmd_args.get("formNumber"), cmd_args.get("dryRun", False))
3463
+
3464
+
3465
+ def _dispatch_serve_command(cfg, token, cmd, cmd_args):
3328
3466
  """Dispatch commands in serve mode to corresponding business functions.
3329
3467
 
3330
3468
  Args:
@@ -4342,7 +4480,7 @@ def _dispatch_serve(cfg, token, cmd, cmd_args):
4342
4480
  form_name=cmd_args.get("formName") or cmd_args.get("form_name"),
4343
4481
  field_name=cmd_args.get("fieldName") or cmd_args.get("field_name"),
4344
4482
  biz_word=cmd_args.get("bizWord") or cmd_args.get("biz_word"),
4345
- field_type=cmd_args.get("fieldType") or cmd_args.get("field_type") or "TextField",
4483
+ field_type=cmd_args.get("fieldType") or cmd_args.get("field_type"),
4346
4484
  app_root=cmd_args.get("appRoot") or cmd_args.get("app_root"),
4347
4485
  project_root=cmd_args.get("projectRoot") or cmd_args.get("project_root"),
4348
4486
  app_name=cmd_args.get("appName") or cmd_args.get("app_name"),
@@ -4395,7 +4533,7 @@ def _dispatch_serve(cfg, token, cmd, cmd_args):
4395
4533
  form_name=cmd_args.get("formName") or cmd_args.get("form_name"),
4396
4534
  field_name=cmd_args.get("fieldName") or cmd_args.get("field_name"),
4397
4535
  biz_word=cmd_args.get("bizWord") or cmd_args.get("biz_word"),
4398
- field_type=cmd_args.get("fieldType") or cmd_args.get("field_type") or "TextField",
4536
+ field_type=cmd_args.get("fieldType") or cmd_args.get("field_type"),
4399
4537
  app_root=cmd_args.get("appRoot") or cmd_args.get("app_root"),
4400
4538
  project_root=cmd_args.get("projectRoot") or cmd_args.get("project_root"),
4401
4539
  app_name=cmd_args.get("appName") or cmd_args.get("app_name"),
@@ -4534,6 +4672,15 @@ def _dispatch_serve(cfg, token, cmd, cmd_args):
4534
4672
  form_number=cmd_args.get("formNumber"),
4535
4673
  )
4536
4674
 
4675
+ elif cmd == "removeReportColumn":
4676
+ from types import SimpleNamespace
4677
+ if not isinstance(cmd_args.get("dryRun", False), bool):
4678
+ raise ValueError("REPORT_INPUT_INVALID: dryRun must be boolean")
4679
+ return _remove_owned_report_column(cfg, token, SimpleNamespace(
4680
+ app_root=cmd_args.get("appRoot"), formNumber=cmd_args.get("formNumber"),
4681
+ formId=cmd_args.get("formId"), fieldKey=cmd_args.get("fieldKey"),
4682
+ dry_run=cmd_args.get("dryRun", False)))
4683
+
4537
4684
  elif cmd == "setReportFilters":
4538
4685
  filters = normalize_report_filters_input(_load_json_arg(cmd_args.get("filters", [])))
4539
4686
  return do_set_report_filters(
@@ -4545,14 +4692,8 @@ def _dispatch_serve(cfg, token, cmd, cmd_args):
4545
4692
  )
4546
4693
 
4547
4694
  elif cmd == "setReportSorts":
4548
- sorts = normalize_report_sorts_input(_load_json_arg(cmd_args.get("sorts", [])))
4549
- return do_set_report_sorts(
4550
- cfg, token,
4551
- form_id=cmd_args.get("formId"),
4552
- sorts=sorts,
4553
- dry_run=cmd_args.get("dryRun", False),
4554
- form_number=cmd_args.get("formNumber"),
4555
- )
4695
+ normalize_report_sorts_input(_load_json_arg(cmd_args.get("sorts", [])))
4696
+ raise RuntimeError(REPORT_SORTS_API_UNSAFE)
4556
4697
 
4557
4698
  elif cmd == "registerReportPlugin":
4558
4699
  plugin = normalize_report_plugin_input(_load_json_arg(cmd_args.get("plugin", {})))
@@ -4979,8 +5120,7 @@ def _serve_loop(cfg):
4979
5120
 
4980
5121
 
4981
5122
  # ─── Report Pipeline ─────────────────────────────────────────────────────────
4982
- # Orchestrates the full report creation flow: createPage → setReportColumns →
4983
- # setReportFilters → setReportSorts → registerReportPlugin ×N
5123
+ # 创建流水线保留七步依赖顺序;默认排序仅进入 Java 契约,不写入 InitField。
4984
5124
  #
4985
5125
  # Report data sources are hidden in the Cosmic designer and are no longer written here.
4986
5126
  # A report gets its rows from the data-fetch plugin's query(ReportQueryParam, Object).
@@ -5042,9 +5182,13 @@ def _settle_report_property_design_state(cfg, token, result, form_number, app_ro
5042
5182
  print(f"[{command}] could not sync design-state evidence: {exc}",
5043
5183
  file=sys.stderr, flush=True)
5044
5184
  return
5045
- attach_report_property_design_state(
5046
- result, evidence_path,
5047
- expectation["hostType"], expectation["expected"])
5185
+ try:
5186
+ attach_report_property_design_state(
5187
+ result, evidence_path,
5188
+ expectation["hostType"], expectation["expected"])
5189
+ except Exception as exc:
5190
+ # 此处只有写成功后才会进入,设计证据失败不能丢失该边界。
5191
+ raise ReportMutationError(str(exc), mutation_applied=True) from exc
5048
5192
 
5049
5193
 
5050
5194
  def _execute_report_pipeline(cfg, token, artifact, biz_app_id, biz_unit_id, app_root=None):
@@ -5055,7 +5199,7 @@ def _execute_report_pipeline(cfg, token, artifact, biz_app_id, biz_unit_id, app_
5055
5199
  2. setReportQueryDataSource (if a query data source is declared)
5056
5200
  3. setReportColumns
5057
5201
  4. setReportFilters (if any)
5058
- 5. setReportSorts (if any)
5202
+ 5. preserve default sorts for the Java query contract (no metadata write)
5059
5203
  6. registerReportPlugin for each plugin (if any)
5060
5204
  7. apply reportProperties (if any), LAST so it settles ShowFilterFields after step 4
5061
5205
 
@@ -5104,6 +5248,10 @@ def _execute_report_pipeline(cfg, token, artifact, biz_app_id, biz_unit_id, app_
5104
5248
  normalize_report_plugin_input(item)
5105
5249
  for item in (artifact.get("reportPlugins") or [])
5106
5250
  ]
5251
+ # 创建前确认排序引用和承接插件;不能创建后才发现需求没有实现通道。
5252
+ default_sorts = compile_report_default_sorts({
5253
+ "reportSorts": sorts, "reportColumns": columns, "reportPlugins": plugins,
5254
+ })
5107
5255
  if artifact.get("reportBehavior"):
5108
5256
  raise ValueError(
5109
5257
  "REPORT_INPUT_INVALID: reportBehavior is not a published typed CLI capability"
@@ -5182,20 +5330,21 @@ def _execute_report_pipeline(cfg, token, artifact, biz_app_id, biz_unit_id, app_
5182
5330
  print("[report-pipeline] Step 3/7: no reportColumns (skipped)", file=sys.stderr, flush=True)
5183
5331
 
5184
5332
  # Step 4: setReportFilters
5333
+ # The outcome records what the platform did with each typed filter editor: a platform without
5334
+ # the typed report filter patch renders the legacy control, which is reported, not failed.
5335
+ filter_editor_outcome = {}
5185
5336
  if filters:
5186
5337
  print(f"[report-pipeline] Step 4/7: setReportFilters ({len(filters)} filters)",
5187
5338
  file=sys.stderr, flush=True)
5188
- do_set_report_filters(cfg, token, form_id=form_id, filters=filters)
5339
+ do_set_report_filters(cfg, token, form_id=form_id, filters=filters,
5340
+ outcome=filter_editor_outcome)
5189
5341
  else:
5190
5342
  print("[report-pipeline] Step 4/7: no reportFilters (skipped)", file=sys.stderr, flush=True)
5191
5343
 
5192
- # Step 5: setReportSorts
5344
+ # 第五步仅保留插件排序契约,绝不调用会污染 InitField 的 ReportSorts。
5193
5345
  if sorts:
5194
- print(f"[report-pipeline] Step 5/7: setReportSorts ({len(sorts)} sort rules)",
5195
- file=sys.stderr, flush=True)
5196
- validate_report_references(
5197
- "sorts", sorts, do_get_report_schema(cfg, token, form_id=form_id))
5198
- do_set_report_sorts(cfg, token, form_id=form_id, sorts=sorts)
5346
+ print(f"[report-pipeline] Step 5/7: default sorts -> Java query contract "
5347
+ f"({len(default_sorts)} rules; runtime unverified)", file=sys.stderr, flush=True)
5199
5348
  else:
5200
5349
  print("[report-pipeline] Step 5/7: no reportSorts (skipped)", file=sys.stderr, flush=True)
5201
5350
 
@@ -5210,9 +5359,11 @@ def _execute_report_pipeline(cfg, token, artifact, biz_app_id, biz_unit_id, app_
5210
5359
  print("[report-pipeline] Step 6/7: no reportPlugins (skipped)", file=sys.stderr, flush=True)
5211
5360
 
5212
5361
  # Step 7: reportProperties
5213
- # Last on purpose. setReportFilters writes ShowFilterFields as a comma-joined string the
5214
- # runtime cannot parse, so applying the property section afterwards is what leaves a
5215
- # freshly created report with the JSON-array form. Every write here verifies itself.
5362
+ # Last on purpose. A platform without the typed report filter patch has setReportFilters
5363
+ # write ShowFilterFields as a comma-joined string the runtime cannot parse, and a patched one
5364
+ # resets FilterMustInput and the initial filter fields from the filters, so applying the
5365
+ # property section afterwards is what leaves a freshly created report with what it declared.
5366
+ # Every write here verifies itself.
5216
5367
  property_results = []
5217
5368
  if property_plan:
5218
5369
  _prop_number = str(
@@ -5258,15 +5409,10 @@ def _execute_report_pipeline(cfg, token, artifact, biz_app_id, biz_unit_id, app_
5258
5409
  file=sys.stderr, flush=True)
5259
5410
 
5260
5411
  final_schema = do_get_report_schema(cfg, token, form_id=form_id)
5261
- # Sorts and plugins are absent from formLayout, so on a deployment without a canonical
5262
- # projection they had no observable read-back at all. The platform's own design-state
5263
- # export does carry them: sync it now — after every write, before verification — so the
5264
- # file on disk cannot be older than what it is being used to prove.
5265
- # The same export also proves the filter properties the API projection cannot decide, so a
5266
- # report with a required filter needs it even when it declares no sorts and no plugin.
5412
+ # 设计态导出只补充插件和过滤属性证据,不证明排序。所有写入后同步,避免使用旧文件。
5267
5413
  _pending_dym_sections = [
5268
5414
  section for section in (final_schema.get("unverifiableSections") or [])
5269
- if (section == "reportSorts" and sorts) or (section == "reportPlugins" and plugins)
5415
+ if section == "reportPlugins" and plugins
5270
5416
  ]
5271
5417
  if app_root and (_pending_dym_sections or filters_need_dym_evidence(filters)):
5272
5418
  _dym_number = str(final_schema.get("formNumber") or form_number or "").strip()
@@ -5291,12 +5437,13 @@ def _execute_report_pipeline(cfg, token, artifact, biz_app_id, biz_unit_id, app_
5291
5437
  if columns:
5292
5438
  checks.append(report_readback_verification("columns", columns, final_schema))
5293
5439
  if filters:
5294
- if any(item.get("source") for item in filters):
5295
- from _core.report_ops import report_filter_source_expectations
5296
- final_schema["filterSourceExpectations"] = report_filter_source_expectations(cfg, token, filters)
5297
- checks.append(report_readback_verification("filters", filters, final_schema))
5298
- if sorts and "reportSorts" not in unverifiable:
5299
- checks.append(report_readback_verification("sorts", sorts, final_schema))
5440
+ if filter_editor_outcome:
5441
+ final_schema["filterEditorOutcome"] = filter_editor_outcome
5442
+ filter_check = report_readback_verification("filters", filters, final_schema)
5443
+ checks.append(filter_check)
5444
+ if filter_check.get("filterEditors"):
5445
+ filter_editor_outcome = filter_check["filterEditors"]
5446
+ # 默认排序由 query() 实现,不纳入元数据回读通过项。
5300
5447
  if "reportPlugins" not in unverifiable:
5301
5448
  for plugin in plugins:
5302
5449
  checks.append(report_readback_verification("plugin", plugin, final_schema))
@@ -5326,6 +5473,15 @@ def _execute_report_pipeline(cfg, token, artifact, biz_app_id, biz_unit_id, app_
5326
5473
  print("[report-pipeline] no report read-back available on this deployment; "
5327
5474
  "section writes succeeded but were not independently re-read",
5328
5475
  file=sys.stderr, flush=True)
5476
+ # The platform's own filter receipt is still evidence: an editor it reported as neither the
5477
+ # requested one nor the legacy one fails the run even without a read-back.
5478
+ if filter_editor_outcome.get("typedFilterSupport") == "mismatch":
5479
+ _editors, _receipt_mismatches = resolve_filter_editors(filter_editor_outcome, [])
5480
+ failed_checks = [{
5481
+ "section": "reportFilters", "source": "receipt", "verified": False,
5482
+ "mismatches": _receipt_mismatches, "filterEditors": _editors,
5483
+ }]
5484
+ checks = list(failed_checks)
5329
5485
 
5330
5486
  # createPage adds the ISV prefix, so reconcile form_number to the authoritative
5331
5487
  # value read back by formId (keeps archive/formNumbers/syncDym pointing at the real form).
@@ -5357,8 +5513,26 @@ def _execute_report_pipeline(cfg, token, artifact, biz_app_id, biz_unit_id, app_
5357
5513
  "readBackSource": read_back_source,
5358
5514
  "verification": {"verified": verification_verified, "checks": checks},
5359
5515
  }
5516
+ if default_sorts:
5517
+ summary["reportDefaultSorts"] = {
5518
+ "implementation": "reportlist.query", "requirements": default_sorts,
5519
+ "status": "pendingJavaImplementation", "metadataWritten": False, "runtimeVerified": False,
5520
+ }
5360
5521
  if query_data_source_result:
5361
5522
  summary["queryDataSource"] = query_data_source_result
5523
+ if filter_editor_outcome and any(
5524
+ row.get("requestedFieldType") for row in filter_editor_outcome.get("editors") or []):
5525
+ # Which editor each typed filter actually got. prepare_plugin_handoff forwards it to the
5526
+ # plugin contract, and a degraded filter is an unresolved boundary the reply must name.
5527
+ summary["reportFilterEditors"] = {**filter_editor_outcome, "formNumber": form_number}
5528
+ if filter_editor_outcome.get("typedFilterSupport") not in ("available", "notRequested"):
5529
+ print(
5530
+ "[report-pipeline] typed filter editors: "
5531
+ f"{filter_editor_outcome.get('typedFilterSupport')}; degraded "
5532
+ f"{filter_editor_outcome.get('degradedFilters') or []} render the legacy control until "
5533
+ "the platform's typed report filter patch is installed and the filters are re-applied",
5534
+ file=sys.stderr, flush=True,
5535
+ )
5362
5536
  if property_plan:
5363
5537
  # Each property write verified itself, so what the summary carries is which of them could
5364
5538
  # be proven and which resolve on no read path this pipeline owns.
@@ -5383,9 +5557,10 @@ def _execute_report_pipeline(cfg, token, artifact, biz_app_id, biz_unit_id, app_
5383
5557
  _plugin_contract = (
5384
5558
  None if (query_data_source and not plugins)
5385
5559
  else compile_report_plugin_contract(
5386
- {"reportColumns": columns, "reportFilters": filters, "reportPlugins": plugins,
5387
- "reportProperties": artifact.get("reportProperties") or {}},
5560
+ {"reportColumns": columns, "reportFilters": filters, "reportSorts": sorts,
5561
+ "reportPlugins": plugins, "reportProperties": artifact.get("reportProperties") or {}},
5388
5562
  form_number,
5563
+ filter_editors=filter_editor_outcome or None,
5389
5564
  )
5390
5565
  )
5391
5566
  if _plugin_contract:
@@ -5393,6 +5568,7 @@ def _execute_report_pipeline(cfg, token, artifact, biz_app_id, biz_unit_id, app_
5393
5568
  "dataSetColumnTypes": len(_plugin_contract["dataSetColumnTypes"]),
5394
5569
  "columnPresentation": len(_plugin_contract["columnPresentation"]),
5395
5570
  "filterEnforcement": len(_plugin_contract["filterEnforcement"]),
5571
+ "defaultSorts": len(_plugin_contract["defaultSorts"]),
5396
5572
  }
5397
5573
  if app_root:
5398
5574
  try:
@@ -5410,6 +5586,16 @@ def _execute_report_pipeline(cfg, token, artifact, biz_app_id, biz_unit_id, app_
5410
5586
  file=sys.stderr, flush=True,
5411
5587
  )
5412
5588
 
5589
+ if app_root and verification_verified and not failed_checks:
5590
+ try:
5591
+ summary["reportAuthoringState"] = write_report_authoring_state(
5592
+ app_root, form_number, form_id,
5593
+ {**artifact, "reportColumns": columns, "reportFilters": filters, "reportPlugins": plugins},
5594
+ final_schema, filter_editor_outcome or None)
5595
+ except (OSError, ValueError) as exc:
5596
+ summary["reportAuthoringState"] = {"status": "failed", "errorType": type(exc).__name__,
5597
+ "platformMutationApplied": True, "reason": "Repair local state; do not recreate the report"}
5598
+
5413
5599
  # Java plugin delivery consumes this result through
5414
5600
  # prepare_plugin_handoff.py --run-result, so persist it at a stable path instead of
5415
5601
  # relying on pjson's size threshold (a small report summary never reached disk).
@@ -5468,6 +5654,7 @@ _CATALOG_PAYLOAD_SCHEMA_REFS = {
5468
5654
  "createScript": "skills/cosmic-meta-api/contracts/schema/script-create.schema.json",
5469
5655
  "setReportColumns": "skills/cosmic-meta-api/contracts/schema/report-write.schema.json#/$defs/columns",
5470
5656
  "addReportColumns": "skills/cosmic-meta-api/contracts/schema/report-write.schema.json#/$defs/columns",
5657
+ "removeReportColumn": "skills/cosmic-meta-api/contracts/schema/report-write.schema.json#/$defs/columnRemoval",
5471
5658
  "setReportFilters": "skills/cosmic-meta-api/contracts/schema/report-write.schema.json#/$defs/filters",
5472
5659
  "setReportSorts": "skills/cosmic-meta-api/contracts/schema/report-write.schema.json#/$defs/sorts",
5473
5660
  "registerReportPlugin": "skills/cosmic-meta-api/contracts/schema/report-write.schema.json#/$defs/plugin",
@@ -5537,6 +5724,7 @@ _CATALOG_COMMAND_CONTRACT_REFS = {
5537
5724
  "setSummaryToField": "skills/cosmic-meta-api/contracts/cli/rule-domain.json#commands/setSummaryToField",
5538
5725
  "setReportColumns": "skills/cosmic-meta-api/contracts/cli/report-write-contracts.json#commands/setReportColumns",
5539
5726
  "addReportColumns": "skills/cosmic-meta-api/contracts/cli/report-write-contracts.json#commands/addReportColumns",
5727
+ "removeReportColumn": "skills/cosmic-meta-api/contracts/cli/report-write-contracts.json#commands/removeReportColumn",
5540
5728
  "setReportFilters": "skills/cosmic-meta-api/contracts/cli/report-write-contracts.json#commands/setReportFilters",
5541
5729
  "setReportSorts": "skills/cosmic-meta-api/contracts/cli/report-write-contracts.json#commands/setReportSorts",
5542
5730
  "registerReportPlugin": "skills/cosmic-meta-api/contracts/cli/report-write-contracts.json#commands/registerReportPlugin",
@@ -5616,11 +5804,12 @@ _CATALOG_COMMANDS_BY_EFFECT = {
5616
5804
  }),
5617
5805
  "destructive": frozenset({
5618
5806
  "deleteApp", "deleteForm", "deleteMenu", "deleteOperation", "deleteOperationValidation", "deletePlugin",
5619
- "deleteRule", "deleteUnit", "removeContainer", "removeField", "removeFilterColumn",
5807
+ "deleteRule", "deleteUnit", "removeContainer", "removeField", "removeFilterColumn", "removeReportColumn",
5620
5808
  }),
5621
5809
  }
5622
5810
  _CATALOG_RISK_OVERRIDES = {
5623
5811
  "cleanTemp": "medium",
5812
+ "removeReportColumn": "high",
5624
5813
  # the only setter whose blast radius is an entire form: an unmodified round trip
5625
5814
  # silently changed the resolved Features of 17 fields
5626
5815
  "saveFullMetadata": "high",
@@ -5645,6 +5834,11 @@ _CATALOG_UNAVAILABLE_COMMANDS = {
5645
5834
  # 404 came from calling dedicated endpoints that were never supposed to exist.
5646
5835
  # Both now route through setComplexProperty with the complexType the backend
5647
5836
  # actually dispatches on (filter / permissionControl), so they are available again.
5837
+ "setReportSorts": {
5838
+ "availability": "blocked",
5839
+ "availabilityReason": REPORT_SORTS_API_UNSAFE,
5840
+ "replacedBy": "Java reportlist query() via reportContract.defaultSorts",
5841
+ },
5648
5842
  "setComboItems": {
5649
5843
  "availability": "deprecated",
5650
5844
  "availabilityReason": (
@@ -5724,15 +5918,26 @@ _CATALOG_ARGUMENT_CONSTRAINTS = {
5724
5918
  "column is skipped, so repeating the call is idempotent"
5725
5919
  ),
5726
5920
  }],
5921
+ "removeReportColumn": [{
5922
+ "kind": "target_binding",
5923
+ "arguments": ["app_root", "formNumber", "fieldKey"],
5924
+ "identity": "exact leaf fieldKey",
5925
+ "message": "Deletes one leaf through ReportColumns.remove, verifies the remaining tree, and refreshes complete authoring when possible. Backend patch required; unavailable fails closed, never hides or rebuilds. Unknown outcomes must not be replayed; Java and entity cleanup remain separate evidence boundaries.",
5926
+ }],
5727
5927
  "setReportFilters": [{
5728
5928
  "kind": "full_replacement",
5729
5929
  "arguments": ["app_root", "formNumber", "filters"],
5730
- "message": "filters is the complete desired report filter state; CLI performs independent read-back",
5930
+ "message": (
5931
+ "filters is the complete desired report filter state; a filter may declare fieldType "
5932
+ "with its parameters for a native editor, and the result's filterEditors says which "
5933
+ "editor each filter rendered (a platform without the typed filter patch renders the "
5934
+ "legacy control). CLI performs independent read-back"
5935
+ ),
5731
5936
  }],
5732
5937
  "setReportSorts": [{
5733
- "kind": "replacement",
5938
+ "kind": "blocked",
5734
5939
  "arguments": ["app_root", "formNumber", "sorts"],
5735
- "message": "sorts replaces report-owned default sort entries and is independently read back",
5940
+ "message": "REPORT_SORTS_API_UNSAFE: no metadata write; preserve reportSorts in the Java query contract",
5736
5941
  }],
5737
5942
  "registerReportPlugin": [{
5738
5943
  "kind": "upsert",
@@ -5767,8 +5972,7 @@ _CATALOG_ARGUMENT_CONSTRAINTS = {
5767
5972
  "target": "templateNumber",
5768
5973
  "sources": ["template", "artifact.templateNumber", "emitManifest.templateNumber"],
5769
5974
  "message": (
5770
- "templateNumber has no implicit default; an explicit blank is accepted only "
5771
- "for a MainEntity dynamic form"
5975
+ "templateNumber has no implicit default; use blankPage for an explicit blank BillEntity, or empty for a MainEntity dynamic form"
5772
5976
  ),
5773
5977
  },
5774
5978
  {
@@ -5791,8 +5995,7 @@ _CATALOG_ARGUMENT_CONSTRAINTS = {
5791
5995
  "target": "templateNumber",
5792
5996
  "sources": ["template", "artifact.templateNumber", "emitManifest.templateNumber"],
5793
5997
  "message": (
5794
- "no implicit default: every artifact needs an explicit templateNumber; blank is accepted only "
5795
- "for a MainEntity dynamic form"
5998
+ "no implicit default: every artifact needs an explicit templateNumber; blankPage supports BillEntity, empty supports MainEntity"
5796
5999
  ),
5797
6000
  },
5798
6001
  {
@@ -6044,7 +6247,7 @@ _CATALOG_ARGUMENT_CONSTRAINTS = {
6044
6247
  "argument": "field_type",
6045
6248
  "selectionOwner": "model",
6046
6249
  "contractRef": "skills/cosmic-meta-knowledge/domain-model/field-types.md",
6047
- "message": "choose one documented FieldDataType; the CLI rejects invented types before auth or writes",
6250
+ "message": "select the exact documented FieldDataType from business meaning and pass it explicitly for every new field; there is no default field type",
6048
6251
  },
6049
6252
  {
6050
6253
  "kind": "conditional_parameters",
@@ -6619,6 +6822,7 @@ def _main_impl():
6619
6822
  help="One or more artifact file paths (space-separated); when omitted, from --app-root _emit_manifest.json auto-collected")
6620
6823
  prb.add_argument("--app-root", default=None, dest="app_root",
6621
6824
  help="Working directory; when provided, auto-fills --bizAppId / --bizUnitId / --artifacts / --template")
6825
+ prb.add_argument("--compact", action="store_true", help="Return verification and continuation evidence; keep the full receipt at resultPath")
6622
6826
  prb.add_argument("--app-code", "--appCode", default=None, dest="app_code",
6623
6827
  help="Exact existing application code; verifies and synchronizes a trusted appRoot identity before any build write")
6624
6828
  prb.add_argument("--emit-manifest", default=None, dest="emit_manifest",
@@ -6835,7 +7039,7 @@ def _main_impl():
6835
7039
  paf.add_argument("--biz-word", dest="biz_word",
6836
7040
  help="Concise semantic English business word without ISV prefix")
6837
7041
  paf.add_argument(
6838
- "--field-type", default="TextField", dest="field_type",
7042
+ "--field-type", default=None, dest="field_type",
6839
7043
  choices=field_type_choices(),
6840
7044
  help="Exact FieldDataType selected from the authoritative field-types contract"
6841
7045
  )
@@ -6963,8 +7167,8 @@ def _main_impl():
6963
7167
  help="New field display name")
6964
7168
  pasf.add_argument("--biz-word", required=True, dest="biz_word",
6965
7169
  help="Concise semantic English business word without ISV prefix")
6966
- pasf.add_argument("--field-type", default="TextField", dest="field_type",
6967
- help="Platform field type (default TextField)")
7170
+ pasf.add_argument("--field-type", default=None, dest="field_type",
7171
+ help="Exact platform field type selected from business semantics")
6968
7172
  pasf.add_argument("--app-root", default=None, dest="app_root",
6969
7173
  help="Already verified app root")
6970
7174
  pasf.add_argument("--project-root", default=None, dest="project_root",
@@ -7476,7 +7680,7 @@ def _main_impl():
7476
7680
  p_eccv.add_argument("--condition-field", dest="condition_field")
7477
7681
  p_eccv.add_argument(
7478
7682
  "--operator",
7479
- choices=["equals", "not-equals", "greater-than", "greater-or-equal", "less-than", "less-or-equal"])
7683
+ choices=["equals", "not-equals", "greater-than", "greater-or-equal", "less-than", "less-or-equal", "empty", "not-empty"])
7480
7684
  p_eccv.add_argument("--value")
7481
7685
  p_eccv.add_argument("--source", help="Source field key or caption")
7482
7686
  p_eccv.add_argument("--target", help="Target field key or caption")
@@ -8533,6 +8737,14 @@ def _main_impl():
8533
8737
  p_arc.add_argument("--app-root", required=True, dest="app_root",
8534
8738
  help="Owned application root used to prove the exact report target")
8535
8739
 
8740
+ p_rrc = sub.add_parser("removeReportColumn",
8741
+ help="Remove one report leaf column (requires backend deletion patch; no automatic fallback)")
8742
+ p_rrc.add_argument("--formNumber", required=True, help="Exact report form number")
8743
+ p_rrc.add_argument("--formId", default=None, help="Optional exact form ID; mismatch fails closed")
8744
+ p_rrc.add_argument("--fieldKey", required=True, help="Exact leaf column key, including a nested leaf")
8745
+ p_rrc.add_argument("--app-root", required=True, dest="app_root", help="Owned application root")
8746
+ p_rrc.add_argument("--dryRun", action="store_true", dest="dry_run", help="Local payload only; does not prove backend support")
8747
+
8536
8748
  p_srf = sub.add_parser("setReportFilters",
8537
8749
  help="Set report filter fields")
8538
8750
  p_srf.add_argument("--formNumber", required=True, help="Exact report form number")
@@ -8545,13 +8757,13 @@ def _main_impl():
8545
8757
  help="Owned application root used to prove the exact report target")
8546
8758
 
8547
8759
  p_srs = sub.add_parser("setReportSorts",
8548
- help="Set report sort configuration")
8760
+ help="Blocked unsafe metadata sorting; use the Java query contract")
8549
8761
  p_srs.add_argument("--formNumber", required=True, help="Exact report form number")
8550
8762
  p_srs.add_argument("--formId", default=None, help="Optional exact report form ID; mismatch fails closed")
8551
8763
  p_srs.add_argument("--sorts", required=True,
8552
8764
  help="Report sorts JSON file path or inline JSON string")
8553
8765
  p_srs.add_argument("--dryRun", action="store_true", dest="dry_run",
8554
- help="Only output payload, do not submit")
8766
+ help="兼容旧参数;同样拒绝危险排序写入,不输出可执行 payload")
8555
8767
  p_srs.add_argument("--app-root", required=True, dest="app_root",
8556
8768
  help="Owned application root used to prove the exact report target")
8557
8769
 
@@ -8590,8 +8802,9 @@ def _main_impl():
8590
8802
  "moreFilterPanel=ReportMoreFilterPanelAp, form=ReportFormAp (root), "
8591
8803
  "tree=ReportTreeAp, column=one report column or column group")
8592
8804
  p_srp.add_argument("--element", default=None, dest="element_key",
8593
- help="Exact report column or column group key; required for --target column "
8594
- "and refused for every other target")
8805
+ help="Exact report column or column group key; required for --target column, "
8806
+ "optional for --target tree to pick one of several ReportTreeAp controls "
8807
+ "(found by type otherwise), and refused for every other target")
8595
8808
  p_srp.add_argument("--input", required=True, dest="input_file",
8596
8809
  help="Property map as JSON or @file, e.g. '{\"ShowSeq\": false, \"PageRows\": 500}'")
8597
8810
  p_srp.add_argument("--dryRun", action="store_true", dest="dry_run",
@@ -9217,10 +9430,12 @@ def _main_impl():
9217
9430
  set_stdout_mode(bool(args.stdout))
9218
9431
  if (args.stdout or args.output_file) and hasattr(args, "raw_json"):
9219
9432
  args.raw_json = True
9220
- if args.stdout and args.cmd != "serve":
9221
- _route_unstructured_prints_to_stderr()
9433
+ # 进程级日志路由由 main 独占并恢复,内部执行函数不改写 builtins.print。
9222
9434
  app_root = _resolve_app_root_arg(args)
9223
9435
  if app_root:
9436
+ selected = bound_entrypoint(app_root, "skills/cosmic-meta-api/scripts/cosmic_api.py")
9437
+ if selected:
9438
+ return subprocess.call([sys.executable, str(selected), *sys.argv[1:]])
9224
9439
  validate_execution_binding(app_root)
9225
9440
  _retarget_runtime_dir(app_root)
9226
9441
 
@@ -9263,7 +9478,7 @@ def _main_impl():
9263
9478
  args.fields = _load_json_arg(args.fields)
9264
9479
  if args.biz_word is not None or args.combo is not None or args.combo_json is not None:
9265
9480
  raise _CliUsageError("--fields carries each field specification; do not combine single-field specifications")
9266
- if args.field_type != "TextField" or args.required or any(getattr(args, name, None) for name in (
9481
+ if args.field_type is not None or args.required or any(getattr(args, name, None) for name in (
9267
9482
  "basedata", "ref_bill", "mul_table", "unit_field", "flex_type", "ref_base_field", "ref_display_prop", "asstTypeId", "asst_type", "extAppId")):
9268
9483
  raise _CliUsageError("--fields carries reference/type options on each item; do not combine single-field or selected-extension flags")
9269
9484
  validate_add_fields_input(args.fields)
@@ -9330,10 +9545,13 @@ def _main_impl():
9330
9545
  # before resolving a platform target so malformed JSON can never cause a write.
9331
9546
  if args.cmd in ("setReportColumns", "addReportColumns"):
9332
9547
  args._report_payload = normalize_report_columns_input(_load_json_arg(args.columns))
9548
+ elif args.cmd == "removeReportColumn":
9549
+ args.fieldKey = normalize_report_column_removal_input({"fieldKey": args.fieldKey})["fieldKey"]
9333
9550
  elif args.cmd == "setReportFilters":
9334
9551
  args._report_payload = normalize_report_filters_input(_load_json_arg(args.filters))
9335
9552
  elif args.cmd == "setReportSorts":
9336
- args._report_payload = normalize_report_sorts_input(_load_json_arg(args.sorts))
9553
+ normalize_report_sorts_input(_load_json_arg(args.sorts))
9554
+ raise RuntimeError(REPORT_SORTS_API_UNSAFE)
9337
9555
  elif args.cmd == "registerReportPlugin":
9338
9556
  args._report_payload = normalize_report_plugin_input(_load_json_arg(args.plugin))
9339
9557
 
@@ -9528,10 +9746,9 @@ def _main_impl():
9528
9746
  file=sys.stderr,
9529
9747
  )
9530
9748
  sys.exit(2)
9531
- if _tpl == "" and not _artifact_supports_blank_template(raw_artifact):
9749
+ if _tpl in {"", "blankPage"} and not _artifact_supports_blank_template(raw_artifact, _tpl):
9532
9750
  print(
9533
- "⛔ run: 空 templateNumber 仅适用于 MainEntity 动态表单;"
9534
- "BillEntity/BaseEntity 必须提供真实模板编码。",
9751
+ "⛔ run: 空 templateNumber 仅适用于 MainEntity;BillEntity 显式空白创建请使用 blankPage。",
9535
9752
  file=sys.stderr,
9536
9753
  )
9537
9754
  sys.exit(2)
@@ -9821,11 +10038,11 @@ def _main_impl():
9821
10038
  invalid_blank_tpl = [
9822
10039
  os.path.basename(p)
9823
10040
  for p, t in tpl_per_artifact.items()
9824
- if t == "" and not _artifact_file_supports_blank_template(p)
10041
+ if t in {"", "blankPage"} and not _artifact_file_supports_blank_template(p, t)
9825
10042
  ]
9826
10043
  if invalid_blank_tpl:
9827
10044
  print(
9828
- "⛔ runBatch: 空 templateNumber 仅适用于 MainEntity 动态表单:"
10045
+ "⛔ runBatch: 空模板仅支持 MainEntity 或显式 blankPage 的 BillEntity:"
9829
10046
  + ", ".join(invalid_blank_tpl),
9830
10047
  file=sys.stderr,
9831
10048
  )
@@ -10744,7 +10961,7 @@ def _main_impl():
10744
10961
  if not final_ok and phase != "buildMeta" and not summary_one.get("errorCode"):
10745
10962
  _attach_post_build_phase_failure(
10746
10963
  summary_one, transformed, phase=phase, app_root=app_root,
10747
- artifact_path=afile, pending_write=pending_write,
10964
+ artifact_path=afile, pending_write=pending_write, verification=verification,
10748
10965
  )
10749
10966
  _apply_build_failure_contract(transformed, summary_one)
10750
10967
  return {
@@ -10917,7 +11134,7 @@ def _main_impl():
10917
11134
  _atomic_json(progress.result_path, summary)
10918
11135
  elif app_root:
10919
11136
  summary = save_run_batch_result(summary, app_root, artifact_files)
10920
- pjson(summary)
11137
+ pjson(result_summary(summary, summary["resultPath"]) if args.compact and summary.get("resultPath") else summary)
10921
11138
  if args.verify:
10922
11139
  verified = sum(
10923
11140
  1 for item in results
@@ -11750,8 +11967,12 @@ def _main_impl():
11750
11967
  raise _CliUsageError("use --input for several mappings, or --source/--target for one mapping")
11751
11968
  if args.input_file and args.only_empty_target:
11752
11969
  raise _CliUsageError("with --input put per-row onlyEmptyTarget inside mappings")
11753
- if not args.input_file and not all((args.condition_field, args.operator, args.value, args.source, args.target)):
11754
- raise _CliUsageError("single mapping requires condition-field, operator, value, source and target")
11970
+ if not args.input_file and not all((args.condition_field, args.operator, args.source, args.target)):
11971
+ raise _CliUsageError("single mapping requires condition-field, operator, source and target")
11972
+ if not args.input_file and args.operator not in {"empty", "not-empty"} and not args.value:
11973
+ raise _CliUsageError("comparison operator requires a value")
11974
+ if not args.input_file and args.operator in {"empty", "not-empty"} and args.value not in (None, ""):
11975
+ raise _CliUsageError("empty and not-empty do not accept a value")
11755
11976
  token = do_auth(cfg)
11756
11977
  blocked = _bind_rule_domain_target(cfg, token, args, args.cmd)
11757
11978
  if blocked is not None:
@@ -13663,7 +13884,8 @@ def _main_impl():
13663
13884
  form_number=args.formNumber)
13664
13885
  pjson(_attach_report_readback(
13665
13886
  cfg, token, form_id, "columns", columns,
13666
- {"formId": form_id, "formNumber": args.formNumber, "columnIds": column_ids}))
13887
+ {"formId": form_id, "formNumber": args.formNumber, "columnIds": column_ids},
13888
+ app_root=args.app_root, schema_before=schema_before))
13667
13889
 
13668
13890
  elif args.cmd == "addReportColumns":
13669
13891
  columns = args._report_payload
@@ -13685,7 +13907,12 @@ def _main_impl():
13685
13907
  {"before": schema_before.get("reportColumns") or [], "requested": columns},
13686
13908
  {"formId": form_id, "formNumber": args.formNumber,
13687
13909
  "plannedAppendFieldKeys": plan["plannedAppendFieldKeys"],
13688
- "preexistingFieldKeys": plan["preexistingFieldKeys"]}))
13910
+ "preexistingFieldKeys": plan["preexistingFieldKeys"]},
13911
+ app_root=args.app_root, schema_before=schema_before))
13912
+
13913
+ elif args.cmd == "removeReportColumn":
13914
+ token = None if getattr(args, "dry_run", False) else do_auth(cfg)
13915
+ pjson(_remove_owned_report_column(cfg, token, args))
13689
13916
 
13690
13917
  elif args.cmd == "setReportFilters":
13691
13918
  filters = args._report_payload
@@ -13700,32 +13927,17 @@ def _main_impl():
13700
13927
  schema_before = do_get_report_schema(cfg, token, form_id=form_id)
13701
13928
  bound_filters = validate_report_references(
13702
13929
  "filters", filters, schema_before)
13930
+ filter_editors = {}
13703
13931
  filter_ids = do_set_report_filters(
13704
13932
  cfg, token, form_id=form_id, filters=bound_filters,
13705
- form_number=args.formNumber)
13933
+ form_number=args.formNumber, outcome=filter_editors)
13706
13934
  pjson(_attach_report_readback(
13707
13935
  cfg, token, form_id, "filters", filters,
13708
- {"formId": form_id, "formNumber": args.formNumber, "filterIds": filter_ids}))
13936
+ {"formId": form_id, "formNumber": args.formNumber, "filterIds": filter_ids},
13937
+ filter_editors=filter_editors or None, app_root=args.app_root, schema_before=schema_before))
13709
13938
 
13710
13939
  elif args.cmd == "setReportSorts":
13711
- sorts = args._report_payload
13712
- if getattr(args, "dry_run", False):
13713
- pjson(do_set_report_sorts(
13714
- cfg, None, form_id=args.formId or "", sorts=sorts,
13715
- dry_run=True, form_number=args.formNumber))
13716
- return 0
13717
- token = do_auth(cfg)
13718
- target = _resolve_owned_report_target(cfg, token, args, args.cmd)
13719
- form_id = target["formId"]
13720
- schema_before = do_get_report_schema(cfg, token, form_id=form_id)
13721
- validate_report_references(
13722
- "sorts", sorts, schema_before)
13723
- do_set_report_sorts(
13724
- cfg, token, form_id=form_id, sorts=sorts,
13725
- form_number=args.formNumber)
13726
- pjson(_attach_report_readback(
13727
- cfg, token, form_id, "sorts", sorts,
13728
- {"formId": form_id, "formNumber": args.formNumber, "ok": True}))
13940
+ raise RuntimeError(REPORT_SORTS_API_UNSAFE)
13729
13941
 
13730
13942
  elif args.cmd == "registerReportPlugin":
13731
13943
  plugin = args._report_payload
@@ -13737,12 +13949,14 @@ def _main_impl():
13737
13949
  token = do_auth(cfg)
13738
13950
  target = _resolve_owned_report_target(cfg, token, args, args.cmd)
13739
13951
  form_id = target["formId"]
13952
+ schema_before = do_get_report_schema(cfg, token, form_id=form_id)
13740
13953
  plugin_id = do_register_report_plugin(
13741
13954
  cfg, token, form_id=form_id, plugin_spec=plugin,
13742
13955
  form_number=args.formNumber)
13743
13956
  pjson(_attach_report_readback(
13744
13957
  cfg, token, form_id, "plugin", plugin,
13745
- {"formId": form_id, "formNumber": args.formNumber, "pluginId": plugin_id}))
13958
+ {"formId": form_id, "formNumber": args.formNumber, "pluginId": plugin_id},
13959
+ app_root=args.app_root, schema_before=schema_before))
13746
13960
 
13747
13961
  elif args.cmd == "setReportQueryDataSource":
13748
13962
  if getattr(args, "dry_run", False):
@@ -13862,8 +14076,7 @@ def _main_impl():
13862
14076
  _prr_id = str(_prr_row.get("formId") or "").strip()
13863
14077
  _prr_schema = do_get_report_schema(cfg, token, form_id=_prr_id)
13864
14078
  _prr_number = str(_prr_schema.get("formNumber") or args.formNumber or "").strip()
13865
- # Design-state evidence makes the captured state complete: without it the backup would
13866
- # silently omit the sorts and plugins that have to be restored.
14079
+ # 设计态导出补充插件证据;InitField 不证明排序,默认排序需求仍需完整 Artifact 对账。
13867
14080
  try:
13868
14081
  _prr_evidence = parse_report_dym(
13869
14082
  _export_report_dym_evidence(cfg, token, _prr_number, args.app_root)
@@ -13886,7 +14099,21 @@ def _main_impl():
13886
14099
  except Exception as exc:
13887
14100
  print(f"[planReportRecreate] could not read the application menu tree: {exc}",
13888
14101
  file=sys.stderr, flush=True)
13889
- _prr_plan = plan_report_recreate(_prr_schema, _prr_artifact, _prr_menus)
14102
+ # deleteForm takes the plugins on the report form itself with it and run does not
14103
+ # register them, so they are read now, while the form still exists.
14104
+ _prr_plugin_rows = None
14105
+ try:
14106
+ _prr_plugin_result = do_query_editable_plugins(cfg, token, form_number=_prr_number)
14107
+ if _is_api_success(_prr_plugin_result):
14108
+ _prr_plugin_rows = _prr_plugin_result.get("data") or []
14109
+ else:
14110
+ print("[planReportRecreate] could not read the report form's plugins: "
14111
+ f"{(_prr_plugin_result or {}).get('errorCode') or 'failed'}",
14112
+ file=sys.stderr, flush=True)
14113
+ except Exception as exc:
14114
+ print(f"[planReportRecreate] could not read the report form's plugins: {exc}",
14115
+ file=sys.stderr, flush=True)
14116
+ _prr_plan = plan_report_recreate(_prr_schema, _prr_artifact, _prr_menus, _prr_plugin_rows)
13890
14117
  if not args.no_backup:
13891
14118
  _prr_plan["backupPath"] = write_report_recreate_backup(
13892
14119
  args.app_root, _prr_number, _prr_schema, _prr_plan)
@@ -14038,6 +14265,14 @@ def _resolve_form_id_for_schema(cfg, token, args):
14038
14265
 
14039
14266
  def main():
14040
14267
  global _CLI_MACHINE_MODE, _CLI_RESULT_EMITTED, _CLI_DELIVERY_ARGS
14268
+ from _core import formatters
14269
+ original_print = builtins.print
14270
+ previous_cli = (_CLI_MACHINE_MODE, _CLI_RESULT_EMITTED, _CLI_DELIVERY_ARGS)
14271
+ previous_output = (formatters._stdout_mode, formatters._output_file,
14272
+ formatters._current_cmd, formatters._current_arg_hash)
14273
+ set_output_file(None)
14274
+ set_current_command(None)
14275
+ set_stdout_mode(False)
14041
14276
  _CLI_DELIVERY_ARGS = None
14042
14277
  sys.argv[1:] = _normalize_argv(sys.argv[1:])
14043
14278
  _CLI_MACHINE_MODE = "--stdout" in sys.argv[1:]
@@ -14069,12 +14304,23 @@ def main():
14069
14304
  _emit_machine_error("CLI_EXIT_ERROR", f"Command exited with code {code}")
14070
14305
  return code
14071
14306
  except Exception as exc:
14307
+ failure = _report_mutation_failure(exc) if isinstance(exc, ReportMutationError) else None
14308
+ if failure and _CLI_DELIVERY_ARGS is not None:
14309
+ _invalidate_unmerged_report_state(failure, _CLI_DELIVERY_ARGS.cmd,
14310
+ getattr(_CLI_DELIVERY_ARGS, "app_root", None), getattr(_CLI_DELIVERY_ARGS, "formNumber", None),
14311
+ getattr(_CLI_DELIVERY_ARGS, "dry_run", False))
14072
14312
  if _CLI_MACHINE_MODE and not _CLI_RESULT_EMITTED:
14073
14313
  error_code, message = _exception_error_code(exc)
14074
- _emit_machine_error(error_code, message)
14314
+ _emit_machine_error(error_code, message, data=failure["data"] if failure else None)
14075
14315
  else:
14076
14316
  print(f"[ERROR] {exc}", file=sys.stderr)
14077
14317
  return 1
14318
+ finally:
14319
+ # CLI 嵌入同一进程时,日志路由不能吞掉后续插件或卡片的 stdout。
14320
+ builtins.print = original_print
14321
+ (_CLI_MACHINE_MODE, _CLI_RESULT_EMITTED, _CLI_DELIVERY_ARGS) = previous_cli
14322
+ (formatters._stdout_mode, formatters._output_file,
14323
+ formatters._current_cmd, formatters._current_arg_hash) = previous_output
14078
14324
 
14079
14325
 
14080
14326
  if __name__ == "__main__":