asecli 0.8.3__tar.gz → 0.8.4__tar.gz
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.
- {asecli-0.8.3 → asecli-0.8.4}/PKG-INFO +7 -7
- {asecli-0.8.3 → asecli-0.8.4}/README.md +6 -6
- {asecli-0.8.3 → asecli-0.8.4}/pyproject.toml +1 -1
- {asecli-0.8.3 → asecli-0.8.4}/skills/asecli/SKILL.md +10 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/__init__.py +1 -1
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/cli/export_sg_command.py +34 -5
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/sg_export/conversion_report.py +3 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/sg_export/decoder.py +2 -2
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/sg_export/evidence.py +45 -5
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/sg_export/model.py +2 -1
- asecli-0.8.4/src/asecli/sg_export/report_presentation.py +21 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/sg_export/report_semantics.py +5 -2
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/sg_export/surface_matching.py +1 -35
- asecli-0.8.4/src/asecli/sg_export/surface_precision.py +66 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/sg_export/surface_semantics.py +20 -22
- asecli-0.8.4/tests/test_export_precision_diagnostics.py +122 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_export_sg.py +7 -8
- asecli-0.8.4/tests/test_smoke.py +4 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_surface_semantics.py +14 -11
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_surface_semantics_integration.py +2 -0
- {asecli-0.8.3 → asecli-0.8.4}/uv.lock +1 -1
- asecli-0.8.3/tests/test_smoke.py +0 -4
- {asecli-0.8.3 → asecli-0.8.4}/.github/workflows/ci.yml +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/.github/workflows/publish.yml +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/.gitignore +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/.project-architect.json +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/AGENTS.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/LICENSE +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/SECURITY.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/00-governance/audit-supplement.json +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/00-governance/change-register.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/00-governance/delivery-protocol.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/00-governance/project-charter.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/00-governance/supply-chain-policy.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/00-governance/timeline.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/00-governance/traceability.csv +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/01-architecture/assumption-experiments.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/01-architecture/module-map.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/01-architecture/project-architecture-and-requirements.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/01-architecture/technical-route.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/03-quality/bug-register.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/03-quality/regression-catalog.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/03-quality/test-strategy.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/04-delivery/project-plan-task-charter.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/04-delivery/roadmap.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/adversarial-audits/2026-09-01-014104-audit-report.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/adversarial-audits/2026-09-01-014104-optimization-plan.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/adversarial-audits/2026-09-01-145343-audit-report.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/adversarial-audits/2026-09-01-145343-optimization-plan.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/adversarial-audits/2026-09-01-193521-audit-report.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/adversarial-audits/2026-09-01-193521-optimization-plan.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/adversarial-audits/2026-09-01-203438-audit-report.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/adversarial-audits/2026-09-01-203438-optimization-plan.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/adversarial-audits/2026-09-02-121155-audit-report.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/adversarial-audits/2026-09-02-121155-optimization-plan.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/adversarial-audits/2026-09-02-175509-audit-report.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/adversarial-audits/2026-09-02-175509-optimization-plan.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/adversarial-audits/2026-09-04-122304-audit-report.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/adversarial-audits/2026-09-04-122304-optimization-plan.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/adversarial-audits/2026-09-05-142617-audit-report.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/adversarial-audits/2026-09-05-142617-optimization-plan.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/adversarial-audits/2026-09-05-160945-audit-report.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/adversarial-audits/2026-09-05-160945-optimization-plan.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/adversarial-audits/2026-09-05-213523-audit-report.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/adversarial-audits/2026-09-05-213523-optimization-plan.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/adversarial-audits/2026-09-10-193631-audit-report.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/adversarial-audits/2026-09-10-193631-optimization-plan.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/adversarial-audits/2026-09-14-123640-audit-report.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/adversarial-audits/2026-09-14-123640-optimization-plan.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/adversarial-audits/2026-09-14-124947-audit-report.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/adversarial-audits/2026-09-14-124947-optimization-plan.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/adversarial-audits/2026-09-14-132814-audit-report.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/adversarial-audits/2026-09-14-132814-optimization-plan.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/first-principles/2026-08-31-231401-optimization-plan.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/first-principles/2026-09-01-190656-optimization-plan.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/docs/proposals/2026-09-07-shadergraph-to-ase-requirements.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/scripts/install.ps1 +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/scripts/install.sh +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/skills/asecli/references/conversion-publication-receipt.v1.schema.json +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/skills/asecli/references/conversion-workflow.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/skills/asecli/references/layout-standard.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/skills/asecli/references/master-output-settings-standard.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/skills/asecli/references/material-property-standard.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/__init__.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/_editor_port_contract.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/_editor_primitives.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/_editor_spec_grammar.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/_editor_spec_io.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/_editor_spec_validation.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/_gui_handoff_store.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/_gui_project.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/_gui_resource_store.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/_gui_resource_upgrade.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/editor_create.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/editor_spec.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/graph_geometry.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/graph_geometry_parser.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/graph_inspect.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/gui_handoff.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/gui_presentation.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/gui_provider_detection.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/gui_runtime_probe.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/gui_support.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/gui_support_resource.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/mcp_client.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/recompile.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/render_settings.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/resource_text.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/resources/asecli_material_gui.authoring.part00.cs.txt +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/resources/asecli_material_gui.authoring.part01.cs.txt +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/resources/asecli_material_gui.authoring.store.cs.txt +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/resources/asecli_material_gui.condition.cs.txt +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/resources/asecli_material_gui.hydration.cs.txt +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/resources/asecli_material_gui.part00.cs.txt +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/resources/asecli_material_gui.part01.cs.txt +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/resources/asecli_material_gui.reconciliation.cs.txt +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/resources/asecli_material_gui.transaction.cs.txt +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/resources/editor_create.part00.cs.txt +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/resources/editor_create.part01.cs.txt +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/resources/editor_create.part02.cs.txt +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/resources/editor_create.part03.cs.txt +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/resources/editor_create.part04.cs.txt +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/resources/wire_route.transaction.cs.txt +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/bridge/wire_route.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/checks/__init__.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/checks/checksum.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/checks/local_vars.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/checks/usage.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/checks/validate.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/cli/__init__.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/cli/commands.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/cli/commentary_command.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/cli/contract.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/cli/contract_command.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/cli/conversion_output.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/cli/create_command.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/cli/custom_gui_command.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/cli/graph_review_command.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/cli/gui_provider.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/cli/gui_support_command.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/cli/io.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/cli/layout_command.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/cli/main.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/cli/migrate_package_command.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/cli/recompile_metadata.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/cli/skill_command.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/cli/usage_command.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/contracts/editor-graph-spec.v3.schema.json +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/contracts/sgcli.native.v3.schema.json +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/contracts.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/core/__init__.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/core/comment_bounds.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/core/comment_layout.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/core/comment_metrics.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/core/comment_purpose.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/core/commentary.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/core/compiled_metadata.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/core/compiled_properties.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/core/custom_gui.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/core/custom_gui_versions.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/core/fishbone_placement.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/core/fishbone_topology.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/core/graph_ops.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/core/graph_review.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/core/island_layout.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/core/layout.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/core/layout_audit.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/core/layout_audit_geometry.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/core/layout_audit_repeated.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/core/layout_graph.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/core/local_vars.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/core/material_gui_condition.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/core/material_gui_protocol.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/core/material_gui_spec.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/core/meticulous_layout.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/core/model.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/core/property_presentation.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/core/reuse_plan.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/core/wire_geometry.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/core/wire_router.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/export_sg.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/schema/__init__.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/schema/data/observed.json +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/schema/data/schemas.json +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/schema_validation.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/sg_export/__init__.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/sg_export/alpha_modulate.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/sg_export/binding.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/sg_export/custom_expression.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/sg_export/nodes.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/sg_export/pass_blend.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/sg_export/reconciliation.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/src/asecli/sg_export/sources.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/TEST.md +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/__init__.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/conftest.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/editor_create_v3_fixture.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/fixtures/HLIT.shader +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/fixtures/asecli_material_gui_v0_2_0.cs.txt +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/fixtures/step-antialiasing.function.txt +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_adversarial_hardening.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_audit_fixes.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_bridge_contract.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_bridge_recompile.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_build_reproducibility.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_cli_contract.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_commentary.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_contract_schema.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_conversion_report_semantics.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_core_contract.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_create.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_create_constant_presentation.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_cross_cli_workflow.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_custom_gui.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_custom_gui_cli.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_editor_create_bridge.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_editor_create_cli.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_editor_create_e2e.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_editor_create_spec.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_editor_create_spec_v3.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_editor_render_settings.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_export_property_visibility.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_governance.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_graph_review.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_graph_safety.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_gui_handoff.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_gui_native_extension.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_gui_support.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_gui_support_cli_contract.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_gui_upgrade.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_install_scripts.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_island_layout.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_layout.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_layout_governance.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_layout_local_var_policy.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_material_gui_e2e.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_mcp_security.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_migrate_package.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_mutate.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_pass_blend.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_perf.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_property_presentation.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_roundtrip.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_schema_add.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_schema_samples.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_sg_export_sources.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_skill_install.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_supply_chain.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_usage_audit.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tests/test_wire_route_bridge.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tools/build_schema.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tools/check_ci_governance.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tools/check_document_governance.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tools/check_packed_executor_loc.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tools/check_regression_catalog.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tools/check_release_candidate.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tools/compare_build_artifacts.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tools/generate_sbom.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tools/independent_wheel_smoke.py +0 -0
- {asecli-0.8.3 → asecli-0.8.4}/tools/supply_chain_check.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: asecli
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.4
|
|
4
4
|
Summary: Agent-native CLI for reading, modifying, validating and compiling Amplify Shader Editor assets
|
|
5
5
|
Project-URL: Homepage, https://github.com/seeseeczl/ASECLI
|
|
6
6
|
Project-URL: Repository, https://github.com/seeseeczl/ASECLI
|
|
@@ -28,7 +28,7 @@ Description-Content-Type: text/markdown
|
|
|
28
28
|
|
|
29
29
|
AseCLI 是面向 AI Agent 的本地 CLI:离线读取、编辑和校验 ASE 节点图;通过运行中的 Unity/团结引擎与 MCP,让真实 ASE 创建节点、提供精排几何并重新生成 HLSL。Skill 提供跨 Agent 的操作规则,CLI 提供可验证的执行结果。
|
|
30
30
|
|
|
31
|
-
当前正式版:**[0.8.
|
|
31
|
+
当前正式版:**[0.8.4](https://github.com/seeseeczl/ASECLI/releases/tag/v0.8.4)** · [PyPI](https://pypi.org/project/asecli/0.8.4/) · [Agent 操作手册](skills/asecli/SKILL.md) · [SG 转换后整理流程](skills/asecli/references/conversion-workflow.md)
|
|
32
32
|
|
|
33
33
|
ASECLI 接收 SGCLI 生成的裸 `EditorGraphSpec v3`,并可把受支持的 ASE Shader 直接导出为 SGCLI 的裸 `sgcli.native.v3`。两个方向都只有“生产者 spec 原文件→消费者 `--spec`”;独立 report 与发布 receipt 都不进入创建输入,创建成功也不代表转换效果等价。
|
|
34
34
|
|
|
@@ -132,7 +132,7 @@ curl -fsSL https://raw.githubusercontent.com/seeseeczl/ASECLI/main/scripts/insta
|
|
|
132
132
|
uv tool uninstall asecli
|
|
133
133
|
```
|
|
134
134
|
|
|
135
|
-
当前正式版本为 `0.8.
|
|
135
|
+
当前正式版本为 `0.8.4`(ASE→SG 诊断收敛、Custom Function 原样证据与 MZGUI 呈现分层),可从 [PyPI](https://pypi.org/project/asecli/0.8.4/) 或 [GitHub Release](https://github.com/seeseeczl/ASECLI/releases/tag/v0.8.4) 安装。历史版本见 [GitHub Releases](https://github.com/seeseeczl/ASECLI/releases),`v0.8.3` 保留为回滚点。
|
|
136
136
|
|
|
137
137
|
已知限制:真实 MCP 下,属性+纹理 v2 和 Reciprocal 降级 v2 的 create、manifest、validate、独立 recompile 已通过;SGCLI SphereMask v3 spec 在重载时仍可能返回 `node missing after reload: Radius` 并回滚。本次不承诺该 recipe 端到端可用,也不改变 SGCLI v2 默认值契约。
|
|
138
138
|
|
|
@@ -162,7 +162,7 @@ asecli --help
|
|
|
162
162
|
|
|
163
163
|
```bash
|
|
164
164
|
shasum -a 256 -c SHA256SUMS
|
|
165
|
-
uv tool install /path/to/asecli-0.8.
|
|
165
|
+
uv tool install /path/to/asecli-0.8.4-py3-none-any.whl
|
|
166
166
|
asecli --help
|
|
167
167
|
```
|
|
168
168
|
|
|
@@ -340,9 +340,9 @@ asecli recompile Assets/Example.shader
|
|
|
340
340
|
除显式 `--help` 保留人类可读文本外,stdout 恒为单行严格 JSON,agent 可直接解析。`--version` 也返回 JSON:
|
|
341
341
|
|
|
342
342
|
```json
|
|
343
|
-
{"contract_version":1,"cli_version":"0.8.
|
|
344
|
-
{"contract_version":1,"cli_version":"0.8.
|
|
345
|
-
{"contract_version":1,"cli_version":"0.8.
|
|
343
|
+
{"contract_version":1,"cli_version":"0.8.4","command":"parse","ok":true,"data":{"node_count":7}}
|
|
344
|
+
{"contract_version":1,"cli_version":"0.8.4","command":"parse","ok":false,"error":{"code":"NOT_FOUND","message":"..."}}
|
|
345
|
+
{"contract_version":1,"cli_version":"0.8.4","command":"version","ok":true,"data":{"version":"0.8.4"}}
|
|
346
346
|
```
|
|
347
347
|
|
|
348
348
|
常见错误码:`PARSE_ERROR`、`NOT_FOUND`、`USAGE_ERROR`、`SCHEMA_UNAVAILABLE`、`SCHEMA_VERSION_MISMATCH`、`VALIDATION_ERROR`、`PROPERTY_PRESENTATION_ERROR`、`LAYOUT_ERROR`、`CHECKSUM_FORMAT_ERROR`、`GUI_SUPPORT_ERROR`、`CUSTOM_GUI_ERROR`、`COMMENT_GROUP_ERROR`、`GRAPH_REVIEW_ERROR`、`SEMANTIC_MISMATCH`、`SG_EXPORT_BLOCKED`、`EXTERNAL_REFERENCE`、`SKILL_INSTALL_CONFLICT`、`SKILL_INSTALL_ERROR`、`WRITE_CONFLICT`、`WRITE_PARTIAL`、`UNSAFE_PATH`、`WRITE_ERROR`、`BRIDGE_ERROR`、`INTERNAL`。
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
AseCLI 是面向 AI Agent 的本地 CLI:离线读取、编辑和校验 ASE 节点图;通过运行中的 Unity/团结引擎与 MCP,让真实 ASE 创建节点、提供精排几何并重新生成 HLSL。Skill 提供跨 Agent 的操作规则,CLI 提供可验证的执行结果。
|
|
6
6
|
|
|
7
|
-
当前正式版:**[0.8.
|
|
7
|
+
当前正式版:**[0.8.4](https://github.com/seeseeczl/ASECLI/releases/tag/v0.8.4)** · [PyPI](https://pypi.org/project/asecli/0.8.4/) · [Agent 操作手册](skills/asecli/SKILL.md) · [SG 转换后整理流程](skills/asecli/references/conversion-workflow.md)
|
|
8
8
|
|
|
9
9
|
ASECLI 接收 SGCLI 生成的裸 `EditorGraphSpec v3`,并可把受支持的 ASE Shader 直接导出为 SGCLI 的裸 `sgcli.native.v3`。两个方向都只有“生产者 spec 原文件→消费者 `--spec`”;独立 report 与发布 receipt 都不进入创建输入,创建成功也不代表转换效果等价。
|
|
10
10
|
|
|
@@ -108,7 +108,7 @@ curl -fsSL https://raw.githubusercontent.com/seeseeczl/ASECLI/main/scripts/insta
|
|
|
108
108
|
uv tool uninstall asecli
|
|
109
109
|
```
|
|
110
110
|
|
|
111
|
-
当前正式版本为 `0.8.
|
|
111
|
+
当前正式版本为 `0.8.4`(ASE→SG 诊断收敛、Custom Function 原样证据与 MZGUI 呈现分层),可从 [PyPI](https://pypi.org/project/asecli/0.8.4/) 或 [GitHub Release](https://github.com/seeseeczl/ASECLI/releases/tag/v0.8.4) 安装。历史版本见 [GitHub Releases](https://github.com/seeseeczl/ASECLI/releases),`v0.8.3` 保留为回滚点。
|
|
112
112
|
|
|
113
113
|
已知限制:真实 MCP 下,属性+纹理 v2 和 Reciprocal 降级 v2 的 create、manifest、validate、独立 recompile 已通过;SGCLI SphereMask v3 spec 在重载时仍可能返回 `node missing after reload: Radius` 并回滚。本次不承诺该 recipe 端到端可用,也不改变 SGCLI v2 默认值契约。
|
|
114
114
|
|
|
@@ -138,7 +138,7 @@ asecli --help
|
|
|
138
138
|
|
|
139
139
|
```bash
|
|
140
140
|
shasum -a 256 -c SHA256SUMS
|
|
141
|
-
uv tool install /path/to/asecli-0.8.
|
|
141
|
+
uv tool install /path/to/asecli-0.8.4-py3-none-any.whl
|
|
142
142
|
asecli --help
|
|
143
143
|
```
|
|
144
144
|
|
|
@@ -316,9 +316,9 @@ asecli recompile Assets/Example.shader
|
|
|
316
316
|
除显式 `--help` 保留人类可读文本外,stdout 恒为单行严格 JSON,agent 可直接解析。`--version` 也返回 JSON:
|
|
317
317
|
|
|
318
318
|
```json
|
|
319
|
-
{"contract_version":1,"cli_version":"0.8.
|
|
320
|
-
{"contract_version":1,"cli_version":"0.8.
|
|
321
|
-
{"contract_version":1,"cli_version":"0.8.
|
|
319
|
+
{"contract_version":1,"cli_version":"0.8.4","command":"parse","ok":true,"data":{"node_count":7}}
|
|
320
|
+
{"contract_version":1,"cli_version":"0.8.4","command":"parse","ok":false,"error":{"code":"NOT_FOUND","message":"..."}}
|
|
321
|
+
{"contract_version":1,"cli_version":"0.8.4","command":"version","ok":true,"data":{"version":"0.8.4"}}
|
|
322
322
|
```
|
|
323
323
|
|
|
324
324
|
常见错误码:`PARSE_ERROR`、`NOT_FOUND`、`USAGE_ERROR`、`SCHEMA_UNAVAILABLE`、`SCHEMA_VERSION_MISMATCH`、`VALIDATION_ERROR`、`PROPERTY_PRESENTATION_ERROR`、`LAYOUT_ERROR`、`CHECKSUM_FORMAT_ERROR`、`GUI_SUPPORT_ERROR`、`CUSTOM_GUI_ERROR`、`COMMENT_GROUP_ERROR`、`GRAPH_REVIEW_ERROR`、`SEMANTIC_MISMATCH`、`SG_EXPORT_BLOCKED`、`EXTERNAL_REFERENCE`、`SKILL_INSTALL_CONFLICT`、`SKILL_INSTALL_ERROR`、`WRITE_CONFLICT`、`WRITE_PARTIAL`、`UNSAFE_PATH`、`WRITE_ERROR`、`BRIDGE_ERROR`、`INTERNAL`。
|
|
@@ -22,6 +22,16 @@ description: Create, modify, validate, and strictly lay out Amplify Shader Edito
|
|
|
22
22
|
- 旧 `{graph,report}` wrapper 只经 `asecli migrate-package OLD --out-dir DIR --extract-sg-spec` 按登记形状显式提取;只接受内层 `sgcli.native.v3`,未知或 v2 wrapper 失败关闭。无版本 discriminator 时记录 `legacy_format`,不能声称识别出 legacy schema 版本。
|
|
23
23
|
- 报告中的 `source_snapshot_sha256/source_recheck_sha256` 必须一致;`producer_schema_validated` 与 `consumer_loaded` 分开记录,未实际调用 SGCLI 时后者保持 `not_run`。
|
|
24
24
|
|
|
25
|
+
### 导出阻断的确定性处理
|
|
26
|
+
|
|
27
|
+
- 先核对 CLI 路径/版本和源 SHA;当前 `.shader`、历史 `.bak`、旧 report 不可混为同一次转换证据。
|
|
28
|
+
- 新版失败返回的顶层 `data` 包含 `blockers`、`blocker_count`、`unverified_checks`、源快照与 `surface_semantics`。它是本次检测清单,不保证穷尽所有潜在问题;旧版没有这些字段时读取 `report_json`,不要猜测只有一个阻断。
|
|
29
|
+
- `ALPHA_MODULATE_TARGET_CAPABILITY_REQUIRED` 表示末端表达式不能安全折叠到 URP 的 half AlphaModulate;读取 `required_target_capabilities`,不得把 `MultiplySourceAlpha` 用于需要保留背景 Alpha 的源 Shader。
|
|
30
|
+
- 上游 Custom Function 作为目标 Custom Function 原样交接;用 `custom_function_manifest` 核对节点、精度和规范化函数载荷 SHA,不因 Agent 无法解释函数数学意义而拒绝。
|
|
31
|
+
- 已确认的 `MZGUI.MZGUI`、Tooltip/Foldout/HelpBox 只影响 Inspector 呈现,列在 `presentation_warnings`,不阻断渲染语义交接。其他未知 Custom ShaderGUI 仍可能有材质副作用并失败关闭。
|
|
32
|
+
- `retry_unchanged_input=false` 时不要重复相同导出、猜命令或找强制开关。报告缺失能力并停止;SGCLI 的新增能力必须通过公开 JSON 契约另行交接,不跨仓修补。
|
|
33
|
+
- 原生 `.shadergraph` 虽然使用 JSON 序列化,但不是 `sgcli.native.v3` 创建 spec。只有本次命令成功并核对 spec/report/receipt 完整提交与 SHA,才能称“导出成功”;这仍不等于 Editor 编译或最终效果一致已验收。
|
|
34
|
+
|
|
25
35
|
## Agent 兼容性
|
|
26
36
|
|
|
27
37
|
本 Skill 遵循通用 Agent Skills 目录结构:标准 YAML frontmatter、`SKILL.md` 正文和相对路径引用。它只要求 Agent 能运行本地 shell 命令并读取 ASECLI 的单行 JSON,不依赖 Codex、Claude Code、Cursor、Gemini CLI 或 GitHub Copilot 的专属工具名、权限模型或消息格式。
|
|
@@ -106,24 +106,24 @@ def cmd_export_sg(args) -> dict:
|
|
|
106
106
|
raise ValueError(f'resource changed before publication: {file}')
|
|
107
107
|
except ExportError as exc:
|
|
108
108
|
internal = exc.report
|
|
109
|
-
_write_failure_report(
|
|
109
|
+
failure_report = _write_failure_report(
|
|
110
110
|
source, spec_path, report_path, internal, str(exc),
|
|
111
111
|
source_raw=source_raw, schema_status=schema_status,
|
|
112
112
|
)
|
|
113
113
|
raise CliError(
|
|
114
114
|
"SG_EXPORT_BLOCKED",
|
|
115
115
|
str(exc),
|
|
116
|
-
|
|
116
|
+
_failure_details(report_path, internal, failure_report),
|
|
117
117
|
) from exc
|
|
118
118
|
except (OSError, ValueError, RuntimeError, json.JSONDecodeError) as exc:
|
|
119
|
-
_write_failure_report(
|
|
119
|
+
failure_report = _write_failure_report(
|
|
120
120
|
source, spec_path, report_path, internal, str(exc),
|
|
121
121
|
source_raw=source_raw, schema_status=schema_status,
|
|
122
122
|
)
|
|
123
123
|
raise CliError(
|
|
124
124
|
"SG_EXPORT_BLOCKED",
|
|
125
125
|
str(exc),
|
|
126
|
-
|
|
126
|
+
_failure_details(report_path, internal, failure_report),
|
|
127
127
|
) from exc
|
|
128
128
|
write_result = _write_pair(spec_path, spec, report_path, report)
|
|
129
129
|
return {
|
|
@@ -175,7 +175,7 @@ def _write_failure_report(
|
|
|
175
175
|
*,
|
|
176
176
|
source_raw: bytes,
|
|
177
177
|
schema_status: str,
|
|
178
|
-
) ->
|
|
178
|
+
) -> dict:
|
|
179
179
|
if report_path.exists():
|
|
180
180
|
raise CliError("WRITE_CONFLICT", f"output already exists: {report_path}")
|
|
181
181
|
report = build_public_report(
|
|
@@ -189,6 +189,35 @@ def _write_failure_report(
|
|
|
189
189
|
producer_schema_validated=schema_status,
|
|
190
190
|
)
|
|
191
191
|
_write_new(report_path, report)
|
|
192
|
+
return report
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _failure_details(report_path: Path, internal: dict | None, report: dict) -> dict:
|
|
196
|
+
"""Agent-facing diagnostics, separate from the fixed consumer report schema."""
|
|
197
|
+
internal = internal or {}
|
|
198
|
+
blockers = [dict(item, classification="semantic") for item in internal.get("diagnostics", [])]
|
|
199
|
+
blockers.extend(dict(item) for item in internal.get("degradations", []))
|
|
200
|
+
warnings = [dict(item) for item in internal.get("presentation_warnings", [])]
|
|
201
|
+
return {
|
|
202
|
+
"report_json": str(report_path),
|
|
203
|
+
"spec_written": False,
|
|
204
|
+
"receipt_written": False,
|
|
205
|
+
"source": report["source"],
|
|
206
|
+
"blockers": blockers,
|
|
207
|
+
"blocker_count": len(blockers),
|
|
208
|
+
"blocker_scope": "detected_in_this_run_not_exhaustive",
|
|
209
|
+
"presentation_warnings": warnings,
|
|
210
|
+
"warning_count": len(warnings),
|
|
211
|
+
"custom_function_manifest": internal.get("custom_function_manifest", []),
|
|
212
|
+
"required_target_capabilities": (internal.get("surface_semantics") or {}).get(
|
|
213
|
+
"required_target_capabilities"
|
|
214
|
+
),
|
|
215
|
+
"unverified_checks": [name for name, check in report["checks"].items()
|
|
216
|
+
if check["status"] != "verified"],
|
|
217
|
+
"surface_semantics": internal.get("surface_semantics"),
|
|
218
|
+
"retry_unchanged_input": False,
|
|
219
|
+
"next_action": "Inspect all blockers and report evidence; do not force export, substitute a .shadergraph, or edit the source to bypass validation",
|
|
220
|
+
}
|
|
192
221
|
|
|
193
222
|
|
|
194
223
|
def _assert_source_unchanged(source: Path, snapshot: bytes, phase: str) -> bytes:
|
|
@@ -8,6 +8,7 @@ from pathlib import Path
|
|
|
8
8
|
from typing import Any
|
|
9
9
|
|
|
10
10
|
from .report_semantics import check_evidence
|
|
11
|
+
from .report_presentation import presentation_degradations
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
CHECK_RULES = {
|
|
@@ -76,6 +77,7 @@ def build_public_report(
|
|
|
76
77
|
"blend_equation": "verified" if blend_passed else "unknown",
|
|
77
78
|
}
|
|
78
79
|
internal_degradations = (internal or {}).get('degradations', [])
|
|
80
|
+
presentation_warnings = (internal or {}).get('presentation_warnings', [])
|
|
79
81
|
if internal_degradations:
|
|
80
82
|
check_status['properties'] = 'unsupported'
|
|
81
83
|
checks = {
|
|
@@ -125,6 +127,7 @@ def build_public_report(
|
|
|
125
127
|
+ str(item.get('impact', 'Inspector behavior is unproven'))
|
|
126
128
|
+ '; details=' + json.dumps(item.get('details', []), ensure_ascii=False),
|
|
127
129
|
})
|
|
130
|
+
degradations.extend(presentation_degradations(presentation_warnings, source, spec_path))
|
|
128
131
|
if failure:
|
|
129
132
|
degradations.append({
|
|
130
133
|
"rule": "SEM-REPORT-001",
|
|
@@ -13,13 +13,12 @@ from .model import (
|
|
|
13
13
|
)
|
|
14
14
|
from .nodes import convert_node
|
|
15
15
|
from .sources import compiled_properties, load_asset_map
|
|
16
|
-
from .evidence import inspector_degradations, record_port, validate_template_passes
|
|
16
|
+
from .evidence import inspector_degradations, record_custom_functions, record_port, validate_template_passes
|
|
17
17
|
from .surface_semantics import normalize_surface_outputs
|
|
18
18
|
from .reconciliation import (
|
|
19
19
|
groups, reconcile_shaderlab_properties, unique_properties, verify_texture_dependencies,
|
|
20
20
|
)
|
|
21
21
|
|
|
22
|
-
|
|
23
22
|
def build_candidate(
|
|
24
23
|
source_path,
|
|
25
24
|
target_project,
|
|
@@ -124,6 +123,7 @@ def build_candidate(
|
|
|
124
123
|
for node in nodes:
|
|
125
124
|
for target_id in node.source_ids:
|
|
126
125
|
edges.append(SemanticEdge(node.target_id, 0, target_id, -1))
|
|
126
|
+
record_custom_functions(nodes, report)
|
|
127
127
|
nodes, edges = normalize_surface_outputs(nodes, edges, target_spec, mappings, report, text)
|
|
128
128
|
groups_spec = groups(ase, mappings, diagnostics)
|
|
129
129
|
report["output_bindings"] = [
|
|
@@ -3,10 +3,29 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import re
|
|
6
|
+
import hashlib
|
|
7
|
+
import json
|
|
6
8
|
|
|
9
|
+
from .alpha_modulate import _strip_comments
|
|
7
10
|
from .model import MASTER_TYPE, URP_UNLIT_GUID, URP_UNLIT_PASSES, diagnostic
|
|
8
11
|
|
|
9
12
|
|
|
13
|
+
def record_custom_functions(nodes, report):
|
|
14
|
+
manifest = []
|
|
15
|
+
for node in nodes:
|
|
16
|
+
if node.target_type != "custom-function" or node.function is None:
|
|
17
|
+
continue
|
|
18
|
+
canonical = json.dumps(node.function, ensure_ascii=False, sort_keys=True,
|
|
19
|
+
separators=(",", ":")).encode("utf-8")
|
|
20
|
+
manifest.append({
|
|
21
|
+
"source_node_id": node.source_id, "target_node_id": node.target_id,
|
|
22
|
+
"function_sha256": hashlib.sha256(canonical).hexdigest(),
|
|
23
|
+
"precision": node.settings.get("precision", "Inherit"),
|
|
24
|
+
"mapping": "preserved_custom_function_payload",
|
|
25
|
+
})
|
|
26
|
+
report["custom_function_manifest"] = manifest
|
|
27
|
+
|
|
28
|
+
|
|
10
29
|
def validate_template_passes(ase, master, diagnostics, report):
|
|
11
30
|
masters = [node for node in ase.graph.nodes if node.type_name == MASTER_TYPE]
|
|
12
31
|
passes = {node.raw_fields[15] for node in masters if len(node.raw_fields) > 15}
|
|
@@ -38,18 +57,39 @@ def inspector_degradations(ase, properties, report):
|
|
|
38
57
|
match = re.search(rf"(?m)^\s*(?P<attrs>(?:\[[^\]\r\n]*\]\s*)*){re.escape(name)}\s*\(", ase.prefix)
|
|
39
58
|
present = [kind for kind in attributes if match and f"[{kind}" in match.group("attrs")]
|
|
40
59
|
if present:
|
|
41
|
-
report["
|
|
60
|
+
report["presentation_warnings"].append({
|
|
42
61
|
"source": {"property": name}, "code": "INSPECTOR_METADATA_NOT_MIGRATED",
|
|
43
62
|
"details": present, "impact": "Property data is preserved; ASE Inspector UI behavior is not migrated",
|
|
63
|
+
"classification": "presentation_only",
|
|
64
|
+
"source_attributes": match.group("attrs").strip(),
|
|
65
|
+
"material_side_effects": "none",
|
|
44
66
|
})
|
|
45
|
-
|
|
67
|
+
graph_gui = {node.raw_fields[9] for node in ase.graph.nodes
|
|
46
68
|
if node.type_name == MASTER_TYPE and len(node.raw_fields) > 9
|
|
47
69
|
and node.raw_fields[6] == "True"
|
|
48
|
-
and node.raw_fields[9] not in {"", "UnityEditor.ShaderGraphUnlitGUI"}}
|
|
49
|
-
|
|
70
|
+
and node.raw_fields[9] not in {"", "UnityEditor.ShaderGraphUnlitGUI"}}
|
|
71
|
+
# Compiled ShaderLab may disagree with the ASE graph. Neither declaration
|
|
72
|
+
# can be ignored when an unknown GUI can alter keywords/material state.
|
|
73
|
+
shaderlab = ase.prefix.rsplit("/*ASEBEGIN", 1)[0]
|
|
74
|
+
compiled_gui = set(re.findall(r'\bCustomEditor\s+"([^"]+)"', _strip_comments(shaderlab) or ""))
|
|
75
|
+
custom_gui = (graph_gui | compiled_gui) - {"", "UnityEditor.ShaderGraphUnlitGUI"}
|
|
76
|
+
known_presentation = sorted(custom_gui & {"MZGUI.MZGUI"})
|
|
77
|
+
unknown_gui = sorted(custom_gui - {"MZGUI.MZGUI"})
|
|
78
|
+
if known_presentation:
|
|
79
|
+
report["presentation_warnings"].append({
|
|
80
|
+
"source": {"file": str(report["source"]["path"])},
|
|
81
|
+
"code": "CUSTOM_INSPECTOR_PRESENTATION_NOT_MIGRATED",
|
|
82
|
+
"details": known_presentation,
|
|
83
|
+
"classification": "presentation_only",
|
|
84
|
+
"material_side_effects": "none",
|
|
85
|
+
"impact": "MZGUI Inspector styling is not migrated; Shader properties and runtime graph semantics are unchanged",
|
|
86
|
+
})
|
|
87
|
+
if unknown_gui:
|
|
50
88
|
report["degradations"].append({
|
|
51
89
|
"source": {"file": str(report["source"]["path"])}, "code": "CUSTOM_INSPECTOR_NOT_MIGRATED",
|
|
52
|
-
"details":
|
|
90
|
+
"details": unknown_gui,
|
|
91
|
+
"classification": "custom_shader_gui",
|
|
92
|
+
"material_side_effects": "unproven",
|
|
53
93
|
"impact": "ShaderGUI behavior has no equivalent in the creation specification",
|
|
54
94
|
})
|
|
55
95
|
if report["degradations"]:
|
|
@@ -78,7 +78,8 @@ def report_template(source: Path, target: Path, version: str, source_raw: bytes)
|
|
|
78
78
|
"mapping_version": MAPPING_VERSION,
|
|
79
79
|
"receiver_schema": {"name": "sgcli.native.v3", "path": None, "sha256": None},
|
|
80
80
|
"source_validation": {"error_count": 0, "warning_count": 0, "issues": []},
|
|
81
|
-
"mappings": [], "dependencies": [], "degradations": [], "
|
|
81
|
+
"mappings": [], "dependencies": [], "degradations": [], "presentation_warnings": [],
|
|
82
|
+
"custom_function_manifest": [], "diagnostics": [],
|
|
82
83
|
"equivalence": {"status": "not_proven", "reason": "effect validation has not run"},
|
|
83
84
|
"verification": {
|
|
84
85
|
"source_parse": dict(empty), "source_structure": dict(empty),
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Map non-rendering Inspector differences into public evidence gaps."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def presentation_degradations(items, source, target):
|
|
7
|
+
result = []
|
|
8
|
+
for item in items:
|
|
9
|
+
location = item.get("source") or {}
|
|
10
|
+
if isinstance(location, dict):
|
|
11
|
+
location = str(location.get("file") or source) + (
|
|
12
|
+
"#" + str(location["property"]) if "property" in location else ""
|
|
13
|
+
)
|
|
14
|
+
result.append({
|
|
15
|
+
"rule": "SEM-PRESENTATION-001", "category": "evidence_gap",
|
|
16
|
+
"source": str(location or source), "target": str(target),
|
|
17
|
+
"reason": str(item.get("code", "PRESENTATION_NOT_MIGRATED")) + ": "
|
|
18
|
+
+ str(item.get("impact", "Inspector presentation is not migrated"))
|
|
19
|
+
+ "; details=" + json.dumps(item.get("details", []), ensure_ascii=False),
|
|
20
|
+
})
|
|
21
|
+
return result
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from typing import Any
|
|
6
|
+
import json
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
_VERIFIED_EVIDENCE = {
|
|
@@ -17,6 +18,7 @@ _VERIFIED_EVIDENCE = {
|
|
|
17
18
|
_SEMANTIC_CODES = {
|
|
18
19
|
"ALPHA_BLEND_UNREPRESENTABLE", "PASS_BLEND_UNPROVEN",
|
|
19
20
|
"DUPLICATE_ALPHA_MODULATE_UNRESOLVED", "ALPHA_MODULATE_UNPROVEN",
|
|
21
|
+
"ALPHA_MODULATE_TARGET_CAPABILITY_REQUIRED",
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
|
|
@@ -28,9 +30,9 @@ def check_evidence(
|
|
|
28
30
|
return _VERIFIED_EVIDENCE[name] + details
|
|
29
31
|
diagnostics = internal.get("diagnostics", []) if isinstance(internal, dict) else []
|
|
30
32
|
codes = _SEMANTIC_CODES if name in {"alpha_equation", "blend_equation"} else set()
|
|
31
|
-
relevant =
|
|
33
|
+
relevant = [item for item in diagnostics if item.get("code") in codes]
|
|
32
34
|
if relevant:
|
|
33
|
-
return
|
|
35
|
+
return json.dumps(relevant, ensure_ascii=False, sort_keys=True) + details
|
|
34
36
|
if failure:
|
|
35
37
|
return f"conversion stopped before this invariant was proved: {failure}" + details
|
|
36
38
|
if diagnostics:
|
|
@@ -49,6 +51,7 @@ def _blend_details(name: str, internal: dict[str, Any] | None) -> str:
|
|
|
49
51
|
rewrites = semantics.get("rewrites") or []
|
|
50
52
|
matcher = (
|
|
51
53
|
"unresolved" if semantics.get("unresolved")
|
|
54
|
+
or semantics.get("fold_precision", {}).get("status") == "unproven"
|
|
52
55
|
else "folded" if rewrites
|
|
53
56
|
else "preserved_graph_float_expression"
|
|
54
57
|
if semantics.get("alpha_modulate_strategy") == "preserve_graph_float_expression"
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from .alpha_modulate import inputs as alpha_modulate_inputs
|
|
6
5
|
from .model import SemanticEdge, SemanticNode, diagnostic
|
|
7
6
|
|
|
8
7
|
|
|
@@ -39,7 +38,7 @@ def _unproven_modulation(report: dict, semantics: dict, node: SemanticNode) -> N
|
|
|
39
38
|
report.setdefault("diagnostics", []).append(diagnostic(
|
|
40
39
|
node.source_id,
|
|
41
40
|
"ALPHA_MODULATE_UNPROVEN",
|
|
42
|
-
reason,
|
|
41
|
+
{"message": reason, "function": node.function, "precision": node.settings.get("precision", "Inherit")},
|
|
43
42
|
"The exporter cannot prove that target AlphaModulate will execute exactly once",
|
|
44
43
|
))
|
|
45
44
|
|
|
@@ -134,36 +133,3 @@ def _white_value(value) -> bool:
|
|
|
134
133
|
|
|
135
134
|
def _one(value) -> bool:
|
|
136
135
|
return isinstance(value, (int, float)) and not isinstance(value, bool) and float(value) == 1.0
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
def _first_upstream_custom_function(
|
|
140
|
-
nodes: list[SemanticNode], edges: list[SemanticEdge], source_id: str
|
|
141
|
-
) -> SemanticNode | None:
|
|
142
|
-
by_id = {node.target_id: node for node in nodes}
|
|
143
|
-
incoming: dict[str, list[str]] = {}
|
|
144
|
-
for edge in edges:
|
|
145
|
-
incoming.setdefault(edge.target_id, []).append(edge.source_id)
|
|
146
|
-
pending = [source_id]
|
|
147
|
-
visited: set[str] = set()
|
|
148
|
-
while pending:
|
|
149
|
-
current = pending.pop()
|
|
150
|
-
if current in visited:
|
|
151
|
-
continue
|
|
152
|
-
visited.add(current)
|
|
153
|
-
node = by_id.get(current)
|
|
154
|
-
if node is not None and node.target_type == "custom-function":
|
|
155
|
-
return node
|
|
156
|
-
pending.extend(reversed(incoming.get(current, [])))
|
|
157
|
-
return None
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
def _reject_upstream_function(report: dict, semantics: dict, node: SemanticNode) -> None:
|
|
161
|
-
if alpha_modulate_inputs(node.function) is not None:
|
|
162
|
-
_unresolved_modulation(
|
|
163
|
-
report,
|
|
164
|
-
semantics,
|
|
165
|
-
node,
|
|
166
|
-
"AlphaModulate remains upstream of the Multiply BaseColor output",
|
|
167
|
-
)
|
|
168
|
-
else:
|
|
169
|
-
_unproven_modulation(report, semantics, node)
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""Conservative precision proof for moving graph arithmetic into URP."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
from .alpha_modulate import _strip_comments
|
|
6
|
+
from .model import diagnostic
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def fold_precision_verified(node, nodes, edges, report, semantics):
|
|
10
|
+
"""Do not replace float/inherited lerp with URP's half3/half overload.
|
|
11
|
+
|
|
12
|
+
Custom Function parameters establish their own half conversion boundary.
|
|
13
|
+
Native lerp additionally needs explicitly half operands (no mixed promotion).
|
|
14
|
+
This is a narrow proof, not an assertion that half equals float on every GPU.
|
|
15
|
+
"""
|
|
16
|
+
precision = node.settings.get("precision", "Inherit")
|
|
17
|
+
effective_precision = precision
|
|
18
|
+
verified = precision == "Half"
|
|
19
|
+
body = None
|
|
20
|
+
if node.target_type == "custom-function":
|
|
21
|
+
body = _strip_comments((node.function or {}).get("body", ""))
|
|
22
|
+
if body and re.search(r"\bfloat(?:[1-4])?\s*\(", body):
|
|
23
|
+
effective_precision = "Single"
|
|
24
|
+
elif body and re.search(r"\bhalf(?:[1-4])?\s*\(", body):
|
|
25
|
+
effective_precision = "Half"
|
|
26
|
+
verified = verified and body is not None and bool(re.search(r"\bhalf3\s*\(", body))
|
|
27
|
+
else:
|
|
28
|
+
by_id = {item.target_id: item for item in nodes}
|
|
29
|
+
operands = [by_id.get(edge.source_id) for edge in edges if edge.target_id == node.target_id]
|
|
30
|
+
verified = verified and bool(operands) and all(
|
|
31
|
+
item is not None and item.settings.get("precision") == "Half" for item in operands
|
|
32
|
+
)
|
|
33
|
+
proof = {
|
|
34
|
+
"source_node_id": node.source_id,
|
|
35
|
+
"source_node_precision": precision,
|
|
36
|
+
"source_effective_precision": effective_precision,
|
|
37
|
+
"source_expression": body,
|
|
38
|
+
"target_precision": "Half",
|
|
39
|
+
"target_function": "half3 AlphaModulate(half3 albedo, half alpha)",
|
|
40
|
+
"status": "verified" if verified else "unproven",
|
|
41
|
+
}
|
|
42
|
+
semantics["fold_precision"] = proof
|
|
43
|
+
if not verified:
|
|
44
|
+
requirement = {
|
|
45
|
+
"surface": semantics.get("source", {}).get("surface"),
|
|
46
|
+
"rgb_blend": (semantics.get("source_pass_blend") or {}).get("rgb"),
|
|
47
|
+
"alpha_blend": (semantics.get("source_pass_blend") or {}).get("alpha"),
|
|
48
|
+
"explicit_alpha_modulate": {
|
|
49
|
+
"preserve_in_graph": True,
|
|
50
|
+
"precision": effective_precision,
|
|
51
|
+
"source_node_id": node.source_id,
|
|
52
|
+
},
|
|
53
|
+
"implicit_alpha_modulate": False,
|
|
54
|
+
}
|
|
55
|
+
semantics["required_target_capabilities"] = requirement
|
|
56
|
+
semantics["unresolved"] = {
|
|
57
|
+
"rule": "SEM-BLEND-001",
|
|
58
|
+
"source_node": node.source_id,
|
|
59
|
+
"reason": "target cannot preserve explicit AlphaModulate precision while disabling its implicit AlphaModulate",
|
|
60
|
+
}
|
|
61
|
+
report.setdefault("diagnostics", []).append(diagnostic(
|
|
62
|
+
node.source_id, "ALPHA_MODULATE_TARGET_CAPABILITY_REQUIRED",
|
|
63
|
+
{"precision_proof": proof, "required_target_capabilities": requirement},
|
|
64
|
+
"The current public SG target contract cannot preserve this graph expression and blend equation without adding a second half-precision AlphaModulate",
|
|
65
|
+
))
|
|
66
|
+
return verified
|
|
@@ -12,13 +12,13 @@ from __future__ import annotations
|
|
|
12
12
|
from .alpha_modulate import inputs as alpha_modulate_inputs
|
|
13
13
|
from .model import SemanticEdge, SemanticNode, diagnostic
|
|
14
14
|
from .pass_blend import record_pass_blend
|
|
15
|
+
from .surface_precision import fold_precision_verified
|
|
15
16
|
from .surface_matching import (
|
|
16
|
-
_first_upstream_custom_function,
|
|
17
17
|
_input_port,
|
|
18
18
|
_native_alpha_modulate,
|
|
19
|
-
_reject_upstream_function,
|
|
20
19
|
_single_edge_to,
|
|
21
20
|
_target_alpha_modulates,
|
|
21
|
+
_through_reroutes,
|
|
22
22
|
_unproven_modulation,
|
|
23
23
|
_unresolved_modulation,
|
|
24
24
|
)
|
|
@@ -95,9 +95,8 @@ def normalize_surface_outputs(
|
|
|
95
95
|
native = _native_alpha_modulate(nodes, edges, color_edge, alpha_edge)
|
|
96
96
|
if native is not None:
|
|
97
97
|
modulate, color_input, alpha_input, route_nodes, white_node = native
|
|
98
|
-
|
|
99
|
-
if
|
|
100
|
-
_reject_upstream_function(report, semantics, upstream)
|
|
98
|
+
precision_ok = fold_precision_verified(modulate, nodes, edges, report, semantics)
|
|
99
|
+
if not precision_ok:
|
|
101
100
|
return nodes, edges
|
|
102
101
|
replacement = SemanticEdge(
|
|
103
102
|
color_input.source_id,
|
|
@@ -152,6 +151,10 @@ def normalize_surface_outputs(
|
|
|
152
151
|
row["target_nodes"] = [] if removed else row.get("target_nodes", [])
|
|
153
152
|
row["rule"] = "SEM-BLEND-001"
|
|
154
153
|
return nodes, rewritten
|
|
154
|
+
source_edge, route_nodes = _through_reroutes(color_edge, by_id, edges)
|
|
155
|
+
node = by_id.get(source_edge.source_id)
|
|
156
|
+
if node is None:
|
|
157
|
+
return nodes, edges
|
|
155
158
|
matched = (
|
|
156
159
|
alpha_modulate_inputs(node.function)
|
|
157
160
|
if node.target_type == "custom-function"
|
|
@@ -160,10 +163,6 @@ def normalize_surface_outputs(
|
|
|
160
163
|
if matched is None:
|
|
161
164
|
if node.target_type == "custom-function":
|
|
162
165
|
_unproven_modulation(report, semantics, node)
|
|
163
|
-
else:
|
|
164
|
-
upstream = _first_upstream_custom_function(nodes, edges, node.target_id)
|
|
165
|
-
if upstream is not None:
|
|
166
|
-
_reject_upstream_function(report, semantics, upstream)
|
|
167
166
|
return nodes, edges
|
|
168
167
|
color_input_name, alpha_input_name = matched
|
|
169
168
|
color_port = _input_port(node, color_input_name)
|
|
@@ -186,9 +185,8 @@ def normalize_surface_outputs(
|
|
|
186
185
|
"AlphaModulate alpha input differs from Surface Alpha",
|
|
187
186
|
)
|
|
188
187
|
return nodes, edges
|
|
189
|
-
|
|
190
|
-
if
|
|
191
|
-
_reject_upstream_function(report, semantics, upstream)
|
|
188
|
+
precision_ok = fold_precision_verified(node, nodes, edges, report, semantics)
|
|
189
|
+
if not precision_ok:
|
|
192
190
|
return nodes, edges
|
|
193
191
|
replacement = SemanticEdge(
|
|
194
192
|
color_input.source_id,
|
|
@@ -197,16 +195,16 @@ def normalize_surface_outputs(
|
|
|
197
195
|
color_edge.target_port,
|
|
198
196
|
)
|
|
199
197
|
rewritten = [replacement if edge == color_edge else edge for edge in edges]
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
removed
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
nodes = [item for item in nodes if item.target_id
|
|
208
|
-
|
|
209
|
-
|
|
198
|
+
removable_ids = {node.target_id, *(item.target_id for item in route_nodes)}
|
|
199
|
+
external_consumers = [edge for edge in rewritten
|
|
200
|
+
if edge.source_id in removable_ids and edge.target_id not in removable_ids]
|
|
201
|
+
removed = not external_consumers
|
|
202
|
+
if removed:
|
|
203
|
+
rewritten = [edge for edge in rewritten
|
|
204
|
+
if edge.source_id not in removable_ids and edge.target_id not in removable_ids]
|
|
205
|
+
nodes = [item for item in nodes if item.target_id not in removable_ids]
|
|
206
|
+
for item in [node, *route_nodes]:
|
|
207
|
+
mappings.pop(item.source_id, None)
|
|
210
208
|
|
|
211
209
|
semantics["source"].update({
|
|
212
210
|
"explicit_alpha_modulate": True,
|