@shundoo-ai/dsh-cosmic 1.0.19 → 1.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (245) hide show
  1. package/cordis.patch.yml +5 -5
  2. package/package.json +1 -1
  3. package/skills/cosmic-build/CHANGELOG.md +4 -0
  4. package/skills/cosmic-build/LOCAL-PATCHES.md +91 -40
  5. package/skills/cosmic-build/SKILL.md +46 -13
  6. package/skills/cosmic-build/assets/platform-report-filters/TypedReportFilterFields.java +169 -0
  7. package/skills/cosmic-build/assets/platform-report-filters/platform.patch +98 -0
  8. package/skills/cosmic-build/assets/report-algo-benchmark/ReportAlgoBenchmark.java +48 -0
  9. package/skills/cosmic-build/assets/report-algo-benchmark/ReportBenchmarkLogFactory.java +7 -0
  10. package/skills/cosmic-build/examples/java-rules/CosmicEntryQuery.java +26 -0
  11. package/skills/cosmic-build/examples/java-rules/CosmicReportAggregation.java +35 -0
  12. package/skills/cosmic-build/examples/java-rules/CosmicReportFilters.java +77 -0
  13. package/skills/cosmic-build/examples/java-rules/CosmicTimeAdapter.java +25 -0
  14. package/skills/cosmic-build/examples/java-rules/CosmicTimeAdapterCheck.java +26 -0
  15. package/skills/cosmic-build/knowledge/cosmic/platform/api/algo/GroupbyDataSet.md +47 -0
  16. package/skills/cosmic-build/knowledge/cosmic/platform/api/entity/report/FilterInfo.md +12 -12
  17. package/skills/cosmic-build/knowledge/cosmic/platform/api/entity/report/FilterItemInfo.md +2 -0
  18. package/skills/cosmic-build/knowledge/cosmic/platform/form-plugin.md +2 -4
  19. package/skills/cosmic-build/knowledge/cosmic/platform/java-type-contracts.md +11 -2
  20. package/skills/cosmic-build/knowledge/cosmic/platform/list-plugin.md +18 -14
  21. package/skills/cosmic-build/knowledge/cosmic/platform/operation-plugin.md +4 -4
  22. package/skills/cosmic-build/knowledge/cosmic/platform/reportlistdata-plugin.md +17 -9
  23. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/operation/cross-entity-aggregate-writeback.md +2 -0
  24. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/operation/pre-post-operation.md +15 -7
  25. package/skills/cosmic-build/knowledge/cosmic/platform/sdk/sdk-algo.md +24 -68
  26. package/skills/cosmic-build/knowledge/cosmic/platform/workflow-plugin.md +2 -7
  27. package/skills/cosmic-build/references/application-context.md +35 -1
  28. package/skills/cosmic-build/references/common-reference.md +1 -1
  29. package/skills/cosmic-build/references/compile-guide.md +55 -68
  30. package/skills/cosmic-build/references/delivery.md +59 -7
  31. package/skills/cosmic-build/references/execution-contract.md +38 -0
  32. package/skills/cosmic-build/references/form-discovery.md +7 -1
  33. package/skills/cosmic-build/references/java-plugin-delivery.md +32 -162
  34. package/skills/cosmic-build/references/operation-dispatch.md +17 -0
  35. package/skills/cosmic-build/references/plugin-handoff-input.md +131 -0
  36. package/skills/cosmic-build/references/plugin-runtime-routing.md +1 -1
  37. package/skills/cosmic-build/references/report-filter-contract.md +78 -0
  38. package/skills/cosmic-build/references/sdk-inspection.md +23 -0
  39. package/skills/cosmic-build/schemas/data-model-authoring.schema.json +2 -3
  40. package/skills/cosmic-build/scripts/_http_client.py +55 -55
  41. package/skills/cosmic-build/scripts/appconfig_load.py +2 -2
  42. package/skills/cosmic-build/scripts/apply_report_filter_extension.py +39 -0
  43. package/skills/cosmic-build/scripts/bootstrap_cosmic_context.py +37 -2
  44. package/skills/cosmic-build/scripts/compile_plugin_source.py +279 -156
  45. package/skills/cosmic-build/scripts/execution_binding.py +40 -0
  46. package/skills/cosmic-build/scripts/inspect_platform_sdk.py +169 -0
  47. package/skills/cosmic-build/scripts/kd_api.py +23 -22
  48. package/skills/cosmic-build/scripts/kd_auth.py +1 -1
  49. package/skills/cosmic-build/scripts/kd_env_loader.py +19 -10
  50. package/skills/cosmic-build/scripts/kd_token_cache.py +2 -2
  51. package/skills/cosmic-build/scripts/kingscript_sdk_sync.py +350 -0
  52. package/skills/cosmic-build/scripts/kingscript_source_gate.py +11 -8
  53. package/skills/cosmic-build/scripts/platform_api_contract.py +246 -11
  54. package/skills/cosmic-build/scripts/plugin_corpus/build_index.py +403 -0
  55. package/skills/cosmic-build/scripts/plugin_corpus/jvm_classfile.py +281 -0
  56. package/skills/cosmic-build/scripts/plugin_corpus/measure_api_usage.py +446 -0
  57. package/skills/cosmic-build/scripts/plugin_source_gate.py +48 -7
  58. package/skills/cosmic-build/scripts/plugin_target_sdk.py +45 -0
  59. package/skills/cosmic-build/scripts/prepare_plugin_handoff.py +493 -47
  60. package/skills/cosmic-build/scripts/preview_url.py +35 -6
  61. package/skills/cosmic-build/scripts/query_plugin_handoff.py +250 -0
  62. package/skills/cosmic-build/scripts/register_plugins.py +28 -2
  63. package/skills/cosmic-build/scripts/render_output_cards.py +42 -9
  64. package/skills/cosmic-build/scripts/report_algo_benchmark.py +46 -0
  65. package/skills/cosmic-build/scripts/run.py +2 -2
  66. package/skills/cosmic-build/scripts/sync_generated_locale_assets.py +43 -0
  67. package/skills/cosmic-build/scripts/test/fixtures/ReportSdkCheck.java +64 -0
  68. package/skills/cosmic-build/scripts/test/fixtures/java_context_booking_native.json +541 -0
  69. package/skills/cosmic-build/scripts/test/test_add_rule_business_contract.py +85 -14
  70. package/skills/cosmic-build/scripts/test/test_batch5_drift_governance.py +12 -3
  71. package/skills/cosmic-build/scripts/test/test_build_meta_order.py +0 -16
  72. package/skills/cosmic-build/scripts/test/test_cli_continuation.py +87 -0
  73. package/skills/cosmic-build/scripts/test/test_compile_plugin_source.py +224 -3
  74. package/skills/cosmic-build/scripts/test/test_control_authoring.py +123 -4
  75. package/skills/cosmic-build/scripts/test/test_cosmic_sdk_examples.py +141 -0
  76. package/skills/cosmic-build/scripts/test/test_data_model_contract.py +30 -5
  77. package/skills/cosmic-build/scripts/test/test_delivery_stage_boundary.py +2 -10
  78. package/skills/cosmic-build/scripts/test/test_dym_sync_background.py +11 -0
  79. package/skills/cosmic-build/scripts/test/test_dynamic_form_metadata_only_contract.py +59 -1
  80. package/skills/cosmic-build/scripts/test/test_guide_skeleton_contract.py +87 -43
  81. package/skills/cosmic-build/scripts/test/test_inspect_platform_sdk.py +71 -0
  82. package/skills/cosmic-build/scripts/test/test_kd_api.py +67 -19
  83. package/skills/cosmic-build/scripts/test/test_kingscript_sdk_sync.py +221 -0
  84. package/skills/cosmic-build/scripts/test/test_kingscript_source_gate.py +10 -0
  85. package/skills/cosmic-build/scripts/test/test_list_configuration_closure.py +18 -0
  86. package/skills/cosmic-build/scripts/test/test_long_session_regressions.py +116 -0
  87. package/skills/cosmic-build/scripts/test/test_mcp_cli.py +524 -0
  88. package/skills/cosmic-build/scripts/test/test_modeling_input_recovery.py +141 -0
  89. package/skills/cosmic-build/scripts/test/test_open_world_cli_catalog.py +36 -2
  90. package/skills/cosmic-build/scripts/test/test_platform_api_contract.py +161 -1
  91. package/skills/cosmic-build/scripts/test/test_plugin_business_contract.py +91 -0
  92. package/skills/cosmic-build/scripts/test/test_plugin_corpus.py +195 -0
  93. package/skills/cosmic-build/scripts/test/test_plugin_handoff.py +346 -1
  94. package/skills/cosmic-build/scripts/test/test_plugin_target_sdk.py +70 -0
  95. package/skills/cosmic-build/scripts/test/test_preview_url.py +44 -1
  96. package/skills/cosmic-build/scripts/test/test_property_batch.py +22 -0
  97. package/skills/cosmic-build/scripts/test/test_property_input_discovery.py +13 -11
  98. package/skills/cosmic-build/scripts/test/test_query_plugin_handoff.py +160 -0
  99. package/skills/cosmic-build/scripts/test/test_register_plugins.py +31 -1
  100. package/skills/cosmic-build/scripts/test/test_render_output_cards.py +50 -4
  101. package/skills/cosmic-build/scripts/test/test_report_business_contract.py +27 -0
  102. package/skills/cosmic-build/scripts/test/test_report_doc_contract_sync.py +5 -4
  103. package/skills/cosmic-build/scripts/test/test_report_sdk_optimization.py +36 -0
  104. package/skills/cosmic-build/scripts/test/test_rule_batch_evidence.py +1 -1
  105. package/skills/cosmic-build/scripts/test/test_rule_domain_contract.py +20 -0
  106. package/skills/cosmic-build/scripts/test/test_rule_function_formula.py +16 -6
  107. package/skills/cosmic-build/scripts/test/test_skill_entry_contract.py +1 -0
  108. package/skills/cosmic-build/scripts/test/test_test_environment.py +45 -0
  109. package/skills/cosmic-build/scripts/test/test_verify_plugin_loading.py +79 -0
  110. package/skills/cosmic-build/scripts/test_environment.py +49 -0
  111. package/skills/cosmic-build/scripts/verify_plugin_loading.py +71 -0
  112. package/skills/cosmic-build/skills/cosmic-coding-impl/SKILL.md +86 -162
  113. package/skills/cosmic-build/skills/cosmic-coding-script/ATTRIBUTION.md +1 -1
  114. package/skills/cosmic-build/skills/cosmic-coding-script/SKILL.md +45 -14
  115. package/skills/cosmic-build/skills/cosmic-coding-script/references/kwc-controller-authoring.md +6 -0
  116. package/skills/cosmic-build/skills/cosmic-coding-script/references/sdk/README.md +29 -19
  117. package/skills/cosmic-build/skills/cosmic-coding-script/references/sdk/docs//350/204/232/346/234/254SDK.md +3 -4
  118. package/skills/cosmic-build/skills/cosmic-coding-script/references/sdk/indexes/package-index.md +1 -1
  119. package/skills/cosmic-build/skills/cosmic-coding-script/references/sdk/strategy.md +27 -18
  120. package/skills/cosmic-build/skills/cosmic-meta-api/SKILL.md +11 -17
  121. package/skills/cosmic-build/skills/cosmic-meta-api/apis/ai_meta.md +1 -1
  122. package/skills/cosmic-build/skills/cosmic-meta-api/apis/app_menu.md +1 -1
  123. package/skills/cosmic-build/skills/cosmic-meta-api/apis/meta_log.md +1 -1
  124. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/control-authoring.json +45 -4
  125. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/form-control-traits.json +1 -1
  126. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/layout-ui.json +18 -0
  127. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/menu-publish.json +8 -2
  128. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/property-batch.json +7 -0
  129. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/property-input-index.json +3 -3
  130. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/report-write-contracts.json +1 -0
  131. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/rule-domain.json +37 -5
  132. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/rule-event-contract.json +24 -3
  133. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/run-build-meta.json +61 -12
  134. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/schema/dropdown-menu.schema.json +69 -0
  135. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/schema/report-write.schema.json +2 -1
  136. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/schema/rule-set.schema.json +1 -0
  137. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/schema/rule-write.schema.json +14 -7
  138. package/skills/cosmic-build/skills/cosmic-meta-api/docs/api-summary.md +2 -2
  139. package/skills/cosmic-build/skills/cosmic-meta-api/modules/app-action/SKILL.md +6 -3
  140. package/skills/cosmic-build/skills/cosmic-meta-api/modules/develop/SKILL.md +1 -2
  141. package/skills/cosmic-build/skills/cosmic-meta-api/modules/export/SKILL.md +5 -6
  142. package/skills/cosmic-build/skills/cosmic-meta-api/modules/init/SKILL.md +13 -36
  143. package/skills/cosmic-build/skills/cosmic-meta-api/modules/pdm/SKILL.md +8 -9
  144. package/skills/cosmic-build/skills/cosmic-meta-api/modules/pdm/scripts/config.json.example +5 -15
  145. package/skills/cosmic-build/skills/cosmic-meta-api/modules/reset/SKILL.md +6 -6
  146. package/skills/cosmic-build/skills/cosmic-meta-api/modules/usage/SKILL.md +20 -148
  147. package/skills/cosmic-build/skills/cosmic-meta-api/modules/verify/SKILL.md +1 -1
  148. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/build_receipts.py +171 -0
  149. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_apps.py +25 -6
  150. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_controls.py +55 -21
  151. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_fields.py +10 -3
  152. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_formactions.py +63 -6
  153. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_forms.py +59 -2
  154. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_listui.py +8 -0
  155. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_metaops.py +36 -1
  156. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_plugins.py +115 -90
  157. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_properties.py +666 -4
  158. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_rules.py +1415 -87
  159. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/commands.py +6 -1
  160. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/config.py +28 -15
  161. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/execution_errors.py +40 -0
  162. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/formatters.py +32 -5
  163. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/property_policy_contract.py +6 -0
  164. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/report_ops.py +113 -17
  165. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/reset.py +3 -3
  166. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/rule_contract.py +107 -26
  167. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/build_meta_order.py +50 -49
  168. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/cosmic_api.py +856 -150
  169. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/field_decorator.py +47 -14
  170. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_build_receipt_resume.py +374 -0
  171. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_cli_failure_contract.py +24 -0
  172. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_creation_identity_boundary.py +4 -2
  173. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_dropdown_menu_contract.py +382 -0
  174. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_existing_form_ui.py +0 -25
  175. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_field_decorator.py +50 -6
  176. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_functional_group_resolution.py +83 -16
  177. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_identity_template_resolution.py +103 -0
  178. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_plugin_contract.py +28 -2
  179. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_readback_derivation.py +22 -0
  180. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_typed_filters.py +65 -0
  181. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_rule_write_closure.py +1041 -15
  182. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_run_batch_post_build_recovery.py +65 -0
  183. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_run_batch_verification_evidence.py +17 -0
  184. package/skills/cosmic-build/skills/cosmic-meta-knowledge/generation/api-param-checklist.md +1 -1
  185. package/skills/cosmic-build/skills/cosmic-meta-knowledge/generation/rule-modeling-guide.md +7 -4
  186. package/skills/cosmic-build/skills/cosmic-meta-modeling/SKILL.md +61 -13
  187. package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/app-menu/SKILL.md +8 -0
  188. package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/formtemplate/SKILL.md +5 -0
  189. package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/formtemplate/dynamic-form-artifact-schema.md +5 -0
  190. package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/report/SKILL.md +21 -9
  191. package/skills/cosmic-build/skills/cosmic-meta-modeling/references/authoring-entity-graphs.md +1 -1
  192. package/skills/cosmic-build/skills/cosmic-meta-modeling/references/authoring-storage-constraints.md +5 -3
  193. package/skills/cosmic-build/skills/cosmic-meta-modeling/references/authoring-ui-composition.md +6 -3
  194. package/skills/cosmic-build/skills/cosmic-meta-modeling/references/creation-recovery.md +13 -0
  195. package/skills/cosmic-build/skills/cosmic-meta-modeling/references/parse-core.md +3 -0
  196. package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/data_model_contract.py +6 -1
  197. package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/data_model_emit.py +135 -87
  198. package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/field_type_registry.py +11 -1
  199. package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/isv_resolver.py +1 -3
  200. package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/report_validator.py +29 -0
  201. package/skills/cosmic-build/skills/cosmic-meta-modifier/SKILL.md +4 -2
  202. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/context/SKILL.md +2 -2
  203. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/execute/SKILL.md +1 -1
  204. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/operation/SKILL.md +5 -4
  205. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/operation/operation-write.md +6 -2
  206. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/operation/validation.md +2 -0
  207. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/property/SKILL.md +20 -5
  208. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/property/complex-apis.md +1 -1
  209. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/property/policy-grammar.md +4 -1
  210. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/report-op/SKILL.md +9 -0
  211. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/SKILL.md +61 -4
  212. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/raw-rule-authoring.md +3 -1
  213. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/rule-actions.md +2 -2
  214. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/rule-reference.md +6 -6
  215. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/state.md +1 -1
  216. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/unit/SKILL.md +6 -3
  217. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/verify/usage-guide.md +4 -4
  218. package/skills/cosmic-build/skills/cosmic-meta-modifier/references/list-filter-modification.md +4 -1
  219. package/skills/cosmic-build/skills/cosmic-meta-ui/SKILL.md +9 -1
  220. package/skills/cosmic-build/skills/cosmic-meta-ui/modules/button/SKILL.md +3 -0
  221. package/skills/cosmic-build/skills/mcp-server/SKILL.md +210 -0
  222. package/skills/cosmic-build/skills/mcp-server/scripts/mcp_cli.py +431 -0
  223. package/skills/cosmic-build/skills/openapi-gen/SKILL.md +3 -3
  224. package/skills/cosmic-build/skills/openapi-gen/scripts/api/gen_runner.py +2 -2
  225. package/skills/cosmic-dev-best-practices/references/bill-dev-convention.md +19 -9
  226. package/skills/cosmic-dev-best-practices/references/cosmic-meta-api.md +14 -11
  227. package/skills/cosmic-dev-best-practices/references/design-examples/basedata-design-example.md +1 -1
  228. package/skills/cosmic-dev-best-practices/references/design-examples/bill-design-example.md +1 -1
  229. package/skills/cosmic-dev-best-practices/references/elements/AssistantField.md +1 -1
  230. package/skills/cosmic-dev-best-practices/references/elements/BasedataField.md +2 -2
  231. package/skills/cosmic-dev-best-practices/references/elements/BasedataPropField.md +3 -3
  232. package/skills/cosmic-dev-best-practices/references/elements/CreaterField.md +2 -2
  233. package/skills/cosmic-dev-best-practices/references/elements/CurrencyField.md +2 -2
  234. package/skills/cosmic-dev-best-practices/references/elements/CustomerField.md +2 -2
  235. package/skills/cosmic-dev-best-practices/references/elements/FlexField.md +1 -1
  236. package/skills/cosmic-dev-best-practices/references/elements/MaterielField.md +2 -2
  237. package/skills/cosmic-dev-best-practices/references/elements/OrgField.md +2 -2
  238. package/skills/cosmic-dev-best-practices/references/elements/SupplierField.md +2 -2
  239. package/skills/cosmic-dev-best-practices/references/elements/UnitField.md +1 -1
  240. package/skills/cosmic-dev-best-practices/references/elements/UserField.md +3 -3
  241. package/skills/cosmic-dev-best-practices/references/elements/common-field-property-conventions.md +16 -11
  242. package/skills/cosmic-dev-best-practices/references/layout-control-convention.md +5 -4
  243. package/skills/erp-analysis/references/delivery-contract.md +4 -2
  244. package/skills/erp-analysis/templates/AGENTS.template.md +1 -1
  245. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/.dsh/cosmic/.kd/config.json +0 -21
