@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.
- package/cordis.patch.yml +5 -5
- package/package.json +1 -1
- package/skills/cosmic-build/CHANGELOG.md +4 -0
- package/skills/cosmic-build/LOCAL-PATCHES.md +91 -40
- package/skills/cosmic-build/SKILL.md +46 -13
- package/skills/cosmic-build/assets/platform-report-filters/TypedReportFilterFields.java +169 -0
- package/skills/cosmic-build/assets/platform-report-filters/platform.patch +98 -0
- package/skills/cosmic-build/assets/report-algo-benchmark/ReportAlgoBenchmark.java +48 -0
- package/skills/cosmic-build/assets/report-algo-benchmark/ReportBenchmarkLogFactory.java +7 -0
- package/skills/cosmic-build/examples/java-rules/CosmicEntryQuery.java +26 -0
- package/skills/cosmic-build/examples/java-rules/CosmicReportAggregation.java +35 -0
- package/skills/cosmic-build/examples/java-rules/CosmicReportFilters.java +77 -0
- package/skills/cosmic-build/examples/java-rules/CosmicTimeAdapter.java +25 -0
- package/skills/cosmic-build/examples/java-rules/CosmicTimeAdapterCheck.java +26 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/api/algo/GroupbyDataSet.md +47 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/api/entity/report/FilterInfo.md +12 -12
- package/skills/cosmic-build/knowledge/cosmic/platform/api/entity/report/FilterItemInfo.md +2 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/form-plugin.md +2 -4
- package/skills/cosmic-build/knowledge/cosmic/platform/java-type-contracts.md +11 -2
- package/skills/cosmic-build/knowledge/cosmic/platform/list-plugin.md +18 -14
- package/skills/cosmic-build/knowledge/cosmic/platform/operation-plugin.md +4 -4
- package/skills/cosmic-build/knowledge/cosmic/platform/reportlistdata-plugin.md +17 -9
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/operation/cross-entity-aggregate-writeback.md +2 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/operation/pre-post-operation.md +15 -7
- package/skills/cosmic-build/knowledge/cosmic/platform/sdk/sdk-algo.md +24 -68
- package/skills/cosmic-build/knowledge/cosmic/platform/workflow-plugin.md +2 -7
- package/skills/cosmic-build/references/application-context.md +35 -1
- package/skills/cosmic-build/references/common-reference.md +1 -1
- package/skills/cosmic-build/references/compile-guide.md +55 -68
- package/skills/cosmic-build/references/delivery.md +59 -7
- package/skills/cosmic-build/references/execution-contract.md +38 -0
- package/skills/cosmic-build/references/form-discovery.md +7 -1
- package/skills/cosmic-build/references/java-plugin-delivery.md +32 -162
- package/skills/cosmic-build/references/operation-dispatch.md +17 -0
- package/skills/cosmic-build/references/plugin-handoff-input.md +131 -0
- package/skills/cosmic-build/references/plugin-runtime-routing.md +1 -1
- package/skills/cosmic-build/references/report-filter-contract.md +78 -0
- package/skills/cosmic-build/references/sdk-inspection.md +23 -0
- package/skills/cosmic-build/schemas/data-model-authoring.schema.json +2 -3
- package/skills/cosmic-build/scripts/_http_client.py +55 -55
- package/skills/cosmic-build/scripts/appconfig_load.py +2 -2
- package/skills/cosmic-build/scripts/apply_report_filter_extension.py +39 -0
- package/skills/cosmic-build/scripts/bootstrap_cosmic_context.py +37 -2
- package/skills/cosmic-build/scripts/compile_plugin_source.py +279 -156
- package/skills/cosmic-build/scripts/execution_binding.py +40 -0
- package/skills/cosmic-build/scripts/inspect_platform_sdk.py +169 -0
- package/skills/cosmic-build/scripts/kd_api.py +23 -22
- package/skills/cosmic-build/scripts/kd_auth.py +1 -1
- package/skills/cosmic-build/scripts/kd_env_loader.py +19 -10
- package/skills/cosmic-build/scripts/kd_token_cache.py +2 -2
- package/skills/cosmic-build/scripts/kingscript_sdk_sync.py +350 -0
- package/skills/cosmic-build/scripts/kingscript_source_gate.py +11 -8
- package/skills/cosmic-build/scripts/platform_api_contract.py +246 -11
- package/skills/cosmic-build/scripts/plugin_corpus/build_index.py +403 -0
- package/skills/cosmic-build/scripts/plugin_corpus/jvm_classfile.py +281 -0
- package/skills/cosmic-build/scripts/plugin_corpus/measure_api_usage.py +446 -0
- package/skills/cosmic-build/scripts/plugin_source_gate.py +48 -7
- package/skills/cosmic-build/scripts/plugin_target_sdk.py +45 -0
- package/skills/cosmic-build/scripts/prepare_plugin_handoff.py +493 -47
- package/skills/cosmic-build/scripts/preview_url.py +35 -6
- package/skills/cosmic-build/scripts/query_plugin_handoff.py +250 -0
- package/skills/cosmic-build/scripts/register_plugins.py +28 -2
- package/skills/cosmic-build/scripts/render_output_cards.py +42 -9
- package/skills/cosmic-build/scripts/report_algo_benchmark.py +46 -0
- package/skills/cosmic-build/scripts/run.py +2 -2
- package/skills/cosmic-build/scripts/sync_generated_locale_assets.py +43 -0
- package/skills/cosmic-build/scripts/test/fixtures/ReportSdkCheck.java +64 -0
- package/skills/cosmic-build/scripts/test/fixtures/java_context_booking_native.json +541 -0
- package/skills/cosmic-build/scripts/test/test_add_rule_business_contract.py +85 -14
- package/skills/cosmic-build/scripts/test/test_batch5_drift_governance.py +12 -3
- package/skills/cosmic-build/scripts/test/test_build_meta_order.py +0 -16
- package/skills/cosmic-build/scripts/test/test_cli_continuation.py +87 -0
- package/skills/cosmic-build/scripts/test/test_compile_plugin_source.py +224 -3
- package/skills/cosmic-build/scripts/test/test_control_authoring.py +123 -4
- package/skills/cosmic-build/scripts/test/test_cosmic_sdk_examples.py +141 -0
- package/skills/cosmic-build/scripts/test/test_data_model_contract.py +30 -5
- package/skills/cosmic-build/scripts/test/test_delivery_stage_boundary.py +2 -10
- package/skills/cosmic-build/scripts/test/test_dym_sync_background.py +11 -0
- package/skills/cosmic-build/scripts/test/test_dynamic_form_metadata_only_contract.py +59 -1
- package/skills/cosmic-build/scripts/test/test_guide_skeleton_contract.py +87 -43
- package/skills/cosmic-build/scripts/test/test_inspect_platform_sdk.py +71 -0
- package/skills/cosmic-build/scripts/test/test_kd_api.py +67 -19
- package/skills/cosmic-build/scripts/test/test_kingscript_sdk_sync.py +221 -0
- package/skills/cosmic-build/scripts/test/test_kingscript_source_gate.py +10 -0
- package/skills/cosmic-build/scripts/test/test_list_configuration_closure.py +18 -0
- package/skills/cosmic-build/scripts/test/test_long_session_regressions.py +116 -0
- package/skills/cosmic-build/scripts/test/test_mcp_cli.py +524 -0
- package/skills/cosmic-build/scripts/test/test_modeling_input_recovery.py +141 -0
- package/skills/cosmic-build/scripts/test/test_open_world_cli_catalog.py +36 -2
- package/skills/cosmic-build/scripts/test/test_platform_api_contract.py +161 -1
- package/skills/cosmic-build/scripts/test/test_plugin_business_contract.py +91 -0
- package/skills/cosmic-build/scripts/test/test_plugin_corpus.py +195 -0
- package/skills/cosmic-build/scripts/test/test_plugin_handoff.py +346 -1
- package/skills/cosmic-build/scripts/test/test_plugin_target_sdk.py +70 -0
- package/skills/cosmic-build/scripts/test/test_preview_url.py +44 -1
- package/skills/cosmic-build/scripts/test/test_property_batch.py +22 -0
- package/skills/cosmic-build/scripts/test/test_property_input_discovery.py +13 -11
- package/skills/cosmic-build/scripts/test/test_query_plugin_handoff.py +160 -0
- package/skills/cosmic-build/scripts/test/test_register_plugins.py +31 -1
- package/skills/cosmic-build/scripts/test/test_render_output_cards.py +50 -4
- package/skills/cosmic-build/scripts/test/test_report_business_contract.py +27 -0
- package/skills/cosmic-build/scripts/test/test_report_doc_contract_sync.py +5 -4
- package/skills/cosmic-build/scripts/test/test_report_sdk_optimization.py +36 -0
- package/skills/cosmic-build/scripts/test/test_rule_batch_evidence.py +1 -1
- package/skills/cosmic-build/scripts/test/test_rule_domain_contract.py +20 -0
- package/skills/cosmic-build/scripts/test/test_rule_function_formula.py +16 -6
- package/skills/cosmic-build/scripts/test/test_skill_entry_contract.py +1 -0
- package/skills/cosmic-build/scripts/test/test_test_environment.py +45 -0
- package/skills/cosmic-build/scripts/test/test_verify_plugin_loading.py +79 -0
- package/skills/cosmic-build/scripts/test_environment.py +49 -0
- package/skills/cosmic-build/scripts/verify_plugin_loading.py +71 -0
- package/skills/cosmic-build/skills/cosmic-coding-impl/SKILL.md +86 -162
- package/skills/cosmic-build/skills/cosmic-coding-script/ATTRIBUTION.md +1 -1
- package/skills/cosmic-build/skills/cosmic-coding-script/SKILL.md +45 -14
- package/skills/cosmic-build/skills/cosmic-coding-script/references/kwc-controller-authoring.md +6 -0
- package/skills/cosmic-build/skills/cosmic-coding-script/references/sdk/README.md +29 -19
- package/skills/cosmic-build/skills/cosmic-coding-script/references/sdk/docs//350/204/232/346/234/254SDK.md +3 -4
- package/skills/cosmic-build/skills/cosmic-coding-script/references/sdk/indexes/package-index.md +1 -1
- package/skills/cosmic-build/skills/cosmic-coding-script/references/sdk/strategy.md +27 -18
- package/skills/cosmic-build/skills/cosmic-meta-api/SKILL.md +11 -17
- package/skills/cosmic-build/skills/cosmic-meta-api/apis/ai_meta.md +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/apis/app_menu.md +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/apis/meta_log.md +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/control-authoring.json +45 -4
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/form-control-traits.json +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/layout-ui.json +18 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/menu-publish.json +8 -2
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/property-batch.json +7 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/property-input-index.json +3 -3
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/report-write-contracts.json +1 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/rule-domain.json +37 -5
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/rule-event-contract.json +24 -3
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/run-build-meta.json +61 -12
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/schema/dropdown-menu.schema.json +69 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/schema/report-write.schema.json +2 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/schema/rule-set.schema.json +1 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/schema/rule-write.schema.json +14 -7
- package/skills/cosmic-build/skills/cosmic-meta-api/docs/api-summary.md +2 -2
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/app-action/SKILL.md +6 -3
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/develop/SKILL.md +1 -2
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/export/SKILL.md +5 -6
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/init/SKILL.md +13 -36
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/pdm/SKILL.md +8 -9
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/pdm/scripts/config.json.example +5 -15
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/reset/SKILL.md +6 -6
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/usage/SKILL.md +20 -148
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/verify/SKILL.md +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/build_receipts.py +171 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_apps.py +25 -6
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_controls.py +55 -21
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_fields.py +10 -3
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_formactions.py +63 -6
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_forms.py +59 -2
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_listui.py +8 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_metaops.py +36 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_plugins.py +115 -90
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_properties.py +666 -4
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_rules.py +1415 -87
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/commands.py +6 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/config.py +28 -15
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/execution_errors.py +40 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/formatters.py +32 -5
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/property_policy_contract.py +6 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/report_ops.py +113 -17
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/reset.py +3 -3
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/rule_contract.py +107 -26
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/build_meta_order.py +50 -49
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/cosmic_api.py +856 -150
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/field_decorator.py +47 -14
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_build_receipt_resume.py +374 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_cli_failure_contract.py +24 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_creation_identity_boundary.py +4 -2
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_dropdown_menu_contract.py +382 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_existing_form_ui.py +0 -25
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_field_decorator.py +50 -6
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_functional_group_resolution.py +83 -16
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_identity_template_resolution.py +103 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_plugin_contract.py +28 -2
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_readback_derivation.py +22 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_typed_filters.py +65 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_rule_write_closure.py +1041 -15
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_run_batch_post_build_recovery.py +65 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_run_batch_verification_evidence.py +17 -0
- package/skills/cosmic-build/skills/cosmic-meta-knowledge/generation/api-param-checklist.md +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-knowledge/generation/rule-modeling-guide.md +7 -4
- package/skills/cosmic-build/skills/cosmic-meta-modeling/SKILL.md +61 -13
- package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/app-menu/SKILL.md +8 -0
- package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/formtemplate/SKILL.md +5 -0
- package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/formtemplate/dynamic-form-artifact-schema.md +5 -0
- package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/report/SKILL.md +21 -9
- package/skills/cosmic-build/skills/cosmic-meta-modeling/references/authoring-entity-graphs.md +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-modeling/references/authoring-storage-constraints.md +5 -3
- package/skills/cosmic-build/skills/cosmic-meta-modeling/references/authoring-ui-composition.md +6 -3
- package/skills/cosmic-build/skills/cosmic-meta-modeling/references/creation-recovery.md +13 -0
- package/skills/cosmic-build/skills/cosmic-meta-modeling/references/parse-core.md +3 -0
- package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/data_model_contract.py +6 -1
- package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/data_model_emit.py +135 -87
- package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/field_type_registry.py +11 -1
- package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/isv_resolver.py +1 -3
- package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/report_validator.py +29 -0
- package/skills/cosmic-build/skills/cosmic-meta-modifier/SKILL.md +4 -2
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/context/SKILL.md +2 -2
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/execute/SKILL.md +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/operation/SKILL.md +5 -4
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/operation/operation-write.md +6 -2
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/operation/validation.md +2 -0
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/property/SKILL.md +20 -5
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/property/complex-apis.md +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/property/policy-grammar.md +4 -1
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/report-op/SKILL.md +9 -0
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/SKILL.md +61 -4
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/raw-rule-authoring.md +3 -1
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/rule-actions.md +2 -2
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/rule-reference.md +6 -6
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/state.md +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/unit/SKILL.md +6 -3
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/verify/usage-guide.md +4 -4
- package/skills/cosmic-build/skills/cosmic-meta-modifier/references/list-filter-modification.md +4 -1
- package/skills/cosmic-build/skills/cosmic-meta-ui/SKILL.md +9 -1
- package/skills/cosmic-build/skills/cosmic-meta-ui/modules/button/SKILL.md +3 -0
- package/skills/cosmic-build/skills/mcp-server/SKILL.md +210 -0
- package/skills/cosmic-build/skills/mcp-server/scripts/mcp_cli.py +431 -0
- package/skills/cosmic-build/skills/openapi-gen/SKILL.md +3 -3
- package/skills/cosmic-build/skills/openapi-gen/scripts/api/gen_runner.py +2 -2
- package/skills/cosmic-dev-best-practices/references/bill-dev-convention.md +19 -9
- package/skills/cosmic-dev-best-practices/references/cosmic-meta-api.md +14 -11
- package/skills/cosmic-dev-best-practices/references/design-examples/basedata-design-example.md +1 -1
- package/skills/cosmic-dev-best-practices/references/design-examples/bill-design-example.md +1 -1
- package/skills/cosmic-dev-best-practices/references/elements/AssistantField.md +1 -1
- package/skills/cosmic-dev-best-practices/references/elements/BasedataField.md +2 -2
- package/skills/cosmic-dev-best-practices/references/elements/BasedataPropField.md +3 -3
- package/skills/cosmic-dev-best-practices/references/elements/CreaterField.md +2 -2
- package/skills/cosmic-dev-best-practices/references/elements/CurrencyField.md +2 -2
- package/skills/cosmic-dev-best-practices/references/elements/CustomerField.md +2 -2
- package/skills/cosmic-dev-best-practices/references/elements/FlexField.md +1 -1
- package/skills/cosmic-dev-best-practices/references/elements/MaterielField.md +2 -2
- package/skills/cosmic-dev-best-practices/references/elements/OrgField.md +2 -2
- package/skills/cosmic-dev-best-practices/references/elements/SupplierField.md +2 -2
- package/skills/cosmic-dev-best-practices/references/elements/UnitField.md +1 -1
- package/skills/cosmic-dev-best-practices/references/elements/UserField.md +3 -3
- package/skills/cosmic-dev-best-practices/references/elements/common-field-property-conventions.md +16 -11
- package/skills/cosmic-dev-best-practices/references/layout-control-convention.md +5 -4
- package/skills/erp-analysis/references/delivery-contract.md +4 -2
- package/skills/erp-analysis/templates/AGENTS.template.md +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/.dsh/cosmic/.kd/config.json +0 -21
|
@@ -65,6 +65,7 @@ from naming_rules import ( # type: ignore
|
|
|
65
65
|
MAX_FIELD_KEY_LEN,
|
|
66
66
|
MAX_COLUMN_NAME_LEN,
|
|
67
67
|
MAX_TABLE_NAME_LEN,
|
|
68
|
+
MAX_ENTITY_KEY_LEN,
|
|
68
69
|
compute_budgets,
|
|
69
70
|
trim_identifiers,
|
|
70
71
|
validate_dynamic_form_number,
|
|
@@ -83,17 +84,16 @@ _standard_flag: bool | None = None
|
|
|
83
84
|
|
|
84
85
|
def _is_standard(isv: str, standard: bool | None = None) -> bool:
|
|
85
86
|
"""Determine whether to use standard naming rules (no ISV prefix).
|
|
86
|
-
|
|
87
|
-
|
|
87
|
+
|
|
88
|
+
本地补丁 #16:命名规则强制标准模式(preset 作者 2026-08-27)——原判定整段停用。
|
|
89
|
+
上游逻辑(按参数/身份判定)保留在下方注释,便于对照与回退。
|
|
88
90
|
"""
|
|
89
|
-
#
|
|
91
|
+
# 上游原逻辑(已停用):
|
|
90
92
|
# if standard is not None:
|
|
91
93
|
# return bool(standard)
|
|
92
94
|
# if _standard_flag is not None:
|
|
93
95
|
# return bool(_standard_flag)
|
|
94
96
|
# return isv.lower() == "kingdee" if isv else False
|
|
95
|
-
|
|
96
|
-
# 命名规则强制标准模式(无 ISV 前缀,preset 作者 2026-08-27)
|
|
97
97
|
return True
|
|
98
98
|
|
|
99
99
|
|
|
@@ -187,11 +187,10 @@ def make_entity_key_map(
|
|
|
187
187
|
biz_name = str(entity.get("bizName") or "").strip()
|
|
188
188
|
if not biz_name:
|
|
189
189
|
continue
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
)
|
|
190
|
+
logical = str(entity.get("logicalName") or "").strip()
|
|
191
|
+
entity_key = _make_namespaced_key(logical or biz_name, isv, standard=standard)
|
|
192
|
+
if logical:
|
|
193
|
+
result[logical] = entity_key
|
|
195
194
|
result[biz_name] = entity_key
|
|
196
195
|
result[entity_key] = entity_key
|
|
197
196
|
result[_make_entity_key(biz_name, isv)] = entity_key
|
|
@@ -236,10 +235,10 @@ def _consume_physical_name(item: dict, generated: str) -> str:
|
|
|
236
235
|
return physical or generated
|
|
237
236
|
|
|
238
237
|
|
|
239
|
-
def _consume_logical_name(item: dict, generated: str) -> str:
|
|
238
|
+
def _consume_logical_name(item: dict, generated: str, isv: str = "") -> str:
|
|
240
239
|
"""Consume one optional exact persisted logical identifier."""
|
|
241
240
|
logical = str(item.pop("logicalName", "") or "").strip()
|
|
242
|
-
return logical
|
|
241
|
+
return _make_namespaced_key(logical, isv) if logical else generated
|
|
243
242
|
|
|
244
243
|
|
|
245
244
|
def _make_mul_basedata_table_name(entity_key: str, column_name: str) -> str:
|
|
@@ -376,7 +375,16 @@ def _check_lengths(entities: list) -> None:
|
|
|
376
375
|
"""在自动名与精确约束汇合后校验最终落库标识。"""
|
|
377
376
|
identifier = re.compile(r"^[a-z][a-z0-9_]*$")
|
|
378
377
|
seen_tables: set[str] = set()
|
|
378
|
+
seen_entities: set[str] = set()
|
|
379
|
+
seen_fields: set[str] = set()
|
|
379
380
|
for ent in entities:
|
|
381
|
+
entity_key = str(ent.get("entityKey") or "")
|
|
382
|
+
limit = 36 if ent.get("type") == "MainEntity" else MAX_ENTITY_KEY_LEN
|
|
383
|
+
if len(entity_key) > limit:
|
|
384
|
+
raise ValueError(f"entityKey '{entity_key}' exceeds {limit} characters including developer prefix")
|
|
385
|
+
if entity_key in seen_entities:
|
|
386
|
+
raise ValueError(f"duplicate entityKey '{entity_key}'")
|
|
387
|
+
seen_entities.add(entity_key)
|
|
380
388
|
tname = ent.get("tableName", "")
|
|
381
389
|
if tname and not identifier.fullmatch(tname):
|
|
382
390
|
raise ValueError(f"tableName '{tname}' is not a lowercase identifier")
|
|
@@ -390,6 +398,10 @@ def _check_lengths(entities: list) -> None:
|
|
|
390
398
|
seen_columns: set[str] = set()
|
|
391
399
|
for fld in ent.get("fields", []):
|
|
392
400
|
fk = fld.get("fieldKey", "")
|
|
401
|
+
if fk and fk in seen_fields:
|
|
402
|
+
raise ValueError(f"duplicate fieldKey '{fk}'")
|
|
403
|
+
if fk:
|
|
404
|
+
seen_fields.add(fk)
|
|
393
405
|
cn = fld.get("columnName", "")
|
|
394
406
|
if fk and len(fk) > MAX_FIELD_KEY_LEN:
|
|
395
407
|
raise ValueError(
|
|
@@ -522,6 +534,21 @@ def _replace_references(artifact: dict, mapping: dict) -> None:
|
|
|
522
534
|
)
|
|
523
535
|
properties["FireUpdEvt"] = True
|
|
524
536
|
|
|
537
|
+
# Operation parameter reference slots carry logical keys; message text,
|
|
538
|
+
# runtime IDs and arbitrary parameter values remain untouched.
|
|
539
|
+
def map_parameter_references(value):
|
|
540
|
+
if isinstance(value, list):
|
|
541
|
+
for item in value:
|
|
542
|
+
map_parameter_references(item)
|
|
543
|
+
if isinstance(value, dict):
|
|
544
|
+
for key, item in value.items():
|
|
545
|
+
if key.casefold() in {"fieldkey", "entitykey", "entrykey", "formnumber"} and isinstance(item, str):
|
|
546
|
+
value[key] = mapping.get(item, item)
|
|
547
|
+
else:
|
|
548
|
+
map_parameter_references(item)
|
|
549
|
+
for operation in artifact.get("operations", []):
|
|
550
|
+
map_parameter_references(operation.get("parameter"))
|
|
551
|
+
|
|
525
552
|
# propertySettings
|
|
526
553
|
field_keys_by_id = {
|
|
527
554
|
str(field.get("id") or ""): str(field.get("fieldKey") or "")
|
|
@@ -644,9 +671,12 @@ def decorate_artifact(raw_artifact: dict, isv: str, app_number: str,
|
|
|
644
671
|
f"The run command requires artifact in bizWord/bizName format, "
|
|
645
672
|
f"not fieldKey/entityKey format."
|
|
646
673
|
)
|
|
674
|
+
logical_entity = str(ent.get("logicalName") or "").strip()
|
|
647
675
|
entity_key = _consume_logical_name(
|
|
648
|
-
ent, _make_entity_key(biz_name, isv)
|
|
676
|
+
ent, _make_entity_key(biz_name, isv), isv
|
|
649
677
|
)
|
|
678
|
+
if logical_entity:
|
|
679
|
+
mapping[logical_entity] = entity_key
|
|
650
680
|
table_name = _consume_physical_name(
|
|
651
681
|
ent, _make_table_name(entity_key, isv, biz_name)
|
|
652
682
|
)
|
|
@@ -675,9 +705,12 @@ def decorate_artifact(raw_artifact: dict, isv: str, app_number: str,
|
|
|
675
705
|
)
|
|
676
706
|
# Collision detection (must pass max_bw, otherwise numeric suffix would exceed the 0.2 truncation budget)
|
|
677
707
|
resolved = _resolve_collision(biz_word, reserved, seen, max_len=max_bw)
|
|
708
|
+
logical_field = str(fld.get("logicalName") or "").strip()
|
|
678
709
|
field_key = _consume_logical_name(
|
|
679
|
-
fld, _make_field_key(resolved, isv, app_number)
|
|
710
|
+
fld, _make_field_key(resolved, isv, app_number), isv
|
|
680
711
|
)
|
|
712
|
+
if logical_field:
|
|
713
|
+
mapping[logical_field] = field_key
|
|
681
714
|
column_name = _consume_physical_name(
|
|
682
715
|
fld, _make_column_name(resolved, isv)
|
|
683
716
|
)
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import builtins
|
|
3
|
+
from contextlib import contextmanager, ExitStack
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
import subprocess
|
|
6
|
+
import sys
|
|
7
|
+
from unittest.mock import patch
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
11
|
+
SCRIPTS = Path(__file__).resolve().parents[1]
|
|
12
|
+
sys.path.insert(0, str(SCRIPTS))
|
|
13
|
+
import cosmic_api
|
|
14
|
+
from _core.build_receipts import BuildReceipts
|
|
15
|
+
from _core import cmd_metaops
|
|
16
|
+
from _core.execution_errors import attach_execution_error
|
|
17
|
+
from execution_binding import execution_binding, validate_execution_binding
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@contextmanager
|
|
21
|
+
def _isolated_cli():
|
|
22
|
+
from _core import config, formatters
|
|
23
|
+
with ExitStack() as stack:
|
|
24
|
+
stack.enter_context(patch.object(builtins, "print", builtins.print))
|
|
25
|
+
for owner, names in [(cosmic_api, ("_CLI_MACHINE_MODE", "_CLI_DELIVERY_ARGS", "_CLI_RESULT_EMITTED")),
|
|
26
|
+
(formatters, ("_stdout_mode", "_output_file", "_current_cmd", "_current_arg_hash"))]:
|
|
27
|
+
for name in names:
|
|
28
|
+
stack.enter_context(patch.object(owner, name, getattr(owner, name)))
|
|
29
|
+
for owner in (config, cosmic_api):
|
|
30
|
+
for name in ("RUNTIME_DIR", "TOKEN_CACHE", "BIZ_CACHE", "CONTEXT_MD", "CONFIG_MD",
|
|
31
|
+
"ARTIFACTS_DIR", "BATCH_MANIFESTS_DIR", "TEMP_ARTIFACT", "WORKSPACE_JSON", "_BOUND_APP_ROOT"):
|
|
32
|
+
stack.enter_context(patch.object(owner, name, getattr(owner, name, None), create=True))
|
|
33
|
+
yield
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _artifacts(tmp_path):
|
|
37
|
+
a = tmp_path / "base.json"
|
|
38
|
+
b = tmp_path / "bill.json"
|
|
39
|
+
data = {str(a): {"entities": [{"entityKey": "vendor_base"}]},
|
|
40
|
+
str(b): {"entities": [{"entityKey": "vendor_bill", "fields": [{"refBillNumber": "vendor_base"}]}]}}
|
|
41
|
+
for source, artifact in data.items():
|
|
42
|
+
Path(source).write_text(json.dumps(artifact), encoding="utf-8")
|
|
43
|
+
return data
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_verified_dependency_receipt_survives_restart_and_only_pending_target_runs(tmp_path):
|
|
47
|
+
artifacts = _artifacts(tmp_path)
|
|
48
|
+
base, bill = artifacts
|
|
49
|
+
progress = BuildReceipts(tmp_path, "APP", {"baseUrl": "test"}, artifacts)
|
|
50
|
+
progress.start(base)
|
|
51
|
+
progress.finish(base, {"ok": True, "phase": "verify", "writeAttempted": True,
|
|
52
|
+
"verification": {"status": "pass"},
|
|
53
|
+
"result": {"data": {"entityResults": [{"formNumber": "vendor_base", "formId": "EXACT"}]}}})
|
|
54
|
+
progress.finish(bill, {"ok": False, "phase": "resolveRefBill", "writeAttempted": False})
|
|
55
|
+
resumed = BuildReceipts(tmp_path, "APP", {"baseUrl": "test"}, artifacts, resume_result=progress.result_path)
|
|
56
|
+
assert resumed.reused(base)["result"]["data"]["entityResults"][0]["formId"] == "EXACT"
|
|
57
|
+
assert resumed.reused(bill) is None
|
|
58
|
+
resumed.start(bill)
|
|
59
|
+
persisted = json.loads(resumed.result_path.read_text())
|
|
60
|
+
assert persisted["results"][1]["phase"] == "writeUnknown"
|
|
61
|
+
assert persisted["results"][1]["targetNumbers"] == ["vendor_bill"]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_unknown_write_cannot_be_replayed_or_adopted(tmp_path):
|
|
65
|
+
artifacts = _artifacts(tmp_path)
|
|
66
|
+
base = next(iter(artifacts))
|
|
67
|
+
progress = BuildReceipts(tmp_path, "APP", {}, artifacts)
|
|
68
|
+
progress.start(base)
|
|
69
|
+
with pytest.raises(ValueError, match="RECONCILIATION_REQUIRED"):
|
|
70
|
+
BuildReceipts(tmp_path, "APP", {}, artifacts, resume_result=progress.result_path)
|
|
71
|
+
copied = tmp_path / "regenerated.json"
|
|
72
|
+
copied.write_text(Path(base).read_text())
|
|
73
|
+
other = BuildReceipts(tmp_path, "APP", {}, {str(copied): artifacts[base]})
|
|
74
|
+
with pytest.raises(ValueError, match="RECONCILIATION_REQUIRED"):
|
|
75
|
+
other.start(str(copied))
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def test_zero_write_failure_allows_corrected_input(tmp_path):
|
|
79
|
+
artifacts = _artifacts(tmp_path)
|
|
80
|
+
source = next(iter(artifacts))
|
|
81
|
+
progress = BuildReceipts(tmp_path, "APP", {}, artifacts)
|
|
82
|
+
progress.start(source)
|
|
83
|
+
progress.finish(source, {"ok": False, "phase": "preflight", "writeAttempted": False})
|
|
84
|
+
artifacts[source]["entities"][0]["entityKey"] = "vendor_renamed"
|
|
85
|
+
Path(source).write_text(json.dumps(artifacts[source]))
|
|
86
|
+
resumed = BuildReceipts(tmp_path, "APP", {}, artifacts, resume_result=progress.result_path)
|
|
87
|
+
resumed.start(source)
|
|
88
|
+
assert json.loads(resumed.result_path.read_text())["results"][0]["targetNumbers"] == ["vendor_renamed"]
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def test_saved_input_and_environment_changes_require_narrow_repair(tmp_path):
|
|
92
|
+
artifacts = _artifacts(tmp_path)
|
|
93
|
+
source = next(iter(artifacts))
|
|
94
|
+
progress = BuildReceipts(tmp_path, "APP", {}, artifacts)
|
|
95
|
+
progress.start(source)
|
|
96
|
+
progress.finish(source, {"ok": True, "writeAttempted": True, "verification": {"status": "pass"}})
|
|
97
|
+
with pytest.raises(ValueError, match="IDENTITY_CHANGED"):
|
|
98
|
+
BuildReceipts(tmp_path, "OTHER", {}, artifacts, resume_result=progress.result_path)
|
|
99
|
+
Path(source).write_text(Path(source).read_text() + "\n")
|
|
100
|
+
with pytest.raises(ValueError, match="SAVED_TARGET_CHANGED"):
|
|
101
|
+
BuildReceipts(tmp_path, "APP", {}, artifacts, resume_result=progress.result_path)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def test_source_binding_rejects_old_copy_and_changed_revision(tmp_path):
|
|
105
|
+
context = tmp_path / "_runtime" / "context.json"
|
|
106
|
+
context.parent.mkdir()
|
|
107
|
+
context.write_text(json.dumps({"rootDir": str(tmp_path / "old-copy")}))
|
|
108
|
+
with pytest.raises(ValueError, match="SOURCE_MISMATCH"):
|
|
109
|
+
validate_execution_binding(tmp_path)
|
|
110
|
+
binding = execution_binding()
|
|
111
|
+
context.write_text(json.dumps({"executionBinding": {**binding, "revision": "old"}}))
|
|
112
|
+
with pytest.raises(ValueError, match="REVISION_MISMATCH"):
|
|
113
|
+
validate_execution_binding(tmp_path)
|
|
114
|
+
context.write_text(json.dumps({"executionBinding": binding}))
|
|
115
|
+
validate_execution_binding(tmp_path)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@pytest.mark.parametrize("code,category", [
|
|
119
|
+
("REF_BILL_TARGET_UNRESOLVED", "identity_resolution"),
|
|
120
|
+
("RULE_FIELD_UNRESOLVED", "identity_resolution"),
|
|
121
|
+
("CONDITIONAL_CARRY_SOURCE_PROPERTY_UNAVAILABLE", "identity_resolution"),
|
|
122
|
+
("RULE_SET_COMMAND_UNSUPPORTED", "input"),
|
|
123
|
+
("FULL_METADATA_API_UNAVAILABLE", "capability_limit"),
|
|
124
|
+
("HTTP_ERROR", "environment"),
|
|
125
|
+
])
|
|
126
|
+
def test_reference_or_input_errors_do_not_claim_platform_limit(code, category):
|
|
127
|
+
result = attach_execution_error({"status": False, "errorCode": code, "writeAttempted": False})
|
|
128
|
+
assert result["errorCategory"] == category
|
|
129
|
+
assert result["capabilityLimitConfirmed"] == (category == "capability_limit")
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@pytest.mark.parametrize("locale", ["", ".locales/zh"])
|
|
133
|
+
def test_cli_stdout_and_result_file_are_the_same_json_in_path_with_spaces(tmp_path, locale):
|
|
134
|
+
output = tmp_path / "result with spaces.json"
|
|
135
|
+
result = subprocess.run([sys.executable, str(SCRIPTS.parents[2] / locale / "skills" / "cosmic-meta-api" / "scripts" / "cosmic_api.py"), "--stdout", "--output", str(output),
|
|
136
|
+
"describeCommand", "runBatch"], capture_output=True, text=True)
|
|
137
|
+
assert result.returncode == 0, result.stderr
|
|
138
|
+
assert json.loads(result.stdout) == json.loads(output.read_text())
|
|
139
|
+
assert "--resume-result" in result.stdout
|
|
140
|
+
assert "argvPrefix" in result.stdout
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@pytest.mark.parametrize("lookup,code", [
|
|
144
|
+
({"status": True, "data": [{"formNumber": "vendor_bill", "formId": "OTHER", "appId": "OTHER_APP"}]}, "TARGET_ALREADY_EXISTS"),
|
|
145
|
+
({"status": False, "message": "connection closed"}, "TARGET_LOOKUP_UNCONFIRMED"),
|
|
146
|
+
])
|
|
147
|
+
def test_creation_conflict_and_network_failure_prevent_writes(lookup, code):
|
|
148
|
+
with patch("_core.cmd_forms.resolve_template_input", return_value="bos_billtpl"), \
|
|
149
|
+
patch.object(cmd_metaops, "do_query_forms", return_value=lookup), \
|
|
150
|
+
patch.object(cmd_metaops, "_api_call") as write:
|
|
151
|
+
result = cmd_metaops.do_build_meta({}, "token", {"entities": [{"entityKey": "vendor_bill"}]}, "APP", "UNIT")
|
|
152
|
+
assert result["errorCode"] == code
|
|
153
|
+
assert result["writeAttempted"] is False
|
|
154
|
+
write.assert_not_called()
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def test_real_batch_entry_resumes_only_unwritten_dependent_and_binds_saved_id(tmp_path, monkeypatch):
|
|
158
|
+
from contextlib import ExitStack
|
|
159
|
+
import copy
|
|
160
|
+
import kd_env_loader
|
|
161
|
+
from _core import formatters
|
|
162
|
+
from _core import config
|
|
163
|
+
(tmp_path / "archive.json").write_text(json.dumps({"appCode": "test", "appId": "APP", "type": "cosmic"}))
|
|
164
|
+
artifacts = {}
|
|
165
|
+
for name in ("base", "bill"):
|
|
166
|
+
source = tmp_path / (name + ".json")
|
|
167
|
+
artifact = {"templateNumber": "bos_billtpl", "entities": [{
|
|
168
|
+
"id": name, "type": "BillEntity", "entityKey": "vendor_" + name,
|
|
169
|
+
"tableName": "t_" + name, "fields": [],
|
|
170
|
+
}]}
|
|
171
|
+
if name == "bill":
|
|
172
|
+
artifact["entities"][0]["fields"] = [{"fieldKey": "vendor_ref", "columnName": "fref",
|
|
173
|
+
"displayName": "Reference", "dataType": "RefBillField", "refBillTargetNumber": "vendor_base"}]
|
|
174
|
+
source.write_text(json.dumps(artifact))
|
|
175
|
+
artifacts[str(source)] = artifact
|
|
176
|
+
base, bill = artifacts
|
|
177
|
+
prior = BuildReceipts(tmp_path, "APP", {}, artifacts, options=["--bizAppId", "APP"])
|
|
178
|
+
prior.start(base)
|
|
179
|
+
saved = {"status": True, "data": {"entityResults": [{"entityId": "base", "formId": "SAVED-EXACT-ID",
|
|
180
|
+
"formNumber": "vendor_base", "entityKey": "vendor_base", "success": True}], "successCount": 1}}
|
|
181
|
+
prior.finish(base, {"ok": True, "phase": "verify", "writeAttempted": True,
|
|
182
|
+
"result": saved, "verification": {"status": "pass"},
|
|
183
|
+
"summary": cosmic_api._summarize_build_meta_result(saved)})
|
|
184
|
+
prior.finish(bill, {"ok": False, "phase": "dependency", "writeAttempted": False})
|
|
185
|
+
emitted = []
|
|
186
|
+
build_inputs = []
|
|
187
|
+
modification_inputs = []
|
|
188
|
+
|
|
189
|
+
def build(cfg, token, artifact, **kwargs):
|
|
190
|
+
build_inputs.append(copy.deepcopy(artifact))
|
|
191
|
+
return {"status": True, "data": {"entityResults": [{"entityId": "bill", "formId": "NEW-BILL-ID",
|
|
192
|
+
"formNumber": "vendor_bill", "entityKey": "vendor_bill", "success": True}], "successCount": 1}}
|
|
193
|
+
|
|
194
|
+
def modify(cfg, token, **kwargs):
|
|
195
|
+
modification_inputs.append(kwargs)
|
|
196
|
+
return {"status": True, "verification": {"verified": True}}
|
|
197
|
+
|
|
198
|
+
monkeypatch.setattr(sys, "argv", [str(SCRIPTS / "cosmic_api.py"), "--stdout", "runBatch", "--app-root", str(tmp_path),
|
|
199
|
+
"--resume-result", str(prior.result_path), "--verify", "--no-sync-dym", "--artifacts", base, bill])
|
|
200
|
+
monkeypatch.setenv("PROJECT_ROOT", str(tmp_path))
|
|
201
|
+
with _isolated_cli(), ExitStack() as stack:
|
|
202
|
+
for owner, name, value in [
|
|
203
|
+
(cosmic_api, "load_config", lambda: {}),
|
|
204
|
+
(cosmic_api, "_load_workspace_for_write", lambda *a, **k: {"isv": "vendor", "appNumber": "test", "bizAppId": "APP"}),
|
|
205
|
+
(cosmic_api, "do_auth", lambda *a, **k: "test-token"),
|
|
206
|
+
(cosmic_api, "do_build_meta", build),
|
|
207
|
+
(cosmic_api, "do_modify_meta", modify),
|
|
208
|
+
(cosmic_api, "do_get_properties", lambda *a, **k: {"status": True, "data": {"billEntityId": "SAVED-EXACT-ID"}}),
|
|
209
|
+
(cosmic_api, "_verify_build_meta_readback", lambda *a: {"status": "pass"}),
|
|
210
|
+
(cosmic_api, "_append_meta_event", lambda *a, **k: None),
|
|
211
|
+
(cosmic_api, "_archive_finalize_metadata", lambda *a, **k: None),
|
|
212
|
+
(cosmic_api, "_attach_available_form_numbers", lambda c, t, r: r),
|
|
213
|
+
(cosmic_api, "pjson", emitted.append),
|
|
214
|
+
(kd_env_loader, "load_environment_identity", lambda: {}),
|
|
215
|
+
]:
|
|
216
|
+
stack.enter_context(patch.object(owner, name, value))
|
|
217
|
+
stack.enter_context(patch.object(cosmic_api, "do_query_forms", side_effect=AssertionError("The saved identity must come from its receipt")))
|
|
218
|
+
# Isolate mutable process paths and stdout mode from subsequent tests.
|
|
219
|
+
stack.enter_context(patch.object(config, "RUNTIME_DIR", str(tmp_path / "_runtime")))
|
|
220
|
+
stack.enter_context(patch.object(config, "_BOUND_APP_ROOT", str(tmp_path), create=True))
|
|
221
|
+
stack.enter_context(patch.object(formatters, "_stdout_mode", False))
|
|
222
|
+
cosmic_api._main_impl()
|
|
223
|
+
assert len(build_inputs) == 1
|
|
224
|
+
assert build_inputs[0]["entities"][0]["entityKey"] == "vendor_bill"
|
|
225
|
+
assert len(modification_inputs) == 1
|
|
226
|
+
assert modification_inputs[0]["ops"][0]["value"]["billEntityId"] == "SAVED-EXACT-ID"
|
|
227
|
+
assert emitted[-1]["success"] == 2
|
|
228
|
+
assert emitted[-1]["results"][0]["reusedReceipt"] is True
|
|
229
|
+
assert json.loads(Path(emitted[-1]["resultPath"]).read_text())["success"] == 2
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
@pytest.mark.parametrize("present", [False, True])
|
|
233
|
+
def test_read_only_reconciliation_releases_only_proven_absent_target(tmp_path, monkeypatch, present):
|
|
234
|
+
from contextlib import ExitStack
|
|
235
|
+
import kd_env_loader
|
|
236
|
+
from _core.build_receipts import _atomic_json
|
|
237
|
+
artifact = {"templateNumber": "bos_billtpl", "entities": [{"id": "root", "type": "BillEntity",
|
|
238
|
+
"entityKey": "vendor_unknown", "tableName": "t_unknown", "fields": []}]}
|
|
239
|
+
source = tmp_path / "unknown.json"
|
|
240
|
+
source.write_text(json.dumps(artifact))
|
|
241
|
+
(tmp_path / "archive.json").write_text(json.dumps({"appCode": "test", "appId": "APP", "type": "cosmic"}))
|
|
242
|
+
progress = BuildReceipts(tmp_path, "APP", {}, {str(source): artifact}, options=["--bizAppId", "APP"])
|
|
243
|
+
progress.start(str(source))
|
|
244
|
+
# Simulate a completed command that received no trustworthy write response.
|
|
245
|
+
progress.receipt["executionFinished"] = True
|
|
246
|
+
_atomic_json(progress.result_path, progress.receipt)
|
|
247
|
+
emitted = []
|
|
248
|
+
monkeypatch.setattr(sys, "argv", [str(SCRIPTS / "cosmic_api.py"), "--stdout", "runBatch", "--app-root", str(tmp_path),
|
|
249
|
+
"--resume-result", str(progress.result_path), "--reconcile-only", "--verify", "--artifacts", str(source)])
|
|
250
|
+
monkeypatch.setenv("PROJECT_ROOT", str(tmp_path))
|
|
251
|
+
with _isolated_cli(), ExitStack() as stack:
|
|
252
|
+
for owner, name, value in [
|
|
253
|
+
(cosmic_api, "load_config", lambda: {}),
|
|
254
|
+
(cosmic_api, "_load_workspace_for_write", lambda *a, **k: {"isv": "vendor", "appNumber": "test", "bizAppId": "APP"}),
|
|
255
|
+
(cosmic_api, "do_auth", lambda *a, **k: "test-token"),
|
|
256
|
+
(cosmic_api, "do_query_forms", lambda *a, **k: {"status": True, "data": [{"formNumber": "vendor_unknown", "formId": "UNRELATED"}] if present else []}),
|
|
257
|
+
(cosmic_api, "_append_meta_event", lambda *a, **k: None),
|
|
258
|
+
(cosmic_api, "pjson", emitted.append),
|
|
259
|
+
(kd_env_loader, "load_environment_identity", lambda: {}),
|
|
260
|
+
]:
|
|
261
|
+
stack.enter_context(patch.object(owner, name, value))
|
|
262
|
+
build = stack.enter_context(patch.object(cosmic_api, "do_build_meta"))
|
|
263
|
+
with pytest.raises(SystemExit):
|
|
264
|
+
cosmic_api._main_impl()
|
|
265
|
+
build.assert_not_called()
|
|
266
|
+
result = emitted[-1]
|
|
267
|
+
assert result["resume"]["ready"] is (not present)
|
|
268
|
+
assert result["results"][0]["phase"] == ("writeUnknown" if present else "reconciledAbsent")
|
|
269
|
+
if not present:
|
|
270
|
+
assert result["results"][0]["writeAttempted"] is False
|
|
271
|
+
resumed = BuildReceipts(tmp_path, "APP", {}, {str(source): artifact},
|
|
272
|
+
options=["--bizAppId", "APP"], resume_result=result["resultPath"])
|
|
273
|
+
resumed.start(str(source))
|
|
274
|
+
else:
|
|
275
|
+
assert result["results"][0].get("ok") is False
|
|
276
|
+
assert not result.get("forms")
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def test_reconciliation_refuses_an_active_original_process(tmp_path):
|
|
280
|
+
artifacts = _artifacts(tmp_path)
|
|
281
|
+
progress = BuildReceipts(tmp_path, "APP", {}, artifacts)
|
|
282
|
+
progress.start(next(iter(artifacts)))
|
|
283
|
+
with pytest.raises(ValueError, match="still active"):
|
|
284
|
+
BuildReceipts(tmp_path, "APP", {}, artifacts, resume_result=progress.result_path, reconcile_only=True)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def test_bootstrap_machine_failure_stays_parseable(tmp_path):
|
|
288
|
+
result = subprocess.run([sys.executable, str(SCRIPTS.parents[2] / "scripts" / "bootstrap_cosmic_context.py"),
|
|
289
|
+
"--stdout", "--project-root", str(tmp_path)], capture_output=True, text=True)
|
|
290
|
+
assert result.returncode != 0
|
|
291
|
+
assert json.loads(result.stdout)["status"] is False
|
|
292
|
+
assert "executionBinding" in result.stdout
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def test_serve_keeps_protocol_stdout_when_diagnostics_are_routed(capsys):
|
|
296
|
+
import io
|
|
297
|
+
with _isolated_cli(), patch.object(cosmic_api, "do_auth", return_value="test-token"), \
|
|
298
|
+
patch.object(sys, "stdin", io.StringIO('{"cmd":"ping"}\n{"cmd":"exit"}\n')):
|
|
299
|
+
cosmic_api._route_unstructured_prints_to_stderr()
|
|
300
|
+
cosmic_api._serve_loop({})
|
|
301
|
+
lines = capsys.readouterr().out.splitlines()
|
|
302
|
+
assert lines[0] == "###SERVE_READY###"
|
|
303
|
+
assert json.loads(lines[1]) == {"success": True, "data": "pong"}
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def test_root_identity_is_persisted_before_independent_readback(tmp_path):
|
|
307
|
+
artifacts = _artifacts(tmp_path)
|
|
308
|
+
source = next(iter(artifacts))
|
|
309
|
+
progress = BuildReceipts(tmp_path, "APP", {}, artifacts)
|
|
310
|
+
progress.start(source)
|
|
311
|
+
response = {"status": True, "data": {"entityResults": [{"entityKey": "vendor_base",
|
|
312
|
+
"formId": "SAVED-BEFORE-READ", "success": True}]}}
|
|
313
|
+
with patch("_core.cmd_forms.resolve_template_input", return_value="bos_billtpl"), \
|
|
314
|
+
patch.object(cmd_metaops, "do_query_forms", return_value={"status": True, "data": []}), \
|
|
315
|
+
patch.object(cmd_metaops, "_api_call", return_value=response), \
|
|
316
|
+
patch("_core.cmd_forms.do_get_form_schema", side_effect=RuntimeError("read interrupted")):
|
|
317
|
+
result = cmd_metaops.do_build_meta({}, "token", artifacts[source], "APP", "UNIT",
|
|
318
|
+
on_write_result=lambda received: progress.finish(source, {
|
|
319
|
+
"ok": False, "phase": "rootReceipt", "writeAttempted": True, "result": received}))
|
|
320
|
+
persisted = json.loads(progress.result_path.read_text())
|
|
321
|
+
assert persisted["results"][0]["result"]["data"]["entityResults"][0]["formId"] == "SAVED-BEFORE-READ"
|
|
322
|
+
assert result["status"] is False
|
|
323
|
+
assert persisted["resume"]["ready"] is False
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
@pytest.mark.parametrize("reader", ["_core.cmd_forms.resolve_template_input", "_core.cmd_metaops.do_query_forms"])
|
|
327
|
+
def test_precreate_read_exception_is_confirmed_unwritten(reader):
|
|
328
|
+
with patch("_core.cmd_forms.resolve_template_input", return_value="bos_billtpl"), \
|
|
329
|
+
patch("_core.cmd_metaops.do_query_forms", return_value={"status": True, "data": []}), \
|
|
330
|
+
patch(reader, side_effect=ConnectionResetError("injected pre-write network failure")), \
|
|
331
|
+
patch.object(cmd_metaops, "_api_call") as write:
|
|
332
|
+
result = cmd_metaops.do_build_meta({}, "token", {"entities": [{"entityKey": "vendor_probe"}]}, "APP", "UNIT")
|
|
333
|
+
assert result["writeAttempted"] is False
|
|
334
|
+
assert attach_execution_error(result)["errorCategory"] == "environment"
|
|
335
|
+
write.assert_not_called()
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def test_verified_receipt_clears_intermediate_error_category(tmp_path):
|
|
339
|
+
artifacts = _artifacts(tmp_path)
|
|
340
|
+
source = next(iter(artifacts))
|
|
341
|
+
progress = BuildReceipts(tmp_path, "APP", {}, artifacts)
|
|
342
|
+
progress.start(source)
|
|
343
|
+
progress.finish(source, {"ok": False, "phase": "rootReceipt", "writeAttempted": True})
|
|
344
|
+
final = progress.finish(source, {"ok": True, "phase": "verify", "verification": {"status": "pass"}})
|
|
345
|
+
assert "errorCategory" not in final
|
|
346
|
+
assert "capabilityLimitConfirmed" not in final
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
@pytest.mark.parametrize("attempts,expected", [([False], False), ([True, False], True), ([None, False], None)])
|
|
350
|
+
def test_batch_summary_preserves_actual_write_boundary(attempts, expected):
|
|
351
|
+
result = cosmic_api._summarize_run_batch({"failed": len(attempts), "success": 0, "results": [
|
|
352
|
+
{"ok": False, "writeAttempted": value, "summary": {"ok": False, "targetConflicts": [
|
|
353
|
+
{"formNumber": "vendor_existing"}], "writeAttempted": value}}
|
|
354
|
+
for value in attempts
|
|
355
|
+
]})
|
|
356
|
+
assert result["writeAttempted"] is expected
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def test_execution_revision_includes_root_payload_schemas(tmp_path):
|
|
360
|
+
import execution_binding as binding_module
|
|
361
|
+
(tmp_path / "scripts").mkdir()
|
|
362
|
+
(tmp_path / "schemas").mkdir()
|
|
363
|
+
(tmp_path / "SKILL.md").write_text("fixture")
|
|
364
|
+
schema = tmp_path / "schemas" / "payload.json"
|
|
365
|
+
schema.write_text('{"required":["first"]}')
|
|
366
|
+
binding_module.execution_binding.cache_clear()
|
|
367
|
+
try:
|
|
368
|
+
with patch.object(binding_module, "__file__", str(tmp_path / "scripts" / "execution_binding.py")):
|
|
369
|
+
first = binding_module.execution_binding()["revision"]
|
|
370
|
+
schema.write_text('{"required":["second"]}')
|
|
371
|
+
binding_module.execution_binding.cache_clear()
|
|
372
|
+
assert binding_module.execution_binding()["revision"] != first
|
|
373
|
+
finally:
|
|
374
|
+
binding_module.execution_binding.cache_clear()
|
package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_cli_failure_contract.py
CHANGED
|
@@ -25,6 +25,30 @@ from _core import number_validator # noqa: E402
|
|
|
25
25
|
from _core import cmd_rules # noqa: E402
|
|
26
26
|
|
|
27
27
|
|
|
28
|
+
def test_combo_read_failure_preserves_cause_and_does_not_claim_options_absent():
|
|
29
|
+
from _core import cmd_fields
|
|
30
|
+
failure = {"status": False, "errorCode": "406",
|
|
31
|
+
"message": "kd.bos.designer.build.FullMetadataApiService not find"}
|
|
32
|
+
with mock.patch.object(cmd_fields, "_api_call", return_value=failure):
|
|
33
|
+
result = cmd_fields._combo_verify_after_modify(
|
|
34
|
+
{}, "token", "uhyl_dish", "uhyl_dish_status",
|
|
35
|
+
[{"label": "供应中", "value": "available"}], None,
|
|
36
|
+
)
|
|
37
|
+
assert result["status"] == "readback_failed"
|
|
38
|
+
assert result["cause"] == failure
|
|
39
|
+
assert "FullMetadataApiService not find" in result["message"]
|
|
40
|
+
assert "absent" not in result["message"]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_combo_remove_read_failure_cannot_report_success():
|
|
44
|
+
from _core import cmd_fields
|
|
45
|
+
with mock.patch.object(cmd_fields, "_api_call", return_value={"status": False}):
|
|
46
|
+
result = cmd_fields._combo_verify_after_modify(
|
|
47
|
+
{}, "token", "uhyl_dish", "uhyl_dish_status", [], "sold_out",
|
|
48
|
+
)
|
|
49
|
+
assert result["status"] == "readback_failed"
|
|
50
|
+
|
|
51
|
+
|
|
28
52
|
@pytest.mark.parametrize("readback", [
|
|
29
53
|
{"status": False, "message": "read unavailable"},
|
|
30
54
|
{"status": True, "data": {"Items": [{"_Type_": "BizRule", "Id": "r", "PreCondition": "amount > 200"}]}},
|
package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_creation_identity_boundary.py
CHANGED
|
@@ -30,7 +30,9 @@ def test_build_uses_observed_identity_and_schema_aware_trigger_writer():
|
|
|
30
30
|
"propertySettings": [{"elementKey": "field", "settingType": "simple", "properties": {"FireUpdEvt": True}}]}
|
|
31
31
|
original = copy.deepcopy(artifact)
|
|
32
32
|
result = {"status": True, "data": {"entityResults": [{"entityId": "e1", "entityKey": "vendor_Mixed", "formId": "id", "success": True}]}}
|
|
33
|
-
with patch("_core.
|
|
33
|
+
with patch("_core.cmd_forms.resolve_template_input", return_value="bos_billtpl"), patch(
|
|
34
|
+
"_core.cmd_metaops.do_query_forms", return_value={"status": True, "data": []},
|
|
35
|
+
), patch("_core.cmd_metaops._api_call", return_value=result) as build, patch(
|
|
34
36
|
"_core.cmd_forms.do_get_form_schema", return_value={"status": True, "data": {"formNumber": "vendor_mixed"}},
|
|
35
37
|
), patch("_core.cmd_properties.do_set_properties_verified", return_value={"status": True, "verification": {"verified": True}}) as setter:
|
|
36
38
|
actual = do_build_meta({}, "token", artifact, "app", "unit")
|
|
@@ -42,7 +44,7 @@ def test_build_uses_observed_identity_and_schema_aware_trigger_writer():
|
|
|
42
44
|
|
|
43
45
|
def test_partial_build_exposes_observed_fields_and_does_not_write_more():
|
|
44
46
|
result = {"status": True, "data": {"entityResults": [{"formId": "id", "success": False}]}}
|
|
45
|
-
with patch("_core.cmd_metaops._api_call", return_value=result), patch(
|
|
47
|
+
with patch("_core.cmd_forms.resolve_template_input", return_value="bos_billtpl"), patch("_core.cmd_metaops._api_call", return_value=result), patch(
|
|
46
48
|
"_core.cmd_forms.do_get_form_schema", return_value={"status": True, "data": {"formNumber": "existing", "entities": [{"fields": [{"fieldKey": "field"}]}]}},
|
|
47
49
|
), patch("_core.cmd_properties.do_set_properties_verified") as setter:
|
|
48
50
|
actual = do_build_meta({}, "token", {"entities": []}, "app", "unit")
|