beqforge 0.2.5__tar.gz → 0.2.7__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.
- {beqforge-0.2.5 → beqforge-0.2.7}/.github/workflows/build-executable.yml +28 -20
- {beqforge-0.2.5 → beqforge-0.2.7}/PKG-INFO +44 -9
- {beqforge-0.2.5 → beqforge-0.2.7}/README.md +43 -8
- {beqforge-0.2.5 → beqforge-0.2.7}/TODO.md +2 -20
- {beqforge-0.2.5 → beqforge-0.2.7}/beq_common/__init__.py +1 -1
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge/AGENTS.md +17 -6
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge/designer.py +203 -9
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge/explain.py +30 -1
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge/pipeline.py +125 -7
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge/record.py +6 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqoptimiser/parameters.py +8 -9
- {beqforge-0.2.5 → beqforge-0.2.7}/docs/optimiser.md +2 -1
- {beqforge-0.2.5 → beqforge-0.2.7}/packaging/designer/beqforge-designer.cmd +6 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/packaging/designer/smoke.py +22 -3
- beqforge-0.2.7/plans/default-record-and-log-locations.md +46 -0
- beqforge-0.2.7/plans/design-version.md +143 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/plans/done-design-and-pipeline.md +46 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/plans/parameter-space-optimisation.md +14 -1
- beqforge-0.2.7/tests/fixtures/design_golden.json +50 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_designer.py +265 -12
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_designer_server.py +42 -3
- beqforge-0.2.7/tests/test_design_golden.py +232 -0
- beqforge-0.2.7/tests/test_design_target_fit.py +112 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_designer_server_startup.py +36 -2
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_optimiser_parameters.py +34 -1
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/design_beq.py +2 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/designer_server.py +61 -13
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/smoke_test_exe.py +20 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/.dockerignore +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/.gitattributes +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/.github/dependabot.yml +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/.github/workflows/build-designer-image.yml +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/.github/workflows/build-device-check.yml +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/.github/workflows/dependabot-auto-merge.yaml +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/.github/workflows/tests.yml +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/.gitignore +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/.idea/.gitignore +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/.idea/beqforge.iml +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/.idea/editor.xml +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/.idea/jsonSchemas.xml +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/.idea/misc.xml +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/.idea/modules.xml +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/.idea/vcs.xml +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/AGENTS.md +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/CLAUDE.md +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/LICENCE.md +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beq_common/AGENTS.md +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beq_common/biquad.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beq_common/provenance.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beq_common/publication.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beq_common/types.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge/__init__.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge/accept.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge/beqd.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge/biquad.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge/cache.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge/charts.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge/cli.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge/design.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge/diagnose.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge/extraction.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge/filters.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge/harness.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge/identify.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge/material.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge/reference.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge/rolloff.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge/verify.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge-device-check.spec +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge.spec +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge_device_check/AGENTS.md +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge_device_check/__init__.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge_device_check/analyse.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge_device_check/audio.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge_device_check/catalogue.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge_device_check/cli.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge_device_check/coefficients.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge_device_check/engines.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge_device_check/evidence.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge_device_check/manifest.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge_device_check/measurement.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge_device_check/profiles.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge_device_check/qualification.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge_device_check/report.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge_device_check/transactions.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqforge_device_check/verify.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqoptimiser/AGENTS.md +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqoptimiser/__init__.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqoptimiser/cache.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqoptimiser/cli.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqoptimiser/core.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqoptimiser/data/seed-manifest.json +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/beqoptimiser/data/seed.json.gz +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/docs/device-check-examples/minidsp-2x4hd-usb-loopback.json +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/docs/device-check-examples/simulation.json +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/docs/device-check.md +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/docs/optimiser-report/README.md +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/docs/optimiser-report/assets/aggregate-48000.png +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/docs/optimiser-report/assets/aggregate-96000.png +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/docs/optimiser-report/assets/example-1.png +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/docs/optimiser-report/assets/example-2.png +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/docs/optimiser-report/entry-results.json.gz +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/docs/optimiser-report/statistics.json +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/evidence_validation.json +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/negative_corpus.json +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/packaging/designer/Dockerfile +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/packaging/device-check/NOTICE.txt +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/plans/F2-catalogue-filter-optimisation.md +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/plans/F2-device-precision-validation.md +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/plans/F2-package-consolidation.md +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/plans/R2a-server-stage-cache.md +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/plans/done-baseline-evidence.md +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/plans/research-design-decisions.md +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/plans/test-portability-and-runtime.md +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/plans/texture-yardstick-sub-band.md +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/plans/texture-yardstick-sub-band.patch +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/pyproject.toml +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/__init__.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/conftest.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/fixtures/optimiser_seed_entry.json +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_cli.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_accept.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_cache.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_cache_stores.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_channel_evidence.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_charts.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_corpus.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_design.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_device_rate.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_diagnose.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_evidence.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_evidence_excess.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_exclusions.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_explain.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_extraction.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_filters.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_frozen_cache.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_guard.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_harness.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_headroom.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_hum.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_identify.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_jacobian.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_parallel.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_pipeline.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_playback.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_playback_headroom.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_publication.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_record.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_record_fingerprint.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_reference.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_references.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_ripple_band.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_rolloff.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_strategy_config.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_timings.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_design_verify.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_designer_image.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_device_check_analyse.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_device_check_catalogue.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_device_check_catalogue_predict.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_device_check_cli.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_device_check_engines.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_device_check_evidence.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_device_check_examples.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_device_check_manifest.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_device_check_measurement.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_device_check_qualification.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_device_check_transactions.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_device_check_verify.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_extract_layout.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_optimiser.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_optimiser_cache.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_optimiser_report.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tests/test_package_boundaries.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/__init__.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/build_optimiser_seed.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/experiments/a1_sweep.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/experiments/compare_records.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/experiments/compare_verdicts.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/experiments/inject_variants.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/experiments/p14_compare.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/experiments/p14_jacobian.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/experiments/p14_run_pipeline.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/experiments/p14_surrogate.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/experiments/p18_compare_greedy.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/experiments/p18_compare_seeded.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/experiments/p18_compare_structure.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/experiments/p18_greedy.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/experiments/p18_seeded.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/experiments/p18_structure.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/experiments/p20_smooth_drift.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/experiments/parameter_benchmark.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/experiments/probe.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/experiments/refit_probe.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/experiments/request_timing.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/experiments/score_injected.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/experiments/t8_floor_rules.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/extract.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/ledger_template.html +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/negative_corpus.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/optimiser_catalogue_report.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/prepare_device_check_helper.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/render_ledger.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/replay.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/seed_optimiser_cache.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/smoke_test_device_check.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/smoke_test_install.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/summarise.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/tools/validate_evidence.py +0 -0
- {beqforge-0.2.5 → beqforge-0.2.7}/uv.lock +0 -0
|
@@ -34,9 +34,10 @@ jobs:
|
|
|
34
34
|
return;
|
|
35
35
|
}
|
|
36
36
|
const deadline = Date.now() + 15 * 60 * 1000;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
while (true) {
|
|
38
|
+
let runs;
|
|
39
|
+
try {
|
|
40
|
+
runs = await github.paginate(github.rest.actions.listWorkflowRuns, {
|
|
40
41
|
...context.repo,
|
|
41
42
|
workflow_id: 'tests.yml',
|
|
42
43
|
head_sha: context.sha,
|
|
@@ -44,27 +45,34 @@ jobs:
|
|
|
44
45
|
event: 'push',
|
|
45
46
|
per_page: 100,
|
|
46
47
|
});
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
run.status === 'completed' && run.conclusion === 'success'
|
|
52
|
-
);
|
|
53
|
-
if (passed) {
|
|
54
|
-
core.info(`Reusing successful main tests: ${passed.html_url}`);
|
|
55
|
-
await core.summary.addLink('Reused main test run', passed.html_url).write();
|
|
56
|
-
core.setOutput('reusable', 'true');
|
|
48
|
+
} catch (error) {
|
|
49
|
+
// a transient API failure is no reason to rerun the whole suite, so keep trying until the deadline
|
|
50
|
+
if (Date.now() >= deadline) {
|
|
51
|
+
core.warning(`Unable to check main tests; running fresh release tests: ${error.message}`);
|
|
57
52
|
return;
|
|
58
53
|
}
|
|
59
|
-
|
|
60
|
-
core.info('No successful main test run available; running fresh release tests.');
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
core.info('Main tests are still running; checking again in 30 seconds.');
|
|
54
|
+
core.info(`Unable to check main tests (${error.message}); trying again in 30 seconds.`);
|
|
64
55
|
await new Promise(resolve => setTimeout(resolve, 30_000));
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const matching = runs.filter(run =>
|
|
59
|
+
run.head_sha === context.sha && run.head_branch === 'main' && run.event === 'push'
|
|
60
|
+
);
|
|
61
|
+
const passed = matching.find(run =>
|
|
62
|
+
run.status === 'completed' && run.conclusion === 'success'
|
|
63
|
+
);
|
|
64
|
+
if (passed) {
|
|
65
|
+
core.info(`Reusing successful main tests: ${passed.html_url}`);
|
|
66
|
+
await core.summary.addLink('Reused main test run', passed.html_url).write();
|
|
67
|
+
core.setOutput('reusable', 'true');
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (!matching.some(run => run.status !== 'completed') || Date.now() >= deadline) {
|
|
71
|
+
core.info('No successful main test run available; running fresh release tests.');
|
|
72
|
+
return;
|
|
65
73
|
}
|
|
66
|
-
|
|
67
|
-
|
|
74
|
+
core.info('Main tests are still running; checking again in 30 seconds.');
|
|
75
|
+
await new Promise(resolve => setTimeout(resolve, 30_000));
|
|
68
76
|
}
|
|
69
77
|
|
|
70
78
|
tests:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: beqforge
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.7
|
|
4
4
|
Summary: Audio-derived BEQ design, published filter optimisation and DSP validation
|
|
5
5
|
License-File: LICENCE.md
|
|
6
6
|
Requires-Python: <3.15,>=3.13
|
|
@@ -146,7 +146,7 @@ straight off disk. A stale record is skipped with a warning rather than failing
|
|
|
146
146
|
|
|
147
147
|
`beqforge serve-designer` runs this pipeline as a live, HTTP-bound filter designer for
|
|
148
148
|
[`beqdesigner`](https://github.com/3ll3d00d/beqdesigner), implementing its
|
|
149
|
-
`design/designer-interface.md` v1.
|
|
149
|
+
`design/designer-interface.md` v1.4 contract (§7.1's HTTP binding) rather than the file-based
|
|
150
150
|
`.npz` → `design` → `--beq` export flow above:
|
|
151
151
|
|
|
152
152
|
```bash
|
|
@@ -183,7 +183,8 @@ An unusable directory causes a startup error naming the option and path.
|
|
|
183
183
|
relative to that root plus the SHA-256 of its decoded column, instead of inline base64 audio.
|
|
184
184
|
The server resolves, decodes and checks each reference; an unusable reference gets HTTP 422
|
|
185
185
|
naming the array, rather than a design decline. Inline requests remain supported.
|
|
186
|
-
`GET /health` reports `contract_version
|
|
186
|
+
`GET /health` reports `contract_version`, whether `shared_root` is enabled, and 1.4's design
|
|
187
|
+
version, settings and build. Each request
|
|
187
188
|
logs read, parse, decode, design and response timings; `tools/experiments/request_timing.py`
|
|
188
189
|
measures cold and warm requests.
|
|
189
190
|
|
|
@@ -192,7 +193,9 @@ its own worker process; further requests queue. `GET /health` reports it as `par
|
|
|
192
193
|
caller can send each server as many designs as it takes rather than be configured with that
|
|
193
194
|
number. Concurrent designs share the machine's logical processors, SMT threads included, each
|
|
194
195
|
at most the width one design uses alone (`BEQ_FIT_WORKERS`, if set, stays each design's own).
|
|
195
|
-
|
|
196
|
+
`--cores N` caps the processors the designs use between them, for a machine with other work to
|
|
197
|
+
do: concurrent designs split N evenly, and N must be at least `--workers`. The share changes
|
|
198
|
+
how long a fit takes, never its answer.
|
|
196
199
|
|
|
197
200
|
Every `serve-designer` configuration option can also be set through an environment variable.
|
|
198
201
|
Explicit CLI options take precedence; for repeatable options, CLI values replace the entire
|
|
@@ -215,6 +218,7 @@ their existing CLI configuration.
|
|
|
215
218
|
| `--cache-dir` | `BEQFORGE_CACHE_DIR` | path |
|
|
216
219
|
| `--shared-root` | `BEQFORGE_SHARED_ROOT` | path |
|
|
217
220
|
| `--workers` | `BEQFORGE_WORKERS` | integer, 1-8 |
|
|
221
|
+
| `--cores` | `BEQFORGE_CORES` | integer, at least `--workers` |
|
|
218
222
|
| `--quiet` | `BEQFORGE_QUIET` | boolean |
|
|
219
223
|
|
|
220
224
|
Booleans accept `true`/`false`, `yes`/`no`, `on`/`off` or `1`/`0`, ignoring case.
|
|
@@ -247,15 +251,42 @@ diagnosis, in this order:
|
|
|
247
251
|
carried `bass_management`, otherwise the assumed LR4 80 Hz model, and it says which).
|
|
248
252
|
Clipping is reported, never used to reject a filter. The typed `gain_reduction_db` field is
|
|
249
253
|
filled only when the request carries `bass_management`, as the contract requires.
|
|
254
|
+
* `fit` — how closely the filter, as the device plays it, delivers the boost the content
|
|
255
|
+
supports: the typical error, the share of that boost within ±0.5 dB, and the worst miss,
|
|
256
|
+
where and which way. It is a match to that target, not a verdict on whether the target is
|
|
257
|
+
right; `correction` says how much of what is missing that target restores.
|
|
250
258
|
* `alternatives` — why each other candidate was rejected or not chosen.
|
|
251
259
|
|
|
252
260
|
The commentary then continues with `target_notes`, `verdict_notes` (what the checks themselves
|
|
253
|
-
noticed), `effective_params` and `beqforge_revision` (which build answered). The
|
|
261
|
+
noticed), `effective_params` and `beqforge_revision` (which build answered). The same fit is
|
|
262
|
+
in typed fields (contract 1.3): `residual_db` is the worst absolute error over
|
|
263
|
+
`residual_band_hz`, the band the fit was actually scored on, measured on the published filter
|
|
264
|
+
as the device plays it; `residual_at_hz` and `residual_direction` say where and which way;
|
|
265
|
+
`residual_rms_db` and `residual_within_tolerance` (against `residual_tolerance_db`) weight
|
|
266
|
+
each frequency by the boost asked for there, as `confidence` does. The raw
|
|
254
267
|
recovered and shaping fractions are kept in the run record, not the response. A decline's `decline_message` gives the reason first, then `| found: …` with
|
|
255
268
|
the same diagnosis, then the build in brackets. `beqforge/designer.py` is the
|
|
256
269
|
pure `design(request) -> response` adapter, independently testable without a socket;
|
|
257
270
|
`tools/designer_server.py` is the HTTP transport around it.
|
|
258
271
|
|
|
272
|
+
**Which answer a build gives (contract 1.4).** Every response, design or decline, and
|
|
273
|
+
`GET /health` carry `design_version`, `design_settings` and `designer_build`, whatever
|
|
274
|
+
`contract_version` the request names. `design_version` is an integer bumped only by a change
|
|
275
|
+
that can alter an answer for an unchanged request: the candidates, the rejected designs, a
|
|
276
|
+
decline or a fit figure. Wording, speed and
|
|
277
|
+
packaging don't bump it, so the executable and the image of one release report the same
|
|
278
|
+
version and different `designer_build`s (the string also in `beqforge_revision`). Releases
|
|
279
|
+
before it: 0.0.x and 0.1.0 are 0, 0.2.0 is 1, 0.2.1 is 2, 0.2.2 is 3, and 0.2.3 to 0.2.5
|
|
280
|
+
are 4, as is 0.2.7, the first release to report it. One version is one algorithm, not one
|
|
281
|
+
filter on every machine: the `counterfactual` and `parametric` fits can land on a different
|
|
282
|
+
filter on a different processor, so compare versions, never figures. `design_settings` is a 12-hex digest of the server-wide settings that can change an
|
|
283
|
+
answer (`--strategy`, `--exclude`, `--goal-tolerance`, `--goal-tilt`, `--content-edge`,
|
|
284
|
+
`--device-rate`, `--coefficient-bits`, `--integer-bits`) that differ from their defaults, so a
|
|
285
|
+
server started with none of them always sends the same digest. `/health` also reports those
|
|
286
|
+
settings as `design_settings_detail`, for reading when two designers disagree. A caller keys
|
|
287
|
+
each title on (`design_version`, `design_settings`) to tell which titles a new release would
|
|
288
|
+
answer differently.
|
|
289
|
+
|
|
259
290
|
### Running the designer in a container
|
|
260
291
|
|
|
261
292
|
`ghcr.io/3ll3d00d/beqforge-designer:<version>` runs `beqforge serve-designer` for a beqdesigner
|
|
@@ -308,8 +339,10 @@ beqforge-windows-x86_64.exe serve-designer --host 0.0.0.0 --port 8420 ^
|
|
|
308
339
|
```
|
|
309
340
|
|
|
310
341
|
The launcher sets the image's defaults through the environment variables above, each only if
|
|
311
|
-
not already set: `BEQFORGE_HOST=0.0.0.0`, `BEQFORGE_PORT=8420
|
|
312
|
-
`BEQFORGE_CACHE_DIR=%LOCALAPPDATA%\beqforge\cache
|
|
342
|
+
not already set: `BEQFORGE_HOST=0.0.0.0`, `BEQFORGE_PORT=8420`,
|
|
343
|
+
`BEQFORGE_CACHE_DIR=%LOCALAPPDATA%\beqforge\cache` and
|
|
344
|
+
`BEQFORGE_RECORD_DIR=%LOCALAPPDATA%\beqforge\records`, so every request's run record is kept.
|
|
345
|
+
Arguments pass through to `serve-designer`.
|
|
313
346
|
|
|
314
347
|
* **Listen on the network.** The CLI's default host is `127.0.0.1`, which a container cannot
|
|
315
348
|
reach; the launcher uses `0.0.0.0`. Windows Firewall asks the first time: allow private
|
|
@@ -321,7 +354,8 @@ not already set: `BEQFORGE_HOST=0.0.0.0`, `BEQFORGE_PORT=8420` and
|
|
|
321
354
|
`http://host.docker.internal:8420/design`; from another machine, use this one's LAN address.
|
|
322
355
|
Check `http://<address>:8420/health` from there first.
|
|
323
356
|
* **Several at once.** Set `BEQFORGE_WORKERS` (or pass `--workers 2`) for a machine that can
|
|
324
|
-
design more than one title at a time; `/health` reports it as `parallel`.
|
|
357
|
+
design more than one title at a time; `/health` reports it as `parallel`. To leave room for
|
|
358
|
+
other work, set `BEQFORGE_CORES` to the most processors the designer may use between them.
|
|
325
359
|
* **The rest.** The exe is unsigned, so SmartScreen warns on first run. `serve-designer` does not need `ffmpeg`. Close the window, or
|
|
326
360
|
press Ctrl+C, to stop.
|
|
327
361
|
|
|
@@ -338,7 +372,8 @@ one locally: `uv pip install pyinstaller && uv run pyinstaller beqforge.spec`.
|
|
|
338
372
|
|
|
339
373
|
The same workflow builds and checks the Python wheel and source distribution, verifies all
|
|
340
374
|
five dependency profiles, and reuses a successful branch-push run of `tests.yml` on `main`
|
|
341
|
-
at the exact tagged commit. If matching tests are still running,
|
|
375
|
+
at the exact tagged commit. If matching tests are still running, or GitHub's API fails to answer, it
|
|
376
|
+
keeps checking for up to 15 minutes;
|
|
342
377
|
without a successful result, it runs the reusable tests workflow against the release commit.
|
|
343
378
|
GitHub release creation and executable builds require those tests to pass on Linux, macOS
|
|
344
379
|
and Windows, including device-check tests and isolated dependency profiles. PyPI publication
|
|
@@ -120,7 +120,7 @@ straight off disk. A stale record is skipped with a warning rather than failing
|
|
|
120
120
|
|
|
121
121
|
`beqforge serve-designer` runs this pipeline as a live, HTTP-bound filter designer for
|
|
122
122
|
[`beqdesigner`](https://github.com/3ll3d00d/beqdesigner), implementing its
|
|
123
|
-
`design/designer-interface.md` v1.
|
|
123
|
+
`design/designer-interface.md` v1.4 contract (§7.1's HTTP binding) rather than the file-based
|
|
124
124
|
`.npz` → `design` → `--beq` export flow above:
|
|
125
125
|
|
|
126
126
|
```bash
|
|
@@ -157,7 +157,8 @@ An unusable directory causes a startup error naming the option and path.
|
|
|
157
157
|
relative to that root plus the SHA-256 of its decoded column, instead of inline base64 audio.
|
|
158
158
|
The server resolves, decodes and checks each reference; an unusable reference gets HTTP 422
|
|
159
159
|
naming the array, rather than a design decline. Inline requests remain supported.
|
|
160
|
-
`GET /health` reports `contract_version
|
|
160
|
+
`GET /health` reports `contract_version`, whether `shared_root` is enabled, and 1.4's design
|
|
161
|
+
version, settings and build. Each request
|
|
161
162
|
logs read, parse, decode, design and response timings; `tools/experiments/request_timing.py`
|
|
162
163
|
measures cold and warm requests.
|
|
163
164
|
|
|
@@ -166,7 +167,9 @@ its own worker process; further requests queue. `GET /health` reports it as `par
|
|
|
166
167
|
caller can send each server as many designs as it takes rather than be configured with that
|
|
167
168
|
number. Concurrent designs share the machine's logical processors, SMT threads included, each
|
|
168
169
|
at most the width one design uses alone (`BEQ_FIT_WORKERS`, if set, stays each design's own).
|
|
169
|
-
|
|
170
|
+
`--cores N` caps the processors the designs use between them, for a machine with other work to
|
|
171
|
+
do: concurrent designs split N evenly, and N must be at least `--workers`. The share changes
|
|
172
|
+
how long a fit takes, never its answer.
|
|
170
173
|
|
|
171
174
|
Every `serve-designer` configuration option can also be set through an environment variable.
|
|
172
175
|
Explicit CLI options take precedence; for repeatable options, CLI values replace the entire
|
|
@@ -189,6 +192,7 @@ their existing CLI configuration.
|
|
|
189
192
|
| `--cache-dir` | `BEQFORGE_CACHE_DIR` | path |
|
|
190
193
|
| `--shared-root` | `BEQFORGE_SHARED_ROOT` | path |
|
|
191
194
|
| `--workers` | `BEQFORGE_WORKERS` | integer, 1-8 |
|
|
195
|
+
| `--cores` | `BEQFORGE_CORES` | integer, at least `--workers` |
|
|
192
196
|
| `--quiet` | `BEQFORGE_QUIET` | boolean |
|
|
193
197
|
|
|
194
198
|
Booleans accept `true`/`false`, `yes`/`no`, `on`/`off` or `1`/`0`, ignoring case.
|
|
@@ -221,15 +225,42 @@ diagnosis, in this order:
|
|
|
221
225
|
carried `bass_management`, otherwise the assumed LR4 80 Hz model, and it says which).
|
|
222
226
|
Clipping is reported, never used to reject a filter. The typed `gain_reduction_db` field is
|
|
223
227
|
filled only when the request carries `bass_management`, as the contract requires.
|
|
228
|
+
* `fit` — how closely the filter, as the device plays it, delivers the boost the content
|
|
229
|
+
supports: the typical error, the share of that boost within ±0.5 dB, and the worst miss,
|
|
230
|
+
where and which way. It is a match to that target, not a verdict on whether the target is
|
|
231
|
+
right; `correction` says how much of what is missing that target restores.
|
|
224
232
|
* `alternatives` — why each other candidate was rejected or not chosen.
|
|
225
233
|
|
|
226
234
|
The commentary then continues with `target_notes`, `verdict_notes` (what the checks themselves
|
|
227
|
-
noticed), `effective_params` and `beqforge_revision` (which build answered). The
|
|
235
|
+
noticed), `effective_params` and `beqforge_revision` (which build answered). The same fit is
|
|
236
|
+
in typed fields (contract 1.3): `residual_db` is the worst absolute error over
|
|
237
|
+
`residual_band_hz`, the band the fit was actually scored on, measured on the published filter
|
|
238
|
+
as the device plays it; `residual_at_hz` and `residual_direction` say where and which way;
|
|
239
|
+
`residual_rms_db` and `residual_within_tolerance` (against `residual_tolerance_db`) weight
|
|
240
|
+
each frequency by the boost asked for there, as `confidence` does. The raw
|
|
228
241
|
recovered and shaping fractions are kept in the run record, not the response. A decline's `decline_message` gives the reason first, then `| found: …` with
|
|
229
242
|
the same diagnosis, then the build in brackets. `beqforge/designer.py` is the
|
|
230
243
|
pure `design(request) -> response` adapter, independently testable without a socket;
|
|
231
244
|
`tools/designer_server.py` is the HTTP transport around it.
|
|
232
245
|
|
|
246
|
+
**Which answer a build gives (contract 1.4).** Every response, design or decline, and
|
|
247
|
+
`GET /health` carry `design_version`, `design_settings` and `designer_build`, whatever
|
|
248
|
+
`contract_version` the request names. `design_version` is an integer bumped only by a change
|
|
249
|
+
that can alter an answer for an unchanged request: the candidates, the rejected designs, a
|
|
250
|
+
decline or a fit figure. Wording, speed and
|
|
251
|
+
packaging don't bump it, so the executable and the image of one release report the same
|
|
252
|
+
version and different `designer_build`s (the string also in `beqforge_revision`). Releases
|
|
253
|
+
before it: 0.0.x and 0.1.0 are 0, 0.2.0 is 1, 0.2.1 is 2, 0.2.2 is 3, and 0.2.3 to 0.2.5
|
|
254
|
+
are 4, as is 0.2.7, the first release to report it. One version is one algorithm, not one
|
|
255
|
+
filter on every machine: the `counterfactual` and `parametric` fits can land on a different
|
|
256
|
+
filter on a different processor, so compare versions, never figures. `design_settings` is a 12-hex digest of the server-wide settings that can change an
|
|
257
|
+
answer (`--strategy`, `--exclude`, `--goal-tolerance`, `--goal-tilt`, `--content-edge`,
|
|
258
|
+
`--device-rate`, `--coefficient-bits`, `--integer-bits`) that differ from their defaults, so a
|
|
259
|
+
server started with none of them always sends the same digest. `/health` also reports those
|
|
260
|
+
settings as `design_settings_detail`, for reading when two designers disagree. A caller keys
|
|
261
|
+
each title on (`design_version`, `design_settings`) to tell which titles a new release would
|
|
262
|
+
answer differently.
|
|
263
|
+
|
|
233
264
|
### Running the designer in a container
|
|
234
265
|
|
|
235
266
|
`ghcr.io/3ll3d00d/beqforge-designer:<version>` runs `beqforge serve-designer` for a beqdesigner
|
|
@@ -282,8 +313,10 @@ beqforge-windows-x86_64.exe serve-designer --host 0.0.0.0 --port 8420 ^
|
|
|
282
313
|
```
|
|
283
314
|
|
|
284
315
|
The launcher sets the image's defaults through the environment variables above, each only if
|
|
285
|
-
not already set: `BEQFORGE_HOST=0.0.0.0`, `BEQFORGE_PORT=8420
|
|
286
|
-
`BEQFORGE_CACHE_DIR=%LOCALAPPDATA%\beqforge\cache
|
|
316
|
+
not already set: `BEQFORGE_HOST=0.0.0.0`, `BEQFORGE_PORT=8420`,
|
|
317
|
+
`BEQFORGE_CACHE_DIR=%LOCALAPPDATA%\beqforge\cache` and
|
|
318
|
+
`BEQFORGE_RECORD_DIR=%LOCALAPPDATA%\beqforge\records`, so every request's run record is kept.
|
|
319
|
+
Arguments pass through to `serve-designer`.
|
|
287
320
|
|
|
288
321
|
* **Listen on the network.** The CLI's default host is `127.0.0.1`, which a container cannot
|
|
289
322
|
reach; the launcher uses `0.0.0.0`. Windows Firewall asks the first time: allow private
|
|
@@ -295,7 +328,8 @@ not already set: `BEQFORGE_HOST=0.0.0.0`, `BEQFORGE_PORT=8420` and
|
|
|
295
328
|
`http://host.docker.internal:8420/design`; from another machine, use this one's LAN address.
|
|
296
329
|
Check `http://<address>:8420/health` from there first.
|
|
297
330
|
* **Several at once.** Set `BEQFORGE_WORKERS` (or pass `--workers 2`) for a machine that can
|
|
298
|
-
design more than one title at a time; `/health` reports it as `parallel`.
|
|
331
|
+
design more than one title at a time; `/health` reports it as `parallel`. To leave room for
|
|
332
|
+
other work, set `BEQFORGE_CORES` to the most processors the designer may use between them.
|
|
299
333
|
* **The rest.** The exe is unsigned, so SmartScreen warns on first run. `serve-designer` does not need `ffmpeg`. Close the window, or
|
|
300
334
|
press Ctrl+C, to stop.
|
|
301
335
|
|
|
@@ -312,7 +346,8 @@ one locally: `uv pip install pyinstaller && uv run pyinstaller beqforge.spec`.
|
|
|
312
346
|
|
|
313
347
|
The same workflow builds and checks the Python wheel and source distribution, verifies all
|
|
314
348
|
five dependency profiles, and reuses a successful branch-push run of `tests.yml` on `main`
|
|
315
|
-
at the exact tagged commit. If matching tests are still running,
|
|
349
|
+
at the exact tagged commit. If matching tests are still running, or GitHub's API fails to answer, it
|
|
350
|
+
keeps checking for up to 15 minutes;
|
|
316
351
|
without a successful result, it runs the reusable tests workflow against the release commit.
|
|
317
352
|
GitHub release creation and executable builds require those tests to pass on Linux, macOS
|
|
318
353
|
and Windows, including device-check tests and isolated dependency profiles. PyPI publication
|
|
@@ -30,7 +30,6 @@ are retained so records and code comments remain traceable. Completed items are
|
|
|
30
30
|
| 11 | F4 follow-up | parked | Both fitter-objective selection spikes lost passes/true positives. Judge several fits only with a demonstrated usable alternative discarded before `_judge`; a smaller residual is insufficient evidence. |
|
|
31
31
|
| 12 | F3 follow-up | parked | A fifth section moved no verdict when tried and costs more fitting. Reopen if an evidence-supported target has a demonstrated usable fit beyond four sections and no usable fit within four. |
|
|
32
32
|
| 13 | T2 | parked | The steep-variant audit found no swept cap binding a weak winner. Derive caps only when a binding cap demonstrably prevents a better supported result. |
|
|
33
|
-
| 14 | Residual (former TODO 5) | open | Correct the reported target error and its actual measurement band without changing fit selection. Primarily audit accuracy; any escalation/objective change is a separate, higher-impact decision. Non-parametric reporting is already allowed upstream. |
|
|
34
33
|
| 15 | Authored features | parked | Manual `--exclude` works; no validated automatic detector exists. Reopen with real labelled features and negatives showing that detection preserves legitimate bass. |
|
|
35
34
|
| 16 | Dynamic processing (former TODO 11) | parked | No suspected real case has scoped a useful tripwire. Reopen when compression/limiting demonstrably compromises this correction; non-LTI processing alone does not prove every BEQ is invalid. |
|
|
36
35
|
| 17 | Tilted passband reference (goal/corpus follow-up) | parked | No real in-sub-band case needs a sloping reference yet; Dossier's abstention was decided. Reopen with one. Goal-aware corpus labels belong to the open E10 protocol work. Nearest so far: 13 Going on 30's mix tilts about −7 dB/oct above 50 Hz (bass is coherent across channels, ~200 Hz is not) and finds its plateau only through the one-sided fallback. That reference reads a 36 dB deficit below 25 Hz; each channel's own plateau reads 30-34 dB. A reference built from the channels would be 2-5 dB less generous there (see plans/done-design-and-pipeline.md, 11 October 2026). |
|
|
@@ -43,7 +42,8 @@ are retained so records and code comments remain traceable. Completed items are
|
|
|
43
42
|
| 24 | Chart peak bias | parked | Deliberate catalogue-compatible per-bin maximum; it affects presentation only. Reopen if a reader needs an alternative percentile view; keep it distinct from the 95th-percentile evidence envelope. |
|
|
44
43
|
| 25 | Documentation citations | open | Remove or repoint orphan references and stale descriptions of shipped behaviour. Low filter impact, but prevents future changes being based on obsolete assumptions. |
|
|
45
44
|
| 26 | Texture yardstick retune (library review) | parked | The cubic (row 2) keeps the 200 Hz band only because the sub-band cap admitted one synthetic negative, `broadband/5`: chance unevenness in noise bursts, at a 2.6 dB boost. With 45 gated cases a single case moves the gate's upper bound from 0.079 to 0.118. Carrie is still declined (0.85x about a cubic, 1.33x with the cap). Reopen when the beqdesigner library review (~1,290 titles) is complete. Then retune against its declines: the band edge, the trend degree, and whether the gate weights a false acceptance by its boost. The library holds no known negative, so it can expose wrong declines but not measure false acceptances. Any retune still needs the corpus or a fresh E10 set for that, ideally with negatives built from real programme texture (row 5). See [the outcome](plans/texture-yardstick-sub-band.md#outcome-17-october-2026). |
|
|
46
|
-
| 27 |
|
|
45
|
+
| 27 | Keep run records and logs by default | open | `serve-designer` keeps no run record and no log file unless told to, so the Windows designer's 432 library titles (111 declines) have no records and can be reviewed only from their responses. Default records and a rotating log to the platform's per-user data directory (as the `.cmd` already does for the cache), set and declare `/records` in the image, report `record_dir` on `/health`. See [the handoff](plans/default-record-and-log-locations.md). |
|
|
46
|
+
| 28 | Fit sensitivity to arithmetic | open | The same request can get a different filter on a different processor. On the synthetic BW4 24 Hz title, `counterfactual` gave 2 sections here and 3 on Linux and macOS CI; `parametric`'s boost was 15.21 dB on this AVX-512 machine, 15.78 dB with Haswell BLAS kernels and 16.49 dB on CI. `flatten` matched everywhere. It's repeatable on one machine, so last-digit differences grow into a different local optimum. Measure it on real titles first: run the baseline set under `OPENBLAS_CORETYPE=Haswell` (or on another CPU) and compare verdicts and corrected curves. Then decide whether the fit needs a deterministic tie-break or a conditioning fix, or whether only the golden's coverage does. See [the correction](plans/design-version.md#correction-after-the-v026-release-tests). |
|
|
47
47
|
|
|
48
48
|
## Review evidence
|
|
49
49
|
|
|
@@ -92,10 +92,6 @@ Evidence behind the order and the parked decisions:
|
|
|
92
92
|
filtered/1 reached 5.6 dB over 0.28 octave. Real-title candidates stayed below 0.5 dB.
|
|
93
93
|
Audit this with steep policy; it is not evidence specifically of single-bin holes or
|
|
94
94
|
permission to add a blanket gate.
|
|
95
|
-
* [`designer._to_design_candidate`](beqforge/designer.py) forwards `fit_error_db` and the
|
|
96
|
-
run's residual band, while [`filters._fit_structure`](beqforge/filters.py) returns a
|
|
97
|
-
target-error/device-drift objective and [`design._result`](beqforge/design.py) recomputes
|
|
98
|
-
pure target error. The actual fit score band can widen beyond the reported one.
|
|
99
95
|
* The sibling [`designer-interface.md`](../beqdesigner/design/designer-interface.md)
|
|
100
96
|
§§2–4 already adopts clipping cost, bass management, ordinal confidence, non-parametric
|
|
101
97
|
residuals and decline/report rules. It deliberately excludes device preference.
|
|
@@ -124,20 +120,6 @@ supplying a dict is not enough to claim it was modelled. No hardware capture is
|
|
|
124
120
|
these software comparisons. Caller propagation requires a change in beqdesigner, outside
|
|
125
121
|
this repo; both its batch job and library call omit the available argument today.
|
|
126
122
|
|
|
127
|
-
### Residual reporting
|
|
128
|
-
|
|
129
|
-
`Candidate.fit_error_db` reaches the response's `residual_db`. For fitted candidates it is
|
|
130
|
-
normally the larger of target error and exact-coefficient quantisation change; after pruning
|
|
131
|
-
it can be pure target error. The contract already permits residuals for every method with an
|
|
132
|
-
explicit target and defines them as maximum absolute target error. Recompute the reported
|
|
133
|
-
error from the canonical published cascade against its priced target on a stated band; make
|
|
134
|
-
the evaluation rate and coefficient treatment explicit. Carry or recompute the actual band:
|
|
135
|
-
`_fit_all` can widen it above `params.residual_band_hz`, and parametric proposals carry their
|
|
136
|
-
own effective design settings. Keep the optimisation objective, drift screen and escalation
|
|
137
|
-
unchanged in a reporting fix. Changing what `residual_target_db` compares against is a
|
|
138
|
-
separate decision change and needs the full regression workflow. A small residual says the
|
|
139
|
-
fit matched the constructed target, not that the target is correct.
|
|
140
|
-
|
|
141
123
|
### Fresh validation protocol and steep-filter policy
|
|
142
124
|
|
|
143
125
|
The legacy corpus is development evidence after repeated tuning, not a held-out estimate.
|
|
@@ -40,10 +40,10 @@ still holds: no other API, no other service, and the server is a thin transport
|
|
|
40
40
|
| `beqforge/record.py` | The run record — a fingerprinted, gzipped JSON of everything a run produced, written every run. Ours, not beqdesigner's. |
|
|
41
41
|
| `beqforge/beqd.py` | Export to a `.beq` beqdesigner project. A separate job from the record: idiomatic in their UI, allowed to be lossy. |
|
|
42
42
|
| `beqforge/cli.py` | `beqforge <subcommand> ...` — the installed entry point. Strips the subcommand off argv and hands the rest to the matching script in `tools/` unchanged. |
|
|
43
|
-
| `beqforge/explain.py` | The plain-language account a designer response leads with — `found` (reference plateau, which channels carry it and which are silent, down to where there is real bass content, judged band, goal), `correction` (the cascade, then frequency by frequency what was wanted, what the content supports and before → after), `clipping` (the sub-feed peak and how far to turn the sub down, on the request's or the assumed bass management) and `alternatives` (why each other candidate lost). Reads the `Report`; measures and decides nothing. |
|
|
44
|
-
| `beqforge/designer.py` | The `design(request) -> response` adapter between beqdesigner's `designer-interface.md` v1.1 and `beqforge.pipeline.run` — request/response dataclasses (1.1: every candidate the judge failed goes back in `rejected` with its failures, review only, beside the answer or the decline), wire-format (de)serialisation and the field-by-field mapping onto `Report`/`Candidate`/`Verdict`. Also `validate_response`, an independent re-implementation of beqdesigner's own §3-§5 rules (that repo isn't importable — PyQt6), called by the server before a response ever goes on the wire. Pure and independently testable; no socket. |
|
|
43
|
+
| `beqforge/explain.py` | The plain-language account a designer response leads with — `found` (reference plateau, which channels carry it and which are silent, down to where there is real bass content, judged band, goal), `correction` (the cascade, then frequency by frequency what was wanted, what the content supports and before → after), `clipping` (the sub-feed peak and how far to turn the sub down, on the request's or the assumed bass management), `fit` (how closely the filter as played delivers the boost the content supports — a match to the target, not a verdict on it) and `alternatives` (why each other candidate lost). Reads the `Report`; measures and decides nothing. |
|
|
44
|
+
| `beqforge/designer.py` | The `design(request) -> response` adapter between beqdesigner's `designer-interface.md` v1.1 and `beqforge.pipeline.run` — request/response dataclasses (1.1: every candidate the judge failed goes back in `rejected` with its failures, review only, beside the answer or the decline; 1.3: `residual_db` and its companions come from `Candidate.target_fit`, never `fit_error_db`; 1.4: `design_version` (`DESIGN_VERSION`, bumped only by a change that can alter an answer, guarded by `tests/test_design_golden.py`), `design_settings` (`design_settings_digest`, the non-default server settings that can change an answer) and `designer_build` on every response), wire-format (de)serialisation and the field-by-field mapping onto `Report`/`Candidate`/`Verdict`. Also `validate_response`, an independent re-implementation of beqdesigner's own §3-§5 rules (that repo isn't importable — PyQt6), called by the server before a response ever goes on the wire. Pure and independently testable; no socket. |
|
|
45
45
|
| `tools/design_beq.py` | **The entry point.** One command, a filter and its reasoning. `--charts DIR` for the pictures; writes a `.run.json.gz` record beside the material unless `--no-record`. |
|
|
46
|
-
| `tools/designer_server.py` | `beqforge serve-designer` — the HTTP transport (`designer-interface.md` §7.1) around `beqforge/designer.py`. Server-wide flags for device realisation/strategies/exclusions; everything per-title comes from the request. A `ThreadingHTTPServer`, so `/health` answers during a design, with every design run in a long-lived worker process started with "spawn" (`_DesignWorker`): the fitter forks worker processes (`beqforge.filters.PARALLEL_FITS`) when a fit escalates past one section count, and forking a multi-threaded process risks a deadlock, so the process that forks must stay single-threaded — the worker's log records come back over a `SimpleQueue`, not a `Queue`, for that reason. A worker designs one title at a time; `--workers N` (1-8) runs N of them, each request taking the next idle one, and `/health` reports N as `parallel` so a caller can size its sends from the designer. With N > 1 each worker's fit pool is the logical processors less one, divided by N and capped at `filters.FIT_WORKERS` (`fit_share`), unless `BEQ_FIT_WORKERS` is set: concurrent designs are what can use SMT threads, and pool size changes timing, never an answer. A worker that dies answers 500 and is replaced; the others carry on. SIGTERM shuts down like Ctrl-C, stopping the worker's process group, fit pool included. Every response names its build (`beqforge_revision`); `--record-dir DIR` also writes each request's run record, named by a digest of the request audio, so a review-queue entry can be replayed. `--cache-dir DIR` keeps the stage cache there (`cache.DirStore`, one file per entry, atomic): a repeat request for a title skips the analysis — 110 s cold, 50 s warm on a 2-hour 8-channel title. Each request logs a timing line (read, parse, decode, design, respond, queued); `tools/experiments/request_timing.py` reports them. `--shared-root DIR` accepts audio by reference (contract 1.2, beqdesigner's `design/designer-by-reference.md` §3): an array names a WAV relative to DIR plus the SHA-256 of its decoded column, and `beqforge/reference.py` resolves, decodes and checks it; one that cannot be honoured is a 422 naming the array, never a decline. `GET /health` reports `contract_version`, `shared_root` and `
|
|
46
|
+
| `tools/designer_server.py` | `beqforge serve-designer` — the HTTP transport (`designer-interface.md` §7.1) around `beqforge/designer.py`. Server-wide flags for device realisation/strategies/exclusions; everything per-title comes from the request. A `ThreadingHTTPServer`, so `/health` answers during a design, with every design run in a long-lived worker process started with "spawn" (`_DesignWorker`): the fitter forks worker processes (`beqforge.filters.PARALLEL_FITS`) when a fit escalates past one section count, and forking a multi-threaded process risks a deadlock, so the process that forks must stay single-threaded — the worker's log records come back over a `SimpleQueue`, not a `Queue`, for that reason. A worker designs one title at a time; `--workers N` (1-8) runs N of them, each request taking the next idle one, and `/health` reports N as `parallel` so a caller can size its sends from the designer. With N > 1 each worker's fit pool is the logical processors less one, divided by N and capped at `filters.FIT_WORKERS` (`fit_share`), unless `BEQ_FIT_WORKERS` is set; `--cores N` instead caps the designs' total at N, split evenly, still at most `filters.FIT_WORKERS` each: concurrent designs are what can use SMT threads, and pool size changes timing, never an answer. A worker that dies answers 500 and is replaced; the others carry on. SIGTERM shuts down like Ctrl-C, stopping the worker's process group, fit pool included. Every response names its build (`beqforge_revision`); `--record-dir DIR` also writes each request's run record, named by a digest of the request audio, so a review-queue entry can be replayed. `--cache-dir DIR` keeps the stage cache there (`cache.DirStore`, one file per entry, atomic): a repeat request for a title skips the analysis — 110 s cold, 50 s warm on a 2-hour 8-channel title. Each request logs a timing line (read, parse, decode, design, respond, queued); `tools/experiments/request_timing.py` reports them. `--shared-root DIR` accepts audio by reference (contract 1.2, beqdesigner's `design/designer-by-reference.md` §3): an array names a WAV relative to DIR plus the SHA-256 of its decoded column, and `beqforge/reference.py` resolves, decodes and checks it; one that cannot be honoured is a 422 naming the array, never a decline. `GET /health` reports `contract_version`, `shared_root`, `parallel`, and 1.4's `design_version`, `design_settings`, `design_settings_detail` and `designer_build`. |
|
|
47
47
|
| `tools/replay.py` | Redraw charts and export to beqdesigner from a record — no rerun, no extraction. Refuses on a stale record unless `--force`. |
|
|
48
48
|
| `tools/extract.py` | ffmpeg → 1 kHz per-channel `.npz`. Requires an explicit supported channel layout; preserves layout provenance. Needs no beqdesigner. |
|
|
49
49
|
| `tools/summarise.py` | Sanity-check an extraction before using it. |
|
|
@@ -203,6 +203,16 @@ open questions it resolves. An item with no recorded outcome is not done.
|
|
|
203
203
|
|
|
204
204
|
## Gotchas
|
|
205
205
|
|
|
206
|
+
* A change that can alter an answer for an unchanged request (candidates, rejected designs, a
|
|
207
|
+
decline or a 1.3 fit figure) bumps `designer.DESIGN_VERSION` and regenerates
|
|
208
|
+
`tests/fixtures/design_golden.json` (`BEQFORGE_REGENERATE_GOLDEN=1 uv run pytest
|
|
209
|
+
tests/test_design_golden.py`) in the same commit. Callers decide what to redesign from it.
|
|
210
|
+
Wording, speed, packaging and new outputs don't bump it. If the probe shows a moved verdict
|
|
211
|
+
or target, the golden will usually fail too, but it covers only a few synthetic requests:
|
|
212
|
+
a bump follows from what the change can do, not from whether the golden noticed. It leaves
|
|
213
|
+
out `counterfactual` and `parametric` because their fits differ across processors
|
|
214
|
+
(TODO, "Fit sensitivity to arithmetic"): regenerating it on one machine doesn't make those
|
|
215
|
+
hold on CI.
|
|
206
216
|
* RBJ biquad formulae exist twice — `biquad.py`'s class hierarchy and `filters.py`'s
|
|
207
217
|
`_sos_from_parameters`. Fix both or neither; the test asserting they agree is what keeps that
|
|
208
218
|
honest.
|
|
@@ -425,18 +435,19 @@ least `min_judge_octaves` above the floor — no fixed top (historical review T6
|
|
|
425
435
|
|
|
426
436
|
### Publication, playback and verification
|
|
427
437
|
|
|
428
|
-
* **Robustness is measured
|
|
438
|
+
* **Robustness is measured six ways, and they answer different questions.** Do not add one
|
|
429
439
|
to another, or compare one against a limit meant for another:
|
|
430
440
|
|
|
431
441
|
| Quantity | What it measures | Where, over what |
|
|
432
442
|
| --- | --- | --- |
|
|
433
443
|
| Fit objective (`_fit_structure`'s cost) | the larger of the worst error against the target and the worst change exact-coefficient device quantisation makes to the response | the optimiser's raw parameters, over the fit's scoring band (`residual_band_hz`, widened to cover placements) |
|
|
434
|
-
| `fit_error_db`
|
|
444
|
+
| `fit_error_db` | that objective — **except** that when `_prune` drops a section it becomes the pure worst error against the target, without the quantisation term. Two meanings in one field; the escalation's `residual_target_db` compares against it, so it is left alone. Record-only outside the fitter, never in the response | as above |
|
|
445
|
+
| `TargetFit` / designer `residual_db` and its 1.3 companions | the cascade that plays against the target it was fitted to: worst absolute error, where and which way, RMS and share within `fit_tolerance_db` both weighted by the boost the target asks for. Reported, never gated; a match to the target, not evidence the target is right | published parameters, quantised coefficients at the device rate, design grid, over the band the fit was actually scored on (`_fit_band_hz`, or `residual_band_hz` for `parametric`) |
|
|
435
446
|
| Drift, p90 (`_published_drift`, `Verdict.drift_db`) | the 90th percentile, over `drift_samples` jitters of the *published* parameters by their rounding step, of the worst change quantisation makes — how fragile the neighbourhood is, not what the published filter does | published parameters (since the F1 follow-up both use them), 3-400 Hz design grid |
|
|
436
447
|
| Exposure (`_exposure_db`) | the fallback's ranking when no fit reaches the residual target: the residual, plus the drift if over `max_drift_db`. A sampled heuristic, not a bound on the published response | as its two terms |
|
|
437
448
|
| `device_error_db` and the corrected curve | what the device will actually do: the exact published, quantised response against the unquantised one (`device_error_db`), and that response applied to the programme (`verify`) — the only one that is not a model | published parameters, quantised coefficients, design grid / the programme |
|
|
438
449
|
|
|
439
|
-
Strict Jury stability (`unstable_sections`) is separate from all
|
|
450
|
+
Strict Jury stability (`unstable_sections`) is separate from all six and is a veto: a
|
|
440
451
|
cascade that is unstable at its published, quantised coefficients is never accepted, and its
|
|
441
452
|
drift and exposure are infinite.
|
|
442
453
|
|