@@ -441,12 +441,16 @@ from .cmd_rules import ( # noqa: F401
441
441
  do_ensure_conditional_entry_style,
442
442
  do_ensure_conditional_control_state,
443
443
  do_ensure_conditional_clear_fields,
444
+ do_ensure_conditional_field_change,
444
445
  do_ensure_conditional_copy_field_value,
446
+ do_ensure_copy_field_mappings,
445
447
  do_ensure_conditional_carry_reference_property,
448
+ do_ensure_carry_reference_mappings,
446
449
  do_ensure_conditional_calculation,
447
450
  do_ensure_quantity_conversion,
448
451
  do_ensure_conditional_field_state,
449
452
  do_ensure_field_calculation,
453
+ do_ensure_time_difference_calculation,
450
454
  do_ensure_rule_function_formula,
451
455
  do_ensure_rule_set,
452
456
  do_update_rule,
@@ -502,7 +506,7 @@ from .cmd_properties import ( # noqa: F401
502
506
  do_set_display_prop,
503
507
  do_set_permission_control,
504
508
  do_set_business_control,
505
- do_set_complex_property, do_set_custom_style, do_set_collapse_summary,
509
+ do_set_complex_property, do_configure_dropdown_menu, do_set_custom_style, do_set_collapse_summary,
506
510
  do_set_edit_search_prop,
507
511
  do_set_ref_layout,
508
512
  do_set_summary_to_field_resolved,
@@ -559,6 +563,7 @@ from .cmd_tabpage import ( # noqa: F401
559
563
 
560
564
  from .cmd_controls import ( # noqa: F401
561
565
  control_catalog,
566
+ control_input_schema,
562
567
  validate_control_batch_input,
563
568
  do_add_controls,
564
569
  )
@@ -30,6 +30,7 @@ if str(_ROOT_SCRIPTS_P) not in sys.path:
30
30
 
31
31
  from project_paths import (
32
32
  ensure_app_runtime_ignore,
33
+ get_app_identity,
33
34
  get_cosmic_metadata_runtime_dir,
34
35
  get_metadata_dir,
35
36
  get_modify_dir,
@@ -163,10 +164,16 @@ def _init_paths(temp_dir=None, app_root=None):
163
164
  resolved_app_root = (
164
165
  _Path(app_root).expanduser().resolve() if app_root is not None else None
165
166
  )
166
- _BOUND_APP_ROOT = resolved_app_root
167
167
  if resolved_app_root is not None:
168
168
  if not resolved_app_root.is_dir():
169
169
  raise ValueError(f"app root does not exist: {resolved_app_root}")
170
+ if not get_app_identity(resolved_app_root):
171
+ raise ValueError(
172
+ "APP_ROOT_IDENTITY_INVALID: appRoot must be the bound Cosmic application directory, "
173
+ "not the project root or app container"
174
+ )
175
+ _BOUND_APP_ROOT = resolved_app_root
176
+ if resolved_app_root is not None:
170
177
  ensure_app_runtime_ignore(resolved_app_root)
171
178
  temp_dir = str(
172
179
  resolved_app_root / "_runtime" / "cosmic-metadata"
@@ -211,8 +218,10 @@ def _init_paths(temp_dir=None, app_root=None):
211
218
  # Ensure directories exist
212
219
  os.makedirs(temp_dir, exist_ok=True)
213
220
  os.makedirs(os.path.join(temp_dir, "ops"), exist_ok=True) # transient ops files
214
- os.makedirs(ARTIFACTS_DIR, exist_ok=True)
215
- os.makedirs(BATCH_MANIFESTS_DIR, exist_ok=True)
221
+ # Global discovery/auth may need session state, but has no application artifacts.
222
+ if resolved_app_root is not None or _read_context_json().get("appRoot"):
223
+ os.makedirs(ARTIFACTS_DIR, exist_ok=True)
224
+ os.makedirs(BATCH_MANIFESTS_DIR, exist_ok=True)
216
225
 
217
226
  # Proactively create workspace.json (write business cloud fixed defaults), ensuring the file always exists
218
227
  # isv / standard are not written here; they are dynamically obtained by bootstrapWorkspace → getDevInfo
@@ -361,7 +370,8 @@ _API_PATH = {
361
370
  "metalogQuery": "/kapi/v2/devportal/ai-meta/metalog/query",
362
371
  "metalogCompare": "/kapi/v2/devportal/ai-meta/metalog/compare",
363
372
  "metalogRestore": "/kapi/v2/devportal/ai-meta/metalog/restore",
364
- # KSQL 导出/查询(预置数据/基础数据,管理员/子管理员)
373
+
374
+ # ─── 本地补丁(LOCAL-PATCHES #13 / #14):上游 0.0.326 仍无这些端点 ───
365
375
  "ksqlListDbRoutes": "/kapi/v2/devportal/ai-meta/ksql/listDbRoutes",
366
376
  "ksqlListDbTypes": "/kapi/v2/devportal/ai-meta/ksql/listDbTypes",
367
377
  "ksqlListTables": "/kapi/v2/devportal/ai-meta/ksql/listTables",
@@ -369,11 +379,9 @@ _API_PATH = {
369
379
  "ksqlTranslate": "/kapi/v2/devportal/ai-meta/ksql/translate",
370
380
  "exportKSQLByTable": "/kapi/v2/devportal/ai-meta/ksql/exportByTable",
371
381
  "exportKSQLByObject": "/kapi/v2/devportal/ai-meta/ksql/exportByObject",
372
- # 运行时元数据查询(数据分析 / entity-download 依赖)
373
382
  "getEntityOperate": "/kapi/v2/devportal/ai-meta/getEntityOperate",
374
383
  "getIsv": "/kapi/v2/devportal/ai-meta/getIsv",
375
384
  "getControl": "/kapi/v2/devportal/ai-meta/getControl",
376
- # 自定义数据查询 API(数据分析核心)
377
385
  "dataQuery": "/kapi/v2/open/openapi/data/query",
378
386
  "dataQueryWithPagination": "/kapi/v2/open/openapi/data/queryWithPagination",
379
387
  "getApiByUrl": "/kapi/v2/open/openapi_apilist/queryByUrl",
@@ -496,15 +504,16 @@ def config_is_initialized():
496
504
  _md._md_has_section(cfg_path, "Business Cloud / App Info"))
497
505
 
498
506
 
499
- # ─── dataQuery 参数自动 hex 编码(配合服务端解码,绕过网关 SQL 检测)──────────────
507
+ # ─── 本地补丁(LOCAL-PATCHES #15):dataQuery 参数 hex 编码 ─────────────────────
508
+ # 服务端 DataServiceController.decodeHex 解码还原;用于绕过网关 SQL 注入检测。
500
509
 
501
510
  _HEX_ENCODE_FIELDS = ("filters", "selectFields", "orderBys")
502
511
 
503
512
 
504
513
  def _encode_api_params(payload):
505
- """dataQuery / dataQueryWithPagination 发送前自动处理:filters / selectFields / orderBys
506
- 三个字符串参数 hex 编码(网关 SQL 注入检测绕过;服务端 DataServiceController 已加
507
- decodeHex 解码还原,agent 无感)。其余参数(entityId/top/pageSize 等)原样。
514
+ """dataQuery / dataQueryWithPagination 发送前把 filters / selectFields / orderBys
515
+ 三个字符串参数 hex 编码(网关 SQL 检测绕过;服务端解码,agent 无感)。
516
+ 其余参数(entityId/top/pageSize 等)原样。
508
517
  """
509
518
  if not isinstance(payload, dict):
510
519
  return payload
@@ -523,25 +532,29 @@ def _api_call(cfg, token, command, payload, method="GET"):
523
532
  (kd_api internally manages token retrieval/encrypted caching/401 forced refresh). Endpoint
524
533
  routing is still provided by this module's ``_api_url`` / ``_API_PATH``.
525
534
 
526
- ``callApiByUrl``:payload['url'] 为相对路径(如 /v2/xxx/query),动态拼接 base_url 调用
527
- (数据分析自定义 API 通道,照抄旧版 transport 逻辑)。
535
+ 本地补丁(LOCAL-PATCHES #13 / #15):
536
+ - ``callApiByUrl``:payload['url'] 为相对路径(如 /v2/xxx/query),动态拼 base_url 调用;
537
+ - dataQuery / dataQueryWithPagination 发送前自动 hex 编码。
528
538
  """
529
539
  import kd_api
530
540
 
531
- # dataQuery 参数自动 hex 编码(服务端 DataServiceController.decodeHex 解码,WAF 绕过;agent 无感)
541
+ # dataQuery 参数自动 hex 编码(本地补丁 #15)
532
542
  if command in ("dataQuery", "dataQueryWithPagination"):
533
543
  _encode_api_params(payload)
534
544
 
535
- base_url = (cfg.get("base_url") or cfg.get("baseUrl", "")).rstrip("/")
545
+ # 按 URL 直调(本地补丁 #13):拆 base(token 缓存 key 用根 URL)与 path,
546
+ # 避免 get_token 用错 base 而重新登录。
536
547
  if command == "callApiByUrl":
548
+ base_url = (cfg.get("base_url") or cfg.get("baseUrl", "")).rstrip("/")
537
549
  rel_url = payload.get("url", "")
538
550
  rest = {k: v for k, v in payload.items() if k != "url"}
539
551
  if not rel_url.startswith("/kapi/"):
540
552
  rel_url = "/kapi" + rel_url
541
- # 拆 base(token 缓存 key 用根 URL)+ path(相对路径),避免 get_token 用错 base 重新登录
542
553
  if method == "GET":
543
554
  return kd_api.get(base_url, rel_url, cfg, params=rest)
544
555
  return kd_api.post(base_url, rel_url, cfg, rest)
556
+
557
+ base_url = (cfg.get("base_url") or cfg.get("baseUrl", "")).rstrip("/")
545
558
  _api_url(cfg, command) # Validate endpoint is registered (raise ValueError if unknown)
546
559
  api_path = _API_PATH[command]
547
560
  if method == "GET":
@@ -0,0 +1,40 @@
1
+ """Evidence-based error categories shared by machine results and batch receipts."""
2
+
3
+
4
+ def classify_execution_error(result):
5
+ code = str(result.get("errorCode") or "")
6
+ data = result.get("data") if isinstance(result.get("data"), dict) else {}
7
+ attempted = result.get("writeAttempted", data.get("writeAttempted"))
8
+ if code == "RULE_SET_ITEM_FAILED" and data.get("results"):
9
+ return classify_execution_error(data["results"][-1]["result"])
10
+ if code == "RULE_SET_ITEM_EXCEPTION":
11
+ return "unknown_write"
12
+ if attempted is not False and (result.get("outcome") == "unknown_after_write"
13
+ or result.get("phase") == "writeUnknown"):
14
+ return "unknown_write"
15
+ if code == "FULL_METADATA_API_UNAVAILABLE":
16
+ return "capability_limit"
17
+ if code.endswith("LOOKUP_UNCONFIRMED"):
18
+ return "environment"
19
+ if code.startswith("BATCH_WRITE_"):
20
+ return "unknown_write"
21
+ if code == "COMMAND_UNAVAILABLE":
22
+ return "input"
23
+ if code == "CONDITIONAL_CARRY_SOURCE_PROPERTY_UNAVAILABLE":
24
+ return "identity_resolution"
25
+ if code.endswith(("_UNRESOLVED", "_AMBIGUOUS", "_NOT_FOUND")) or code.startswith(("REF_", "BD-REFERENCE", "TARGET_", "FORM_", "CREATED_IDENTITY", "CALLER_CONTEXT")):
26
+ return "identity_resolution"
27
+ if code.startswith(("SKILL_", "AUTH", "TOKEN", "HTTP", "NETWORK", "ENV", "CLI_EXIT")):
28
+ return "environment"
29
+ if code.startswith(("CLI_ARGUMENT", "INLINE_JSON", "RULE_SET_ARGS", "RULE_SET_INPUT", "BATCH_INPUT", "ARTIFACT_", "RULE_INPUT_", "RULE_FUNCTION_ARGUMENT", "RULE_SET_COMMAND")):
30
+ return "input"
31
+ return "business_validation" if attempted is False else "unknown"
32
+
33
+
34
+ def attach_execution_error(result):
35
+ if not isinstance(result, dict) or result.get("status") is not False:
36
+ return result
37
+ result = dict(result)
38
+ result["errorCategory"] = classify_execution_error(result)
39
+ result["capabilityLimitConfirmed"] = result["errorCategory"] == "capability_limit"
40
+ return result
@@ -11,6 +11,7 @@ import hashlib
11
11
  import json
12
12
  import os
13
13
  import sys
14
+ import time
14
15
 
15
16
  from .cache import _iter_biz_tree
16
17
 
@@ -639,6 +640,18 @@ def _last_result_legacy_path():
639
640
  return os.path.join(output_dir, "last_result.json")
640
641
 
641
642
 
643
+ def save_run_batch_result(result, app_root, artifact_paths):
644
+ """Save the original batch receipt for downstream verified Java preparation."""
645
+ root = os.path.abspath(app_root)
646
+ destination = os.path.join(root, "_runtime", "output", f"runBatch.{time.time_ns()}.json")
647
+ receipt = {**result, "appRoot": root, "artifactPaths": [os.path.abspath(p) for p in artifact_paths],
648
+ "resultPath": destination}
649
+ os.makedirs(os.path.dirname(destination), exist_ok=True)
650
+ with open(destination, "w", encoding="utf-8") as handle:
651
+ json.dump(receipt, handle, ensure_ascii=False, indent=2)
652
+ return receipt
653
+
654
+
642
655
  def pjson(obj, force_stdout=False):
643
656
  """Output JSON result.
644
657
 
@@ -679,11 +692,25 @@ def pjson(obj, force_stdout=False):
679
692
  f.write(text)
680
693
  except OSError:
681
694
  pass
682
- print(
683
- f"[output] Result written to: {target_file}",
684
- file=sys.stdout,
685
- flush=True,
686
- )
695
+ if _stdout_mode or force_stdout:
696
+ print(text, file=sys.stdout, flush=True)
697
+ else:
698
+ print(f"[output] Result written to: {target_file}", file=sys.stdout, flush=True)
699
+ if isinstance(obj, dict) and (obj.get("delivery") or obj.get("results")):
700
+ # Keep the complete receipt on disk, while exposing the facts
701
+ # needed to continue. Never infer verification from exit status.
702
+ summary = {key: obj[key] for key in (
703
+ "status", "errorCode", "errorCategory", "message", "total", "completed", "success", "failed",
704
+ "forms", "delivery", "executionFinished", "writeAttempted", "metadataRetained",
705
+ "doNotRetryBuildMeta", "resume", "reconcile", "reconciliation",
706
+ ) if key in obj}
707
+ summary["resultPath"] = os.path.abspath(target_file)
708
+ if isinstance(obj.get("results"), list):
709
+ summary["results"] = [{**{key: row[key] for key in ("artifactPath", "ok", "phase", "error") if key in row},
710
+ **({"verification": {key: row["verification"][key] for key in ("performed", "status", "verified")
711
+ if key in row["verification"]}} if isinstance(row.get("verification"), dict) else {})}
712
+ for row in obj["results"] if isinstance(row, dict)]
713
+ print("RESULT_SUMMARY=" + json.dumps(summary, ensure_ascii=False, separators=(",", ":")), file=sys.stdout, flush=True)
687
714
  else:
688
715
  print(text, file=sys.stdout, flush=True)
689
716
 
@@ -0,0 +1,6 @@
1
+ """Language-neutral property policy declarations; copied to locales by the asset synchronizer."""
2
+
3
+ PUBLISHED_SIMPLE_PROPERTIES = frozenset({
4
+ "mustinput", "defvalue", "maxlength", "precision", "scale", "invisible",
5
+ "width", "height",
6
+ })
@@ -235,7 +235,7 @@ def normalize_report_filters_input(value):
235
235
  if not isinstance(value, list) or not value:
236
236
  _input_error("filters must be a non-empty array")
237
237
  allowed = {
238
- "fieldPath", "displayName", "compare", "defaultValue", "mustInput", "visible",
238
+ "fieldPath", "displayName", "compare", "defaultValue", "mustInput", "visible", "source",
239
239
  }
240
240
  result = []
241
241
  seen = set()
@@ -250,6 +250,10 @@ def normalize_report_filters_input(value):
250
250
  if item["compare"] not in {"=", "in", "between"}:
251
251
  _input_error(
252
252
  f"filters[{index}].compare is not in the verified set (=, in, between)")
253
+ if "source" in item:
254
+ source = _object(item["source"], f"filters[{index}].source", {"formNumber", "fieldKey"})
255
+ item["source"] = {key: _text(source.get(key), f"filters[{index}].source.{key}", identifier=True)
256
+ for key in ("formNumber", "fieldKey")}
253
257
  for key in ("mustInput", "visible"):
254
258
  if key in item:
255
259
  item[key] = _boolean(item[key], f"filters[{index}].{key}")
@@ -617,28 +621,57 @@ def set_report_filters(cfg, token, form_id, filters, dry_run=False, form_number=
617
621
  file=sys.stderr, flush=True,
618
622
  )
619
623
 
620
- # `compare` was silently dropped before this notice existed: it is mapped and sent, and the
621
- # platform stores every comparison as the same CommonFilterColumnAp, so a `between` date
622
- # range renders as a plain filter box. Say it once per write, and let the plugin contract
623
- # carry each comparison as something query() has to apply itself.
624
+ # Comparison semantics remain a query requirement; editor shape depends on the
625
+ # deployed platform version and must be observed instead of assumed to be plain text.
624
626
  print(
625
- "[setReportFilters] this deployment does not persist filter compare: "
627
+ "[setReportFilters] query comparisons: "
626
628
  + ", ".join(
627
629
  f"{f.get('fieldPath', '')}={f.get('compare', '')}" for f in filters
628
630
  )
629
- + ". Every comparison must be applied in the data-fetch plugin's query(); "
630
- "the filter panel renders one plain control per key regardless of compare.",
631
+ + ". Verify actual filter editor types from report read-back; compare is not an "
632
+ "arbitrary control-type selector. Apply the requested query conditions in query().",
631
633
  file=sys.stderr, flush=True,
632
634
  )
633
635
 
634
636
  form_number = _ensure_form_number(cfg, token, form_id, form_number)
637
+ if any(f.get("source") for f in filters):
638
+ report_filter_source_expectations(cfg, token, filters)
635
639
  _assert_report_entity_writable(
636
640
  cfg, token, form_id, form_number, requested_filter_keys=filter_ids)
637
- result = _set_complex(cfg, token, form_number, "ReportFilters", filter_payload)
641
+ # A separate route makes an old platform reject before mutation instead of silently
642
+ # ignoring source and creating TextField. Never fall back after an unsupported write.
643
+ complex_type = "ReportFilters.typed" if any(f.get("source") for f in filters) else "ReportFilters"
644
+ result = _set_complex(cfg, token, form_number, complex_type, filter_payload)
638
645
  _check_result(result, "setReportFilters", form_mutation_possible=True)
639
646
  return filter_ids
640
647
 
641
648
 
649
+ def report_filter_source_expectations(cfg, token, filters):
650
+ """Read only the selected source forms once; no inferred reference IDs or enum values."""
651
+ sources = {}
652
+ expected = {}
653
+ for item in filters:
654
+ source = item.get("source")
655
+ if not source:
656
+ continue
657
+ number = source["formNumber"]
658
+ if number not in sources:
659
+ data = (_get_schema(cfg, token, number) or {}).get("data") or {}
660
+ sources[number] = data.get("fieldSchemas")
661
+ if not isinstance(sources[number], list):
662
+ raise RuntimeError("REPORT_TYPED_FILTER_PLATFORM_UNSUPPORTED: source field schema unavailable; install the typed filter platform extension")
663
+ matches = [row for row in sources[number] if row.get("fieldPath") == source["fieldKey"]]
664
+ if len(matches) != 1:
665
+ raise ValueError("REPORT_FILTER_SOURCE_FIELD_AMBIGUOUS_OR_MISSING: " + source["fieldKey"])
666
+ wanted = {key: value for key, value in matches[0].items() if key not in {"fieldPath", "displayName", "mustInput"}}
667
+ if item["compare"] == "between" and wanted.get("fieldType") != "DateRangeField":
668
+ if wanted.get("fieldType") not in {"DateField", "DateTimeField"}:
669
+ raise ValueError("REPORT_FILTER_RANGE_TYPE_UNSUPPORTED: " + str(wanted.get("fieldType")))
670
+ wanted["fieldType"] = "DateRangeField"
671
+ expected[item["fieldPath"]] = wanted
672
+ return expected
673
+
674
+
642
675
  def set_report_sorts(cfg, token, form_id, sorts, dry_run=False, form_number=None):
643
676
  """Set report sort configuration.
644
677
 
@@ -923,9 +956,20 @@ def get_report_schema(cfg, token, form_id):
923
956
  "unverifiableSections": [],
924
957
  }
925
958
 
959
+ typed = data.get("reportFilterSchema")
960
+ if isinstance(typed, dict) and typed.get("schemaVersion") == "app-build-typed-report-filters/v1":
961
+ report_schema["reportFilters"] = typed.get("reportFilters") or []
962
+ report_schema["typedFiltersReadBack"] = True
963
+
926
964
  if report_schema["readBackAvailable"]:
927
965
  return report_schema
928
966
 
967
+ if report_schema.get("typedFiltersReadBack"):
968
+ report_schema["readBackAvailable"] = True
969
+ report_schema["readBackSource"] = "typedFilters"
970
+ report_schema["unverifiableSections"] = ["reportColumns", "reportSorts", "reportPlugins"]
971
+ report_schema["undecidableFilterProperties"] = ["compare", "visible", "defaultValue"]
972
+
929
973
  # Derived read-back. The tested deployment exposes no data.reportSchema, but
930
974
  # data.formLayout is the same ControlAp tree that setReportColumns / setReportFilters
931
975
  # write, so the requested structure is provable from it. Returning empty arrays here used
@@ -935,7 +979,9 @@ def get_report_schema(cfg, token, form_id):
935
979
  if isinstance(layout, dict):
936
980
  report_schema["_raw_formLayout"] = layout
937
981
  report_schema["reportColumns"] = _columns_from_layout(layout)
938
- report_schema["reportFilters"] = _filters_from_layout(layout)
982
+ report_schema["reportFilters"] = _filters_from_layout(layout, data.get("schemaText") or "")
983
+ if report_schema.get("typedFiltersReadBack"):
984
+ report_schema["reportFilters"] = typed.get("reportFilters") or []
939
985
  report_schema["readBackAvailable"] = True
940
986
  report_schema["readBackSource"] = "formLayout"
941
987
  # formLayout carries the control tree only. Default sort lives in the ReportListAp
@@ -954,6 +1000,8 @@ def get_report_schema(cfg, token, form_id):
954
1000
  report_schema["undecidableFilterProperties"] = [
955
1001
  "compare", "mustInput", "visible", "defaultValue",
956
1002
  ]
1003
+ if report_schema.get("typedFiltersReadBack"):
1004
+ report_schema["undecidableFilterProperties"].remove("mustInput")
957
1005
 
958
1006
  schema_text = data.get("schemaText") or ""
959
1007
  if schema_text:
@@ -1006,17 +1054,22 @@ def _columns_from_layout(layout):
1006
1054
  ]
1007
1055
 
1008
1056
 
1009
- def _filters_from_layout(layout):
1057
+ def _filters_from_layout(layout, schema_text=""):
1010
1058
  """Derive report filter controls from the ReportFilterAp subtree of formLayout."""
1011
1059
  panel = _layout_node(layout, "ReportCommFilterPanelAp")
1012
1060
  if not isinstance(panel, dict):
1013
1061
  return []
1062
+ entities = parse_schema_text(schema_text)
1063
+ fields = (entities[0].get("fields") or []) if entities else []
1064
+ field_types = {field["fieldKey"]: field["fieldType"] for field in fields if field.get("fieldType")}
1014
1065
  result = []
1015
1066
  for child in panel.get("children") or []:
1016
1067
  if not isinstance(child, dict):
1017
1068
  continue
1018
1069
  node_type = str(child.get("type") or child.get("$type") or "")
1019
1070
  control = next((name for name in _FILTER_CONTROL_COMPARE if name in node_type), "")
1071
+ if node_type.rsplit(".", 1)[-1] == "FieldAp":
1072
+ control = "FieldAp"
1020
1073
  if not control:
1021
1074
  continue
1022
1075
  item = {
@@ -1024,6 +1077,8 @@ def _filters_from_layout(layout):
1024
1077
  "displayName": child.get("name") or "",
1025
1078
  "controlType": control,
1026
1079
  }
1080
+ if item["fieldPath"] in field_types:
1081
+ item["fieldType"] = field_types[item["fieldPath"]]
1027
1082
  # The layout's own `visible` is a control-phase list ("init,new,edit,...") and is not
1028
1083
  # the filter contract's boolean `visible`. Exposing it under the same key would make a
1029
1084
  # dropped `visible: false` look persisted, so keep the names apart.
@@ -1649,9 +1704,32 @@ def compile_report_plugin_contract(artifact, form_number=""):
1649
1704
  entry["requiredBy"] = bound_by
1650
1705
 
1651
1706
  enforcement = []
1707
+ value_contracts = []
1708
+ field_config = (artifact.get("reportProperties") or {}).get("filterFieldConfig") or []
1652
1709
  for item in filters:
1653
1710
  filter_key = str(item.get("fieldPath") or "")
1654
- accessor = f'ReportQueryParam.getFilter().getFilterItem("{filter_key}")'
1711
+ binding = next((row for row in field_config if row.get("key") == filter_key), {})
1712
+ candidates = list(dict.fromkeys(key for key in (
1713
+ binding.get("fieldName"), filter_key,
1714
+ binding["fieldName"].split("_", 1)[0] + "_" + filter_key[4:]
1715
+ if filter_key.startswith("flt_") and "_" in binding.get("fieldName", "") else None,
1716
+ ) if key))
1717
+ between = item.get("compare") == "between"
1718
+ accessor = ('ReportQueryParam.getFilter().getFilterItems()' if between
1719
+ else f'ReportQueryParam.getFilter().getValue("{filter_key}")')
1720
+ value_contracts.append({
1721
+ "filterKey": filter_key,
1722
+ "compare": item.get("compare"),
1723
+ "candidateValueKeys": candidates,
1724
+ "runtimeKeysVerified": False,
1725
+ "valueChannel": "getFilterItems" if between else "getValue",
1726
+ "rangeItemKeys": {"start": filter_key + "_startdate", "end": filter_key + "_enddate"} if between else None,
1727
+ "sourceBinding": item.get("source") or {key: binding[key] for key in ("dataSourceId", "fieldName") if binding.get(key)},
1728
+ "requiredEvidence": ["source field type and editor/reference/enum configuration",
1729
+ "actual runtime key mapping and comparison semantics",
1730
+ "query entity, field and value type",
1731
+ "F7 organization scope and number uniqueness when applicable"],
1732
+ })
1655
1733
  # Every filter carries its comparison here. The platform stores one plain control per
1656
1734
  # key whatever the compare is, so `between` / `in` / `=` exist only as a requirement on
1657
1735
  # query() — without this entry they reached Java delivery as nothing at all.
@@ -1696,6 +1774,8 @@ def compile_report_plugin_contract(artifact, form_number=""):
1696
1774
  "dataSetColumnTypes": column_types,
1697
1775
  "columnPresentation": presentation,
1698
1776
  "filterEnforcement": enforcement,
1777
+ "filterValueContracts": value_contracts,
1778
+ "permissionFilterChannel": "getQFilters",
1699
1779
  "notes": [
1700
1780
  "dataSetColumns are the column names query() must return, case-sensitive, and "
1701
1781
  "dataSetColumnTypes is the Java type each one must carry. Nothing in metadata can "
@@ -1708,10 +1788,13 @@ def compile_report_plugin_contract(artifact, form_number=""):
1708
1788
  "Bind columnPresentation in getColumns() using each java call plus its "
1709
1789
  "javaFollowUp setters; do not guess ReportColumn.TYPE_* names. Recurse into "
1710
1790
  "ReportColumnGroup.getChildren(): a grouped column is not in the top-level list.",
1711
- "Enforce each filterEnforcement item in query() from its filter key. requirement="
1712
- "compare is on every filter because the platform renders one plain control per key "
1713
- "whatever the comparison is: query() has to apply between / in / = itself. The "
1714
- "others were accepted by the write but are not observable as persisted.",
1791
+ "Enforce filterEnforcement using filterValueContracts. candidateValueKeys are "
1792
+ "unverified candidates, not a universal prefix rule; confirm actual keys and reject "
1793
+ "conflicting values. Date ranges may use separate start/end FilterItemInfo values. "
1794
+ "Use target-SDK accessors, not DynamicObject casts or toString date parsing. "
1795
+ "getQFilters carries permissions and never substitutes for extracting user values. "
1796
+ "F7 number filters are String comparisons on field.number, while field/id comparisons "
1797
+ "require Long; preserve organization and permission scope.",
1715
1798
  ],
1716
1799
  }
1717
1800
  return contract
@@ -1989,7 +2072,18 @@ def report_readback_verification(section, expected, schema):
1989
2072
  result["unverifiableRequestedProperties"] = sorted(unverifiable_requested)
1990
2073
  result["unverifiedProperties"] = sorted(undecidable)
1991
2074
  else:
1992
- mismatches = _subset_mismatches(_build_filters_payload(expected), observed, f"/{key}")
2075
+ mismatches = _subset_mismatches(_build_filters_payload([{k: v for k, v in item.items() if k != "source"} for item in expected]), observed, f"/{key}")
2076
+ # Structure-only receipts cannot prove source-derived editor configuration.
2077
+ by_key = {row.get("fieldPath"): row for row in observed or []}
2078
+ for index, item in enumerate(expected):
2079
+ if not item.get("source"):
2080
+ continue
2081
+ actual = by_key.get(item["fieldPath"], {})
2082
+ wanted = (schema.get("filterSourceExpectations") or {}).get(item["fieldPath"])
2083
+ if (not schema.get("typedFiltersReadBack") or not wanted
2084
+ or any(actual.get(name) != value for name, value in wanted.items())):
2085
+ mismatches.append({"path": f"/{key}/{index}/source", "expected": item["source"],
2086
+ "actual": actual.get("fieldType"), "reason": "typed-editor-source-not-proven"})
1993
2087
  else:
1994
2088
  mismatches = _subset_mismatches(expected, observed, f"/{key}")
1995
2089
 
@@ -2151,6 +2245,8 @@ def _build_filters_payload(filters):
2151
2245
  "displayName": f.get("displayName", ""),
2152
2246
  "compare": _map_compare_operator(f.get("compare", "=")),
2153
2247
  }
2248
+ if f.get("source"):
2249
+ item["source"] = f["source"]
2154
2250
  # Only forward keys the normalized contract accepts. The previous version also
2155
2251
  # emitted required / enableFuzzy / basedataFilter, which normalize_report_filters_input
2156
2252
  # already rejects — dead branches that could only reintroduce silent drift.
@@ -287,16 +287,16 @@ _CONTEXT_TEMPLATE = """# 当前会话上下文
287
287
  _CONFIG_MD_TEMPLATE = """# 苍穹 Open API 配置说明
288
288
 
289
289
  > **数据分工**:
290
- > - **`scripts/config.json`**(唯一真实来源):连接参数、密钥等**机器级**配置,代码直接读取
290
+ > - **`~/.kd/config.json`**(唯一真实来源):连接参数、密钥等**机器级**配置,代码直接读取
291
291
  > - **本文件 `config.md`**(人类可读 + AI 可读):ISV 信息、业务云列表等**项目级**持久配置,由 API 自动写入
292
- > - 两者存在重复的字段(如 `isv`),以 `config.json` 为准;config.md 仅作展示和 AI 快速查阅
292
+ > - 两者存在重复的字段(如 `isv`),以 `~/.kd/config.json` 为准;config.md 仅作展示和 AI 快速查阅
293
293
 
294
294
  ---
295
295
 
296
296
  ## 开发商 ISV 信息
297
297
 
298
298
  > 由 `getDevInfo` 接口自动写入,供 Skill 生成字段/实体 key 时使用。
299
- > **重置环境后此处已清空**,请切换 config.json 后运行 `getDevInfo` 或由建模/改单流程触发刷新。
299
+ > **重置环境后此处已清空**,请在 `~/.kd/config.json` 切换环境后运行 `getDevInfo` 或由建模/改单流程触发刷新。
300
300
 
301
301
  | 字段 | 值 |
302
302
  |------|-----|