sdr-visualizer 1.2.7__tar.gz → 1.2.9__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.
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/CHANGELOG.md +92 -1
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/PKG-INFO +21 -5
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/README.md +19 -4
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/docs/ADAPTER_GUIDE.md +52 -4
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/docs/PERFORMANCE.md +4 -1
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/docs/PRODUCT_CONTRACT.md +6 -3
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/docs/RELEASING.md +1 -1
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/pyproject.toml +4 -1
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/scripts/dependabot_review.py +43 -9
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/scripts/perf_browser_check.py +10 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/__init__.py +1 -1
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/adapters/aa.py +75 -28
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/adapters/cja.py +86 -34
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/cli/lineage_output.py +7 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/cli/main.py +5 -5
- sdr_visualizer-1.2.9/src/sdr_visualizer/cli/output_safety.py +61 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/cli/workspace_usage.py +2 -4
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/core/structure_limits.py +3 -1
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/core/workspace_usage.py +2 -1
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/input/detect.py +2 -1
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/input/loader.py +38 -8
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/input/series.py +2 -2
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/input/shell_out.py +16 -3
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_adapters_aa.py +1 -1
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_adapters_cja.py +43 -3
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_browser_functional.py +22 -12
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_cli.py +2 -2
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_dependabot_review.py +127 -4
- sdr_visualizer-1.2.9/tests/test_input_hardening.py +407 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_input_modes.py +3 -2
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_lineage_cli.py +25 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_output_safety.py +133 -2
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_structure_limits.py +20 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_workspace_usage_cli.py +54 -5
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_workspace_usage_collection.py +6 -0
- sdr_visualizer-1.2.7/docs/ADAPTER_CORRECTNESS_1.1.2.md +0 -114
- sdr_visualizer-1.2.7/docs/DEPENDABOT_AUTOMATION.md +0 -116
- sdr_visualizer-1.2.7/docs/RELEASE_EVIDENCE_TEMPLATE.md +0 -386
- sdr_visualizer-1.2.7/src/sdr_visualizer/cli/output_safety.py +0 -28
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/.gitignore +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/AGENTS.md +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/CODE_OF_CONDUCT.md +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/CONTRIBUTING.md +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/LICENSE +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/SECURITY.md +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/THIRD_PARTY_LICENSES +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/docs/ARCHITECTURE.md +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/docs/EMBEDDED_DATA_FORMAT.md +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/docs/LINEAGE.md +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/docs/SHOWCASE.md +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/docs/WORKSPACE_USAGE.md +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/docs/payload-schema.json +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/docs/workspace-usage-schema.json +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/scripts/capture_screenshot.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/scripts/check_color_pack_parity.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/scripts/check_markdown_links.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/scripts/check_workflow_policy.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/scripts/corpus_check.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/scripts/generate_aa_large_fixture.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/scripts/generate_cja_lineage_poc.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/scripts/generate_examples.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/scripts/generate_large_fixture.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/scripts/mutate_fixture.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/scripts/package_smoke_check.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/scripts/perf_check.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/scripts/perf_lineage_poc.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/scripts/workspace_usage_fixture.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/__main__.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/adapters/__init__.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/adapters/base.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/adapters/cja_lineage.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/analysis/__init__.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/analysis/diff.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/analysis/formula_tree.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/analysis/lineage_layout.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/analysis/references.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/analysis/segment_tree.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/analysis/trend.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/cli/__init__.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/cli/exit_codes.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/cli/lineage.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/core/__init__.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/core/exceptions.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/core/lineage.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/core/models.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/core/visualizer.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/input/__init__.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/input/json_io.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/input/lineage_discovery.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/input/workspace_usage.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/render/__init__.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/render/color_packs.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/render/data_payload.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/render/lineage_payload.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/render/lineage_renderer.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/render/renderer.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/render/static/cja_lineage.css +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/render/static/cja_lineage.js +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/render/static/cja_lineage_graph.js +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/render/static/cja_lineage_roles.js +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/render/static/d3.min.js +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/render/static/visualizer.css +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/render/static/visualizer.js +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/render/static/visualizer_trend.js +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/render/templates/catalog.html.j2 +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/render/templates/changes.html.j2 +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/render/templates/cja_lineage.html.j2 +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/render/templates/graph.html.j2 +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/render/templates/index.html.j2 +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/render/templates/trend.html.j2 +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/render/trend_charts.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/render/workspace_usage.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/usage/__init__.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/usage/aa_api.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/usage/aa_sdk.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/usage/auth.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/usage/cja_api.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/usage/cja_sdk.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/usage/collector.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/usage/normalize.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/usage/sdk_worker.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/src/sdr_visualizer/usage/transport.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/__init__.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/adapter_cases.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/browser_support.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/conftest.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/fixtures/__init__.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/fixtures/aa_snapshot_clean.json +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/fixtures/aa_snapshot_messy.json +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/fixtures/cja_lineage_degraded.json +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/fixtures/cja_lineage_enriched.json +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/fixtures/cja_lineage_hostile.json +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/fixtures/cja_lineage_shared.json +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/fixtures/cja_snapshot_clean.json +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/fixtures/cja_snapshot_hostile.json +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/fixtures/cja_snapshot_messy.json +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/fixtures/cja_snapshot_minimal.json +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/fixtures/workspace_usage_aa_all_types.json +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/fixtures/workspace_usage_cja_all_types.json +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_adapter_correctness.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_adapter_fuzz.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_analysis_diff.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_analysis_formula_tree.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_analysis_references.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_analysis_segment_tree.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_analysis_trend.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_browser_support.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_cja_lineage.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_cja_lineage_driver.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_cli_flags.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_color_pack_parity.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_color_packs.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_corpus_check.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_data_payload.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_entrypoint.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_generate_examples.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_input_series.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_lineage_browser.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_lineage_discovery.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_lineage_performance.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_lineage_renderer.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_markdown_links.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_minimal_fixture.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_mutate_fixture.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_package_smoke_check.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_payload_schema.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_perf.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_perf_browser_check.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_release_documentation.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_renderer.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_renderer_anatomy.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_renderer_graph.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_semantic_properties.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_trend_charts.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_workflow_policy.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_workspace_usage.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_workspace_usage_aa_sdk.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_workspace_usage_auth.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_workspace_usage_cja_sdk.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_workspace_usage_offline.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_workspace_usage_payload.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_workspace_usage_perf.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_workspace_usage_sdk.py +0 -0
- {sdr_visualizer-1.2.7 → sdr_visualizer-1.2.9}/tests/test_workspace_usage_transport.py +0 -0
|
@@ -4,6 +4,95 @@ All notable changes to `sdr-visualizer` will be documented here. The format foll
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [1.2.9] - 2026-11-12
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- State in the README and on the project site that sdr-visualizer is not
|
|
12
|
+
affiliated with Adobe, and add a trademark notice.
|
|
13
|
+
- Replace the README's "public preview" label with a plain "Scope and limits"
|
|
14
|
+
section, and mark the package as production/stable on PyPI. The 1.x semantic
|
|
15
|
+
versioning promise is unchanged.
|
|
16
|
+
- Remove the 1.1.2 adapter review record from `docs/`. Its evidence stays in
|
|
17
|
+
pull request #41.
|
|
18
|
+
- Leave the maintainer-only Dependabot and release evidence documents out of
|
|
19
|
+
the source distribution.
|
|
20
|
+
- Maintainer tooling only: add a version-sync check and README badge, warm up
|
|
21
|
+
the browser before the first timed report in the performance gate, and
|
|
22
|
+
update the pytest and hypothesis development dependencies.
|
|
23
|
+
|
|
24
|
+
## [1.2.8] - 2026-10-31
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- Treat a number too large to convert, such as a 400-digit
|
|
29
|
+
`complexity_score` or an infinite segment `nesting_depth`, as invalid input
|
|
30
|
+
(exit 3) instead of an unexpected error (exit 1). `--trend` skips such a
|
|
31
|
+
snapshot with a warning instead of stopping.
|
|
32
|
+
- Treat an `--at` value that its UTC offset pushes outside the supported date
|
|
33
|
+
range, and standard input that is not UTF-8, as invalid input (exit 3)
|
|
34
|
+
instead of an unexpected error. The stdin change matches sdr-grader.
|
|
35
|
+
- Skip a `*.json` entry whose file metadata cannot be read, such as a broken
|
|
36
|
+
symlink, when a directory is ordered by modification time. It no longer
|
|
37
|
+
stops the run, and it is still protected from being overwritten by an output.
|
|
38
|
+
- Stop error messages from repeating snapshot content. A record without an ID
|
|
39
|
+
is now reported by its list and position, such as `CJA metrics[3]`, instead
|
|
40
|
+
of by printing the whole record. Number errors name the value's type, not
|
|
41
|
+
the value. A failed live run reports the generator's exit status but no
|
|
42
|
+
longer prints its error output. These match sdr-grader.
|
|
43
|
+
- Leave out an AA calculated metric's `attribution` or `allocation` when it is
|
|
44
|
+
not a string, so the embedded payload and `--json` sidecar match
|
|
45
|
+
`docs/payload-schema.json`.
|
|
46
|
+
- Reject an HTML and `--json` destination pair whose names differ only in
|
|
47
|
+
letter case or Unicode normalization, in the same directory. On a
|
|
48
|
+
case-insensitive filesystem, such as the macOS default, the JSON used to
|
|
49
|
+
replace the report. A Workspace usage output or config path that reaches a
|
|
50
|
+
snapshot directory through a different letter case is now also rejected.
|
|
51
|
+
- Print "Workspace artifact writing interrupted" when Ctrl-C stops usage file
|
|
52
|
+
writing; the message was empty.
|
|
53
|
+
- Drop null, number, boolean and empty-string values from a component's tags
|
|
54
|
+
instead of turning them into tags such as `"None"` or `"7"`. Tag objects
|
|
55
|
+
still contribute their `name`, or else their `id`. This matches sdr-grader.
|
|
56
|
+
- Reject a definition, tag list or reference list whose embedded JSON repeats
|
|
57
|
+
a key within one object, as invalid input (exit 3), instead of keeping the
|
|
58
|
+
last value. Snapshot files already reject this. This matches sdr-grader.
|
|
59
|
+
- Report a snapshot directory that cannot be read as an input error. Before,
|
|
60
|
+
it looked like an empty directory. This matches sdr-grader.
|
|
61
|
+
- Decode standard input as UTF-8 whatever the terminal's locale encoding is.
|
|
62
|
+
This matches sdr-grader.
|
|
63
|
+
- Reject an empty `--dataview` or `--rsid` ID, or one that starts with `-`,
|
|
64
|
+
before running the generator (exit 3). The generator would read such a value
|
|
65
|
+
as an option. This matches sdr-grader.
|
|
66
|
+
- Detect an AA snapshot from the same `report_suite` key the AA adapter reads.
|
|
67
|
+
This matches sdr-grader.
|
|
68
|
+
- Treat a NaN or infinite component, derived field, calculated metric or
|
|
69
|
+
segment name, a component type, or an AA report suite name as missing,
|
|
70
|
+
instead of showing "nan". Spreadsheet-based exports write NaN for an empty
|
|
71
|
+
cell. This matches sdr-grader.
|
|
72
|
+
- `cja-lineage` now checks that the output's parent directory exists before
|
|
73
|
+
reading discovery input, and exits 3 as LINEAGE.md documents. It used to
|
|
74
|
+
exit 1 after discovery had already run.
|
|
75
|
+
|
|
76
|
+
### Changed
|
|
77
|
+
|
|
78
|
+
- Advance the AA tested-through generator marker to `aa_auto_sdr` 1.24.6.
|
|
79
|
+
Snapshots that declare that version no longer print a newer-than-tested
|
|
80
|
+
warning. The CJA marker stays at `cja_auto_sdr` 3.13.6, the current release.
|
|
81
|
+
- Build reports faster. The check for invalid Unicode in snapshot text now
|
|
82
|
+
uses the same compiled pattern as sdr-grader. Median build time is 39% to
|
|
83
|
+
46% lower across the synthetic performance tiers. Output is byte-identical.
|
|
84
|
+
- Maintainer tooling only: Dependabot auto-merge now also covers `pytest-xdist`
|
|
85
|
+
patch updates and `hypothesis` minor and patch updates. Every automatically
|
|
86
|
+
merged release must be at least 3 days old on PyPI, and Dependabot waits 3
|
|
87
|
+
days before opening `uv` update PRs.
|
|
88
|
+
- Maintainer tooling only: `uv.lock` now uses lockfile revision 5, which is the
|
|
89
|
+
format Dependabot writes, so Dependabot lockfile PRs no longer fail the
|
|
90
|
+
policy's settings check. No package versions changed.
|
|
91
|
+
- Maintainer tooling only: make tests independent of pytest 9.2.0 internals
|
|
92
|
+
and of machine load. One test no longer fakes `os.fstat` for the whole
|
|
93
|
+
process, two collection tests no longer depend on interpreter startup time,
|
|
94
|
+
and browser tests wait for results instead of sleeping.
|
|
95
|
+
|
|
7
96
|
## [1.2.7] - 2026-10-30
|
|
8
97
|
|
|
9
98
|
### Changed
|
|
@@ -815,7 +904,9 @@ The following are explicitly internal and may change without notice:
|
|
|
815
904
|
- Workspace project visualization (v0.4)
|
|
816
905
|
- Schema map view (v0.5)
|
|
817
906
|
|
|
818
|
-
[Unreleased]: https://github.com/brian-a-au/sdr-visualizer/compare/v1.2.
|
|
907
|
+
[Unreleased]: https://github.com/brian-a-au/sdr-visualizer/compare/v1.2.9...HEAD
|
|
908
|
+
[1.2.9]: https://github.com/brian-a-au/sdr-visualizer/releases/tag/v1.2.9
|
|
909
|
+
[1.2.8]: https://github.com/brian-a-au/sdr-visualizer/releases/tag/v1.2.8
|
|
819
910
|
[1.2.7]: https://github.com/brian-a-au/sdr-visualizer/releases/tag/v1.2.7
|
|
820
911
|
[1.2.6]: https://github.com/brian-a-au/sdr-visualizer/releases/tag/v1.2.6
|
|
821
912
|
[1.2.5]: https://github.com/brian-a-au/sdr-visualizer/releases/tag/v1.2.5
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: sdr-visualizer
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.9
|
|
4
4
|
Summary: Static-output visual catalog generator for Adobe Customer Journey Analytics and Adobe Analytics implementations.
|
|
5
5
|
Project-URL: Homepage, https://brian-a-au.github.io/sdr-visualizer/
|
|
6
6
|
Project-URL: Documentation, https://github.com/brian-a-au/sdr-visualizer#documentation
|
|
@@ -11,6 +11,7 @@ Author: Brian Au
|
|
|
11
11
|
License-Expression: MIT
|
|
12
12
|
License-File: LICENSE
|
|
13
13
|
License-File: THIRD_PARTY_LICENSES
|
|
14
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
15
|
Classifier: Environment :: Console
|
|
15
16
|
Classifier: Intended Audience :: Developers
|
|
16
17
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -35,6 +36,7 @@ Description-Content-Type: text/markdown
|
|
|
35
36
|
[](https://pypi.org/project/sdr-visualizer/)
|
|
36
37
|
[](https://github.com/brian-a-au/sdr-visualizer/actions/workflows/test.yml)
|
|
37
38
|
[](https://github.com/brian-a-au/sdr-visualizer/actions/workflows/lint.yml)
|
|
39
|
+
[](https://github.com/brian-a-au/sdr-visualizer/actions/workflows/version-sync.yml)
|
|
38
40
|
[](https://www.python.org/downloads/)
|
|
39
41
|
[](https://github.com/brian-a-au/sdr-visualizer/tree/main/tests)
|
|
40
42
|
[](https://github.com/astral-sh/ruff)
|
|
@@ -68,9 +70,12 @@ Treat it as derived from the source snapshot. Move, email, post, or attach it
|
|
|
68
70
|
only in authorized locations and in accordance with your organization's
|
|
69
71
|
confidentiality and data-handling policy.
|
|
70
72
|
|
|
71
|
-
|
|
73
|
+
sdr-visualizer is an independent open source project. It is not affiliated
|
|
74
|
+
with, endorsed by, or supported by Adobe. See [Trademarks](#trademarks).
|
|
72
75
|
|
|
73
|
-
|
|
76
|
+
## Scope and limits
|
|
77
|
+
|
|
78
|
+
A report shows what a snapshot configures. It
|
|
74
79
|
does not grade the implementation or confirm that it works. Check the details
|
|
75
80
|
against the source implementation before you act on them.
|
|
76
81
|
|
|
@@ -330,8 +335,9 @@ sidecar share one schema, published at
|
|
|
330
335
|
and validated in CI against every payload shape produced by the bundled
|
|
331
336
|
fixtures. Removing or retyping a field is major; adding optional fields is
|
|
332
337
|
minor. The `segment_trees` / `formula_trees` node internals are documented in
|
|
333
|
-
the schema as loosely specified.
|
|
334
|
-
|
|
338
|
+
the schema as loosely specified. Before each release, the maintainer also
|
|
339
|
+
checks the payload against fresh generator output and a private set of real
|
|
340
|
+
snapshots. Those snapshots contain customer data and are never published. See
|
|
335
341
|
[`docs/RELEASING.md`](https://github.com/brian-a-au/sdr-visualizer/blob/main/docs/RELEASING.md).
|
|
336
342
|
|
|
337
343
|
**CJA lineage.** `cja-lineage` adds a separate CJA-only command. Its documented
|
|
@@ -413,3 +419,13 @@ is governed by the [`CODE_OF_CONDUCT.md`](https://github.com/brian-a-au/sdr-visu
|
|
|
413
419
|
MIT — see [`LICENSE`](https://github.com/brian-a-au/sdr-visualizer/blob/main/LICENSE). The output bundles [D3](https://d3js.org) v7,
|
|
414
420
|
vendored under the ISC license; see
|
|
415
421
|
[`THIRD_PARTY_LICENSES`](https://github.com/brian-a-au/sdr-visualizer/blob/main/THIRD_PARTY_LICENSES) for the full notice.
|
|
422
|
+
|
|
423
|
+
## Trademarks
|
|
424
|
+
|
|
425
|
+
sdr-visualizer is an independent project. It is not affiliated with, endorsed
|
|
426
|
+
by, sponsored by, or supported by Adobe Inc. Adobe, Adobe Analytics, and
|
|
427
|
+
Customer Journey Analytics are trademarks or registered trademarks of Adobe
|
|
428
|
+
Inc. in the United States and other countries. This project uses those names
|
|
429
|
+
only to say which products its input comes from. For help with an Adobe
|
|
430
|
+
product, contact Adobe. For help with this tool, open an issue in this
|
|
431
|
+
repository.
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
[](https://pypi.org/project/sdr-visualizer/)
|
|
4
4
|
[](https://github.com/brian-a-au/sdr-visualizer/actions/workflows/test.yml)
|
|
5
5
|
[](https://github.com/brian-a-au/sdr-visualizer/actions/workflows/lint.yml)
|
|
6
|
+
[](https://github.com/brian-a-au/sdr-visualizer/actions/workflows/version-sync.yml)
|
|
6
7
|
[](https://www.python.org/downloads/)
|
|
7
8
|
[](https://github.com/brian-a-au/sdr-visualizer/tree/main/tests)
|
|
8
9
|
[](https://github.com/astral-sh/ruff)
|
|
@@ -36,9 +37,12 @@ Treat it as derived from the source snapshot. Move, email, post, or attach it
|
|
|
36
37
|
only in authorized locations and in accordance with your organization's
|
|
37
38
|
confidentiality and data-handling policy.
|
|
38
39
|
|
|
39
|
-
|
|
40
|
+
sdr-visualizer is an independent open source project. It is not affiliated
|
|
41
|
+
with, endorsed by, or supported by Adobe. See [Trademarks](#trademarks).
|
|
40
42
|
|
|
41
|
-
|
|
43
|
+
## Scope and limits
|
|
44
|
+
|
|
45
|
+
A report shows what a snapshot configures. It
|
|
42
46
|
does not grade the implementation or confirm that it works. Check the details
|
|
43
47
|
against the source implementation before you act on them.
|
|
44
48
|
|
|
@@ -298,8 +302,9 @@ sidecar share one schema, published at
|
|
|
298
302
|
and validated in CI against every payload shape produced by the bundled
|
|
299
303
|
fixtures. Removing or retyping a field is major; adding optional fields is
|
|
300
304
|
minor. The `segment_trees` / `formula_trees` node internals are documented in
|
|
301
|
-
the schema as loosely specified.
|
|
302
|
-
|
|
305
|
+
the schema as loosely specified. Before each release, the maintainer also
|
|
306
|
+
checks the payload against fresh generator output and a private set of real
|
|
307
|
+
snapshots. Those snapshots contain customer data and are never published. See
|
|
303
308
|
[`docs/RELEASING.md`](https://github.com/brian-a-au/sdr-visualizer/blob/main/docs/RELEASING.md).
|
|
304
309
|
|
|
305
310
|
**CJA lineage.** `cja-lineage` adds a separate CJA-only command. Its documented
|
|
@@ -381,3 +386,13 @@ is governed by the [`CODE_OF_CONDUCT.md`](https://github.com/brian-a-au/sdr-visu
|
|
|
381
386
|
MIT — see [`LICENSE`](https://github.com/brian-a-au/sdr-visualizer/blob/main/LICENSE). The output bundles [D3](https://d3js.org) v7,
|
|
382
387
|
vendored under the ISC license; see
|
|
383
388
|
[`THIRD_PARTY_LICENSES`](https://github.com/brian-a-au/sdr-visualizer/blob/main/THIRD_PARTY_LICENSES) for the full notice.
|
|
389
|
+
|
|
390
|
+
## Trademarks
|
|
391
|
+
|
|
392
|
+
sdr-visualizer is an independent project. It is not affiliated with, endorsed
|
|
393
|
+
by, sponsored by, or supported by Adobe Inc. Adobe, Adobe Analytics, and
|
|
394
|
+
Customer Journey Analytics are trademarks or registered trademarks of Adobe
|
|
395
|
+
Inc. in the United States and other countries. This project uses those names
|
|
396
|
+
only to say which products its input comes from. For help with an Adobe
|
|
397
|
+
product, contact Adobe. For help with this tool, open an issue in this
|
|
398
|
+
repository.
|
|
@@ -167,13 +167,36 @@ The fixtures may diverge from sdr-grader's over time — the visualizer wants mo
|
|
|
167
167
|
window-cap filtering (including an empty list for an empty directory) and
|
|
168
168
|
maps discovery failures to `InvalidSnapshotError`.
|
|
169
169
|
The grader mirrors this loader primitive even though it has no trend mode.
|
|
170
|
+
The grader also drops non-regular files here. The visualizer keeps them,
|
|
171
|
+
because a dangling symlink or `*.json` directory must stay a protected output
|
|
172
|
+
candidate; mtime-based selection skips a candidate whose metadata cannot be
|
|
173
|
+
read, with a warning (1.2.8).
|
|
174
|
+
- Record diagnostics — a malformed or ID-less record is reported by collection
|
|
175
|
+
and index (`CJA metrics[3]`, `AA segments[0]`) through `_record_context`,
|
|
176
|
+
never by echoing record content. Mirrored from sdr-grader `011b3d2` in 1.2.8.
|
|
177
|
+
- `input.shell_out` failure messages — a nonzero generator exit reports the
|
|
178
|
+
tool and exit status only; generator stderr is not echoed (sdr-grader
|
|
179
|
+
`011b3d2`). An empty or dash-leading `--dataview` / `--rsid` ID is rejected
|
|
180
|
+
before the generator starts, because it would be read as an option
|
|
181
|
+
(sdr-grader `90a9b3f`). The grader's capture byte limits and process-group
|
|
182
|
+
cleanup are not yet mirrored.
|
|
183
|
+
- `input.loader` stdin — stdin bytes are decoded as UTF-8 whatever the locale
|
|
184
|
+
encoding, and undecodable stdin is invalid input ("stdin is not valid UTF-8"),
|
|
185
|
+
mirrored from sdr-grader `2b18cd1` and `82c742e` in 1.2.8.
|
|
186
|
+
- `input.detect` — AA detection reads `report_suite` whenever that key is
|
|
187
|
+
present, the same key choice as the AA adapter (sdr-grader `5736d46`).
|
|
188
|
+
- `_present` — component, derived-field, calculated-metric and segment names
|
|
189
|
+
and component types (plus the AA report suite name) treat a non-finite float
|
|
190
|
+
as missing, because pandas writes NaN for an empty cell (sdr-grader `7cf980b`).
|
|
170
191
|
- `_parse_tag_list` / `_parse_ref_list` — parse `tags` and reference fields
|
|
171
192
|
that `cja_auto_sdr` ships as JSON-encoded list strings (`'["a"]'`) while
|
|
172
193
|
tolerating native lists. Ordinary JSON syntax failures drop to `[]`;
|
|
173
194
|
decoder resource failures raise `InvalidSnapshotError`. Every successful
|
|
174
195
|
decode is limited to depth 100 and 10,000 nodes before shape fallback or
|
|
175
196
|
coercion; this decoded-structure guard is part of the required sibling
|
|
176
|
-
parity behavior.
|
|
197
|
+
parity behavior. A tag list keeps non-empty strings and the `name` (or else
|
|
198
|
+
`id`) of tag objects. Null, numbers, booleans and empty strings are dropped,
|
|
199
|
+
so they never count a component as tagged.
|
|
177
200
|
- Snapshot structure validation rejects surrogate code points in string values
|
|
178
201
|
and mapping keys before normalization. Embedded definition, tag, and
|
|
179
202
|
reference helpers repeat that Unicode-scalar check after decoding, when
|
|
@@ -181,21 +204,45 @@ The fixtures may diverge from sdr-grader's over time — the visualizer wants mo
|
|
|
181
204
|
- `input/json_io.py` (`unique_object`, `DuplicateJSONKeyError`) — snapshot
|
|
182
205
|
files, stdin, and generator output reject a JSON object that repeats a member
|
|
183
206
|
name, mirrored from sdr-grader #127 (`c8e7316`). The helper body is
|
|
184
|
-
identical; only the module docstring differs.
|
|
207
|
+
identical; only the module docstring differs. The embedded JSON in
|
|
208
|
+
definitions, tag lists and reference lists uses the same hook and raises
|
|
209
|
+
"<label> JSON contains duplicate object keys" (sdr-grader #156).
|
|
185
210
|
- `_optional_list` (AA) — an absent/null optional section is `[]`, but a present non-list value raises `InvalidSnapshotError` (a malformed export, not an empty one). CJA gets the same guarantee through `_section_records`.
|
|
186
211
|
- `generator_version_warning` / `_version_tuple` / `TESTED_THROUGH_GENERATOR_VERSION` — the Q5 version-compat warning mechanism (1.0.0). The helper bodies are behavior-identical; the constant's *value* is per-platform and per-release by design (the newest generator version that release was validated against).
|
|
187
212
|
- `_optional_timestamp` — guards `created_at`/`modified_at`: keeps the value only if it's already a non-empty string, else `None`, so `_compact` drops a non-string timestamp (an epoch int, say) instead of leaking it into the payload. Present in both `cja.py` and `aa.py` here, and mirrored into the grader's copies of both adapters — a non-string timestamp is *missing*, not a value worth coercing to a numeric string.
|
|
188
213
|
- `_optional_str` (CJA only) — guards `owner` and the derived-field `data_type` at the CJA record builders that previously passed them straight through unguarded (`str(x) if x else None`, the same pattern the metric/dimension `data_type` path already used). No grader-side mirror needed for `owner`: the grader's `_normalize_owner` (governance helper, see below) already reduces a non-string owner to `None` — a different mechanism, the same outcome — so grader `owner` needs no additional guard. AA needs no `_optional_str` mirror either: its three `owner_id` builder sites and its one `data_type` site were already cast (`str(x) if x else None`) before this cycle.
|
|
189
214
|
|
|
215
|
+
**Reviewed for 1.2.8 and not mirrored:** sdr-grader 1.7.0 also changed these
|
|
216
|
+
shared paths. Each is a deliberate project difference, not a missed defensive
|
|
217
|
+
fix:
|
|
218
|
+
|
|
219
|
+
- `49ed033` computes CJA segment `nesting_depth` from the definition's
|
|
220
|
+
containers so a grading threshold means the same on both platforms. The
|
|
221
|
+
visualizer describes the exported value, so it keeps `cja_auto_sdr`'s
|
|
222
|
+
`nesting_depth`.
|
|
223
|
+
- `f63704a` adds a grader warning for a degraded AA `fetch_status`. It is a new
|
|
224
|
+
diagnostic tied to grading findings, not input hardening.
|
|
225
|
+
- `0ceb788` and the earlier `19c9e94` accept a UTF-8 BOM. The visualizer still
|
|
226
|
+
rejects a BOM on file and stdin input alike; accepting it would widen input
|
|
227
|
+
acceptance and is left to a feature release.
|
|
228
|
+
- `2bed8a1` raises the grader's snapshot node budget to 1,000,000. The
|
|
229
|
+
visualizer's 250,000-node budget is its own published contract limit.
|
|
230
|
+
|
|
190
231
|
**Grader-only (do not port — evaluative, not descriptive):** the grader carries logic that exists only to serve its grading rules — governance signals (`_governance_approved`, `_governance_shared_to_count`, `_normalize_owner`, `_aa_governance_signals`) and inline-echo de-duplication (`_echoes_derived_field`, which drops a metric/dimension that merely re-declares a derived field so rule SCH-001 doesn't false-fire on the duplicate name). The visualizer describes rather than grades: it keeps such echoes and instead warns on duplicate component ids (last-writer-wins for anatomy), so it never adopts these helpers.
|
|
191
232
|
|
|
192
233
|
**Visualizer-only numeric coercion — intentional divergence, do NOT reconcile to the grader:** `_as_float` / `_as_int` are the visualizer's variant of the grader's tolerant `_safe_float` / `_safe_int`. Two deltas, both driven by visualizer-only features:
|
|
193
234
|
|
|
194
|
-
1. A present-but-unconvertible numeric **raises** `InvalidSnapshotError` (the grader defaults). Trend mode relies on the raise to *skip* a malformed snapshot rather than chart a fabricated value; a single snapshot exits 3.
|
|
235
|
+
1. A present-but-unconvertible numeric **raises** `InvalidSnapshotError` (the grader defaults). Trend mode relies on the raise to *skip* a malformed snapshot rather than chart a fabricated value; a single snapshot exits 3. This includes values that overflow the conversion, such as a 400-digit integer or an infinite `nesting_depth` (1.2.8). The message names the value's type, not the value.
|
|
195
236
|
2. `NaN` / `Infinity` **pass through** to the renderer's `allow_nan=False` guard, which rejects the whole report (audit H2). The grader coerces them to a default. A visualizer report that embeds `NaN` cannot boot in a browser, so rejecting loudly beats substituting `0.0`.
|
|
196
237
|
|
|
197
238
|
These deltas are pinned by `test_nan_snapshot_exits_3`, `test_nan_in_snapshot_raises_invalid_snapshot_error`, and the trend bad-scalar skip test — a sync that "fixes" the divergence will fail them.
|
|
198
239
|
|
|
240
|
+
**AA calculated-metric attribution — intentional divergence:** the visualizer
|
|
241
|
+
keeps `attribution`/`attribution_model` and `allocation` only when they are
|
|
242
|
+
strings and otherwise omits them, so the payload matches its published schema
|
|
243
|
+
(1.2.8). The grader rejects a non-string attribution or allocation as invalid
|
|
244
|
+
input (sdr-grader #156).
|
|
245
|
+
|
|
199
246
|
**Visualizer-only output protection — do not port to the grader:** before its
|
|
200
247
|
first write, the visualizer compares both HTML and optional JSON destinations
|
|
201
248
|
with every explicit snapshot and every directory candidate. Resolved paths and
|
|
@@ -241,4 +288,5 @@ Unlike the grader, the visualizer never treats an omitted built-in as an
|
|
|
241
288
|
available node. Raw segment definitions remain outside snapshot comparison,
|
|
242
289
|
and CJA comparison still uses exported formula summaries and reference arrays;
|
|
243
290
|
this patch does not claim semantic equivalence checking for arbitrary ASTs.
|
|
244
|
-
|
|
291
|
+
The 1.1.2 review evidence is recorded in
|
|
292
|
+
[PR #41](https://github.com/brian-a-au/sdr-visualizer/pull/41).
|
|
@@ -169,7 +169,10 @@ Build time is the median of three runs.
|
|
|
169
169
|
|
|
170
170
|
`scripts/perf_browser_check.py` opens generated reports in headless Chromium
|
|
171
171
|
through Playwright. It measures initial render time, cold and warm catalog
|
|
172
|
-
filtering, and the main-thread pause when the graph initializes.
|
|
172
|
+
filtering, and the main-thread pause when the graph initializes. Every report
|
|
173
|
+
embeds the same JavaScript, so the browser compiles it once and reuses it. The
|
|
174
|
+
gate opens the first report once without timing it, so that one-time compile is
|
|
175
|
+
not charged only to whichever report happens to be measured first.
|
|
173
176
|
|
|
174
177
|
The browser gate exercises all declared size tiers and enforces a 700 ms graph
|
|
175
178
|
initialization cap, including a deterministic graph of roughly 1,000 nodes and
|
|
@@ -91,7 +91,9 @@ Before the first write, the visualizer verifies that the HTML and optional JSON
|
|
|
91
91
|
destinations do not alias one another or any filesystem snapshot input. For a
|
|
92
92
|
primary, baseline, or trend directory, every `*.json` candidate is protected,
|
|
93
93
|
including corrupt, skipped, retained, and capped-out members. The check covers
|
|
94
|
-
resolved paths, symlinks, symlinked parents,
|
|
94
|
+
resolved paths, symlinks, symlinked parents, existing hard links, and new
|
|
95
|
+
names in one directory that differ only in letter case or Unicode
|
|
96
|
+
normalization (which a case-insensitive filesystem stores as one file); a
|
|
95
97
|
collision is invalid input and leaves source bytes unchanged.
|
|
96
98
|
|
|
97
99
|
Offline is a transport property, not a confidentiality classification.
|
|
@@ -214,12 +216,13 @@ unusable, and capped-out members do not warn. The current markers are:
|
|
|
214
216
|
| Platform | Warning threshold |
|
|
215
217
|
|---|---:|
|
|
216
218
|
| CJA (`cja_auto_sdr`) | 3.13.6 |
|
|
217
|
-
| AA (`aa_auto_sdr`) | 1.24.
|
|
219
|
+
| AA (`aa_auto_sdr`) | 1.24.6 |
|
|
218
220
|
|
|
219
221
|
A marker is evidence of exercised compatibility, not a promise about every
|
|
220
222
|
possible snapshot produced by that version. It may advance only after a
|
|
221
223
|
fresh, production-representative snapshot from that exact upstream version and
|
|
222
|
-
the
|
|
224
|
+
the maintainer's private set of real historical snapshots pass the release
|
|
225
|
+
checks in
|
|
223
226
|
[`RELEASING.md`](RELEASING.md). Bundled fixtures and CI do not replace that
|
|
224
227
|
evidence.
|
|
225
228
|
|
|
@@ -333,7 +333,7 @@ non-critical item is backlog.
|
|
|
333
333
|
|
|
334
334
|
## Requirement evidence matrix
|
|
335
335
|
|
|
336
|
-
Start from the tracked [release evidence template](RELEASE_EVIDENCE_TEMPLATE.md),
|
|
336
|
+
Start from the tracked [release evidence template](https://github.com/brian-a-au/sdr-visualizer/blob/main/docs/RELEASE_EVIDENCE_TEMPLATE.md),
|
|
337
337
|
copy it into the release pull request, and replace every `pending` with durable
|
|
338
338
|
evidence tied to the candidate SHA. The template is a copy source; this
|
|
339
339
|
checklist remains authoritative.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "sdr-visualizer"
|
|
3
|
-
version = "1.2.
|
|
3
|
+
version = "1.2.9"
|
|
4
4
|
description = "Static-output visual catalog generator for Adobe Customer Journey Analytics and Adobe Analytics implementations."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
@@ -8,6 +8,7 @@ license-files = ["LICENSE", "THIRD_PARTY_LICENSES"]
|
|
|
8
8
|
authors = [{ name = "Brian Au" }]
|
|
9
9
|
requires-python = ">=3.11"
|
|
10
10
|
classifiers = [
|
|
11
|
+
"Development Status :: 5 - Production/Stable",
|
|
11
12
|
"Environment :: Console",
|
|
12
13
|
"Intended Audience :: Developers",
|
|
13
14
|
"Programming Language :: Python :: 3",
|
|
@@ -84,6 +85,8 @@ exclude = [
|
|
|
84
85
|
"tests/fixtures/cja_snapshot_large.json",
|
|
85
86
|
"tests/fixtures/cja_snapshot_xl.json",
|
|
86
87
|
"tests/fixtures/aa_snapshot_large.json",
|
|
88
|
+
"docs/DEPENDABOT_AUTOMATION.md",
|
|
89
|
+
"docs/RELEASE_EVIDENCE_TEMPLATE.md",
|
|
87
90
|
]
|
|
88
91
|
|
|
89
92
|
[tool.pytest.ini_options]
|
|
@@ -16,12 +16,26 @@ import re
|
|
|
16
16
|
import subprocess
|
|
17
17
|
import sys
|
|
18
18
|
import tomllib
|
|
19
|
+
from datetime import UTC, datetime, timedelta
|
|
19
20
|
from pathlib import Path
|
|
20
21
|
from typing import Any
|
|
21
22
|
from urllib.parse import unquote, urlsplit
|
|
22
23
|
from urllib.request import urlopen
|
|
23
24
|
|
|
24
|
-
|
|
25
|
+
# The furthest update each automated package may take: "patch" keeps the
|
|
26
|
+
# major and minor version, "minor" keeps the major version. Every other
|
|
27
|
+
# package, and any larger update, needs a manual merge.
|
|
28
|
+
UPDATE_LEVELS = {
|
|
29
|
+
"ruff": "patch",
|
|
30
|
+
"pytest": "patch",
|
|
31
|
+
"pytest-cov": "patch",
|
|
32
|
+
"pytest-xdist": "patch",
|
|
33
|
+
"hypothesis": "minor",
|
|
34
|
+
}
|
|
35
|
+
ALLOWED_PACKAGES = frozenset(UPDATE_LEVELS)
|
|
36
|
+
# Matches Dependabot's cooldown. A release this new could still be yanked
|
|
37
|
+
# or found compromised, so it waits for a manual merge instead.
|
|
38
|
+
MIN_RELEASE_AGE = timedelta(days=3)
|
|
25
39
|
REQUIRED_CHECKS = frozenset(
|
|
26
40
|
{
|
|
27
41
|
"dependabot-policy",
|
|
@@ -130,7 +144,7 @@ def _valid_artifact(artifact: dict[str, Any], name: str, version: str, *, wheel:
|
|
|
130
144
|
|
|
131
145
|
|
|
132
146
|
def review_locks(before: dict[str, Any], after: dict[str, Any]) -> tuple[bool, str]:
|
|
133
|
-
"""Conservatively approve only direct dev
|
|
147
|
+
"""Conservatively approve only allowlisted direct dev updates with no other changes."""
|
|
134
148
|
if {key: value for key, value in before.items() if key != "package"} != {
|
|
135
149
|
key: value for key, value in after.items() if key != "package"
|
|
136
150
|
}:
|
|
@@ -159,8 +173,10 @@ def review_locks(before: dict[str, Any], after: dict[str, Any]) -> tuple[bool, s
|
|
|
159
173
|
old_version, new_version = [
|
|
160
174
|
tuple(map(int, match.groups())) for match in versions if match is not None
|
|
161
175
|
]
|
|
162
|
-
|
|
163
|
-
|
|
176
|
+
level = UPDATE_LEVELS[name]
|
|
177
|
+
kept = 2 if level == "patch" else 1
|
|
178
|
+
if old_version[:kept] != new_version[:kept] or new_version <= old_version:
|
|
179
|
+
return False, f"{name} is not a forward {level} update"
|
|
164
180
|
allowed_fields = {"version", "sdist", "wheels"}
|
|
165
181
|
if {key: value for key, value in previous.items() if key not in allowed_fields} != {
|
|
166
182
|
key: value for key, value in updated.items() if key not in allowed_fields
|
|
@@ -186,8 +202,16 @@ def read_lock(repo: str, sha: str) -> dict[str, Any]:
|
|
|
186
202
|
return tomllib.loads(base64.b64decode(content["content"]).decode("utf-8"))
|
|
187
203
|
|
|
188
204
|
|
|
189
|
-
def verify_pypi_artifacts(
|
|
190
|
-
|
|
205
|
+
def verify_pypi_artifacts(
|
|
206
|
+
before: dict[str, Any], after: dict[str, Any], *, now: datetime | None = None
|
|
207
|
+
) -> str | None:
|
|
208
|
+
"""Compare every changed artifact with the registry's published metadata.
|
|
209
|
+
|
|
210
|
+
Mismatched or yanked artifacts raise. A release younger than
|
|
211
|
+
MIN_RELEASE_AGE returns the reason it must wait for a manual merge.
|
|
212
|
+
"""
|
|
213
|
+
now = now or datetime.now(UTC)
|
|
214
|
+
too_new = []
|
|
191
215
|
old, new = _packages(before), _packages(after)
|
|
192
216
|
for name, resolutions in new.items():
|
|
193
217
|
if resolutions == old[name]:
|
|
@@ -209,6 +233,15 @@ def verify_pypi_artifacts(before: dict[str, Any], after: dict[str, Any]) -> None
|
|
|
209
233
|
or artifact["size"] != match["size"]
|
|
210
234
|
):
|
|
211
235
|
raise ValueError("Artifact differs from PyPI metadata or has been yanked")
|
|
236
|
+
uploaded = datetime.fromisoformat(match["upload_time_iso_8601"])
|
|
237
|
+
if uploaded.tzinfo is None:
|
|
238
|
+
raise ValueError("PyPI upload time has no timezone")
|
|
239
|
+
if now - uploaded < MIN_RELEASE_AGE:
|
|
240
|
+
too_new.append(name)
|
|
241
|
+
if too_new:
|
|
242
|
+
days = MIN_RELEASE_AGE.days
|
|
243
|
+
return f"{', '.join(sorted(set(too_new)))} was released less than {days} days ago"
|
|
244
|
+
return None
|
|
212
245
|
|
|
213
246
|
|
|
214
247
|
def evaluate_pr(repo: str, pr: dict[str, Any]) -> tuple[bool, str]:
|
|
@@ -222,8 +255,8 @@ def evaluate_pr(repo: str, pr: dict[str, Any]) -> tuple[bool, str]:
|
|
|
222
255
|
return False, "Only an existing uv.lock may be modified"
|
|
223
256
|
before, after = read_lock(repo, pr["base"]["sha"]), read_lock(repo, pr["head"]["sha"])
|
|
224
257
|
eligible, reason = review_locks(before, after)
|
|
225
|
-
if eligible:
|
|
226
|
-
|
|
258
|
+
if eligible and (wait := verify_pypi_artifacts(before, after)):
|
|
259
|
+
return False, wait
|
|
227
260
|
return eligible, reason
|
|
228
261
|
|
|
229
262
|
|
|
@@ -289,7 +322,8 @@ def apply_review(repo: str, pr: dict[str, Any], reason: str) -> None:
|
|
|
289
322
|
"body": (
|
|
290
323
|
f"Automatic policy review: {reason}.\n\n"
|
|
291
324
|
"Verified a lockfile-only update of exclusively direct development dependencies, "
|
|
292
|
-
"unchanged dependency metadata, forward
|
|
325
|
+
"unchanged dependency metadata, forward updates within each package's allowed level, "
|
|
326
|
+
f"PyPI artifact URLs and hashes, and releases at least {MIN_RELEASE_AGE.days} days old. "
|
|
293
327
|
"All required CI checks passed. GitHub must also enforce an up-to-date branch at merge time."
|
|
294
328
|
),
|
|
295
329
|
},
|
|
@@ -413,6 +413,7 @@ def main() -> int:
|
|
|
413
413
|
browser = pw.chromium.launch(headless=True)
|
|
414
414
|
page = browser.new_page()
|
|
415
415
|
with tempfile.TemporaryDirectory() as tmp:
|
|
416
|
+
warmed_up = False
|
|
416
417
|
for case in FIXTURES:
|
|
417
418
|
snap = json.loads(case.path.read_text(encoding="utf-8"))
|
|
418
419
|
implementation = adapters[case.adapter](snap)
|
|
@@ -421,6 +422,15 @@ def main() -> int:
|
|
|
421
422
|
failures.append(mismatch)
|
|
422
423
|
html_path = Path(tmp) / f"{case.path.stem}.html"
|
|
423
424
|
html_path.write_text(render(implementation), encoding="utf-8")
|
|
425
|
+
if not warmed_up:
|
|
426
|
+
# Untimed warm-up load. Every report embeds the same
|
|
427
|
+
# JavaScript and D3, so V8 reuses its compiled code from
|
|
428
|
+
# the second report on. Without this, only the first
|
|
429
|
+
# fixture pays that one-time compile (~60-80ms on CI)
|
|
430
|
+
# and its budget measures a different thing from the rest.
|
|
431
|
+
page.goto(html_path.as_uri())
|
|
432
|
+
page.wait_for_selector("#catalog-body tr", state="attached", timeout=10_000)
|
|
433
|
+
warmed_up = True
|
|
424
434
|
failures += _check(
|
|
425
435
|
page,
|
|
426
436
|
html_path,
|