beqforge 0.2.0__tar.gz → 0.2.2__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.2/.dockerignore +14 -0
- beqforge-0.2.2/.github/workflows/build-designer-image.yml +119 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/.github/workflows/build-device-check.yml +16 -1
- {beqforge-0.2.0 → beqforge-0.2.2}/.github/workflows/build-executable.yml +5 -3
- beqforge-0.2.2/.github/workflows/tests.yml +140 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/AGENTS.md +7 -2
- {beqforge-0.2.0 → beqforge-0.2.2}/PKG-INFO +37 -1
- {beqforge-0.2.0 → beqforge-0.2.2}/README.md +36 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/TODO.md +1 -1
- {beqforge-0.2.0 → beqforge-0.2.2}/beq_common/__init__.py +1 -1
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge/AGENTS.md +1 -1
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge/diagnose.py +83 -23
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge/pipeline.py +54 -2
- {beqforge-0.2.0 → beqforge-0.2.2}/beqoptimiser/AGENTS.md +4 -2
- {beqforge-0.2.0 → beqforge-0.2.2}/beqoptimiser/cache.py +12 -6
- {beqforge-0.2.0 → beqforge-0.2.2}/beqoptimiser/core.py +7 -3
- {beqforge-0.2.0 → beqforge-0.2.2}/beqoptimiser/data/seed-manifest.json +17 -13
- {beqforge-0.2.0 → beqforge-0.2.2}/beqoptimiser/data/seed.json.gz +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/docs/optimiser.md +9 -5
- beqforge-0.2.2/packaging/designer/Dockerfile +42 -0
- beqforge-0.2.2/packaging/designer/smoke.py +187 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/plans/F2-catalogue-filter-optimisation.md +20 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/plans/done-design-and-pipeline.md +151 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/pyproject.toml +18 -1
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_channel_evidence.py +3 -1
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_designer_server.py +81 -48
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_diagnose.py +30 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_exclusions.py +15 -0
- beqforge-0.2.2/tests/test_design_hum.py +63 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_references.py +35 -0
- beqforge-0.2.2/tests/test_designer_image.py +89 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_designer_server_startup.py +17 -18
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_optimiser_cache.py +21 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/designer_server.py +268 -59
- {beqforge-0.2.0 → beqforge-0.2.2}/uv.lock +160 -76
- beqforge-0.2.0/.github/workflows/tests.yml +0 -101
- {beqforge-0.2.0 → beqforge-0.2.2}/.github/dependabot.yml +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/.github/workflows/dependabot-auto-merge.yaml +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/.gitignore +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/.idea/.gitignore +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/.idea/beqforge.iml +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/.idea/editor.xml +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/.idea/jsonSchemas.xml +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/.idea/misc.xml +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/.idea/modules.xml +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/.idea/vcs.xml +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/CLAUDE.md +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/LICENCE.md +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beq_common/AGENTS.md +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beq_common/biquad.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beq_common/provenance.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beq_common/publication.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beq_common/types.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge/__init__.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge/accept.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge/beqd.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge/biquad.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge/cache.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge/charts.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge/cli.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge/design.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge/designer.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge/explain.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge/extraction.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge/filters.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge/harness.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge/identify.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge/material.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge/record.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge/reference.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge/rolloff.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge/verify.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge-device-check.spec +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge.spec +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge_device_check/AGENTS.md +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge_device_check/__init__.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge_device_check/analyse.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge_device_check/audio.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge_device_check/catalogue.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge_device_check/cli.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge_device_check/coefficients.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge_device_check/engines.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge_device_check/evidence.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge_device_check/manifest.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge_device_check/measurement.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge_device_check/profiles.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge_device_check/qualification.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge_device_check/report.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge_device_check/transactions.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqforge_device_check/verify.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqoptimiser/__init__.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/beqoptimiser/cli.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/docs/device-check-examples/minidsp-2x4hd-usb-loopback.json +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/docs/device-check-examples/simulation.json +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/docs/device-check.md +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/docs/optimiser-report/README.md +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/docs/optimiser-report/assets/aggregate-48000.png +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/docs/optimiser-report/assets/aggregate-96000.png +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/docs/optimiser-report/assets/example-1.png +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/docs/optimiser-report/assets/example-2.png +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/docs/optimiser-report/entry-results.json.gz +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/docs/optimiser-report/statistics.json +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/evidence_validation.json +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/negative_corpus.json +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/packaging/device-check/NOTICE.txt +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/plans/F2-device-precision-validation.md +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/plans/F2-package-consolidation.md +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/plans/R2a-server-stage-cache.md +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/plans/done-baseline-evidence.md +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/plans/research-design-decisions.md +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/plans/test-portability-and-runtime.md +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/__init__.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/conftest.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/fixtures/optimiser_seed_entry.json +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_cli.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_accept.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_cache.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_cache_stores.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_charts.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_corpus.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_design.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_designer.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_device_rate.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_evidence.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_evidence_excess.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_explain.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_extraction.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_filters.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_frozen_cache.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_guard.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_harness.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_headroom.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_identify.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_jacobian.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_parallel.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_pipeline.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_playback.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_playback_headroom.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_publication.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_record.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_record_fingerprint.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_reference.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_rolloff.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_strategy_config.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_timings.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_design_verify.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_device_check_analyse.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_device_check_catalogue.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_device_check_catalogue_predict.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_device_check_cli.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_device_check_engines.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_device_check_evidence.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_device_check_examples.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_device_check_manifest.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_device_check_measurement.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_device_check_qualification.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_device_check_transactions.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_device_check_verify.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_extract_layout.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_optimiser.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_optimiser_report.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tests/test_package_boundaries.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/__init__.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/build_optimiser_seed.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/design_beq.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/experiments/a1_sweep.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/experiments/compare_records.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/experiments/compare_verdicts.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/experiments/inject_variants.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/experiments/p14_compare.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/experiments/p14_jacobian.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/experiments/p14_run_pipeline.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/experiments/p14_surrogate.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/experiments/p18_compare_greedy.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/experiments/p18_compare_seeded.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/experiments/p18_compare_structure.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/experiments/p18_greedy.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/experiments/p18_seeded.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/experiments/p18_structure.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/experiments/p20_smooth_drift.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/experiments/probe.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/experiments/refit_probe.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/experiments/request_timing.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/experiments/score_injected.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/experiments/t8_floor_rules.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/extract.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/ledger_template.html +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/negative_corpus.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/optimiser_catalogue_report.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/prepare_device_check_helper.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/render_ledger.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/replay.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/seed_optimiser_cache.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/smoke_test_device_check.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/smoke_test_exe.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/smoke_test_install.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/summarise.py +0 -0
- {beqforge-0.2.0 → beqforge-0.2.2}/tools/validate_evidence.py +0 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
name: Designer image
|
|
2
|
+
|
|
3
|
+
# The designer as a container, beside a beqdesigner pipeline service (README, "Running the
|
|
4
|
+
# designer in a container"). Every change to what goes into it is built and smoke-tested;
|
|
5
|
+
# a vX.Y.Z tag also publishes linux/amd64 and linux/arm64 to GHCR as ghcr.io/<owner>/beqforge-designer.
|
|
6
|
+
# A manual run with `publish` publishes the image of an existing release from a later commit,
|
|
7
|
+
# only if the designer's runtime code is identical to that release's tag (as when the image
|
|
8
|
+
# recipe arrived after the release).
|
|
9
|
+
on:
|
|
10
|
+
push:
|
|
11
|
+
branches: [main]
|
|
12
|
+
tags: ['v*']
|
|
13
|
+
paths:
|
|
14
|
+
- 'packaging/designer/**'
|
|
15
|
+
- '.dockerignore'
|
|
16
|
+
- '.github/workflows/build-designer-image.yml'
|
|
17
|
+
- 'pyproject.toml'
|
|
18
|
+
- 'uv.lock'
|
|
19
|
+
- 'beq_common/**'
|
|
20
|
+
- 'beqforge/**'
|
|
21
|
+
- 'tools/designer_server.py'
|
|
22
|
+
pull_request:
|
|
23
|
+
paths:
|
|
24
|
+
- 'packaging/designer/**'
|
|
25
|
+
- '.dockerignore'
|
|
26
|
+
- '.github/workflows/build-designer-image.yml'
|
|
27
|
+
workflow_dispatch:
|
|
28
|
+
inputs:
|
|
29
|
+
publish:
|
|
30
|
+
description: Publish the image as beq_common.__version__ (its vX.Y.Z tag must exist, with identical designer code)
|
|
31
|
+
type: boolean
|
|
32
|
+
default: false
|
|
33
|
+
|
|
34
|
+
permissions:
|
|
35
|
+
contents: read
|
|
36
|
+
|
|
37
|
+
concurrency:
|
|
38
|
+
group: designer-image-${{ github.ref }}
|
|
39
|
+
cancel-in-progress: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
|
40
|
+
|
|
41
|
+
jobs:
|
|
42
|
+
image:
|
|
43
|
+
runs-on: ubuntu-24.04
|
|
44
|
+
timeout-minutes: 60 # the arm64 image builds under emulation
|
|
45
|
+
permissions:
|
|
46
|
+
contents: read
|
|
47
|
+
packages: write
|
|
48
|
+
steps:
|
|
49
|
+
- uses: actions/checkout@v7
|
|
50
|
+
with:
|
|
51
|
+
fetch-depth: 0 # the release tags, for a manual publish's check
|
|
52
|
+
- name: Choose whether to publish
|
|
53
|
+
id: publish
|
|
54
|
+
shell: bash
|
|
55
|
+
run: |
|
|
56
|
+
if [[ "$GITHUB_REF" == refs/tags/* || "${{ inputs.publish }}" == true ]]; then
|
|
57
|
+
echo "publish=true" >> "$GITHUB_OUTPUT"
|
|
58
|
+
else
|
|
59
|
+
echo "publish=false" >> "$GITHUB_OUTPUT"
|
|
60
|
+
fi
|
|
61
|
+
- name: Set image version
|
|
62
|
+
id: version
|
|
63
|
+
shell: bash
|
|
64
|
+
run: |
|
|
65
|
+
version="$(python3 -c "import re, pathlib; print(re.search(r'__version__ = \"([^\"]+)\"', pathlib.Path('beq_common/__init__.py').read_text()).group(1))")"
|
|
66
|
+
if [[ "$GITHUB_REF" == refs/tags/* && "$GITHUB_REF_NAME" != "v$version" ]]; then
|
|
67
|
+
echo "tag $GITHUB_REF_NAME does not match beq_common.__version__ $version" >&2
|
|
68
|
+
exit 1
|
|
69
|
+
fi
|
|
70
|
+
if [[ "$GITHUB_REF" != refs/tags/* && "${{ inputs.publish }}" == true ]]; then
|
|
71
|
+
# the image says it is release $version: what it runs must be exactly that release's
|
|
72
|
+
if ! git diff --quiet "v$version" HEAD -- beqforge beq_common tools/designer_server.py; then
|
|
73
|
+
echo "the designer code differs from v$version: tag a release instead" >&2
|
|
74
|
+
git diff --stat "v$version" HEAD -- beqforge beq_common tools/designer_server.py >&2
|
|
75
|
+
exit 1
|
|
76
|
+
fi
|
|
77
|
+
fi
|
|
78
|
+
echo "version=$version" >> "$GITHUB_OUTPUT"
|
|
79
|
+
- name: Build image for smoke test
|
|
80
|
+
run: |
|
|
81
|
+
docker build -f packaging/designer/Dockerfile -t beqforge-designer:smoke \
|
|
82
|
+
--build-arg VERSION="${{ steps.version.outputs.version }}" --build-arg REVISION="$GITHUB_SHA" .
|
|
83
|
+
- name: Health, and a design inline and by reference
|
|
84
|
+
run: python3 packaging/designer/smoke.py beqforge-designer:smoke
|
|
85
|
+
- uses: docker/setup-qemu-action@v4
|
|
86
|
+
if: steps.publish.outputs.publish == 'true'
|
|
87
|
+
- uses: docker/setup-buildx-action@v4
|
|
88
|
+
if: steps.publish.outputs.publish == 'true'
|
|
89
|
+
- name: Log in to GHCR
|
|
90
|
+
if: steps.publish.outputs.publish == 'true'
|
|
91
|
+
uses: docker/login-action@v4
|
|
92
|
+
with:
|
|
93
|
+
registry: ghcr.io
|
|
94
|
+
username: ${{ github.actor }}
|
|
95
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
96
|
+
- name: Choose tags
|
|
97
|
+
id: tags
|
|
98
|
+
if: steps.publish.outputs.publish == 'true'
|
|
99
|
+
shell: bash
|
|
100
|
+
run: |
|
|
101
|
+
image="ghcr.io/${GITHUB_REPOSITORY_OWNER,,}/beqforge-designer"
|
|
102
|
+
version="${{ steps.version.outputs.version }}"
|
|
103
|
+
tags="$image:$version"
|
|
104
|
+
if [[ ! "$version" =~ (a|b|rc|dev)[0-9]*$ ]]; then
|
|
105
|
+
tags="$tags,$image:latest"
|
|
106
|
+
fi
|
|
107
|
+
echo "tags=$tags" >> "$GITHUB_OUTPUT"
|
|
108
|
+
- name: Build and publish amd64 and arm64
|
|
109
|
+
if: steps.publish.outputs.publish == 'true'
|
|
110
|
+
uses: docker/build-push-action@v7
|
|
111
|
+
with:
|
|
112
|
+
context: .
|
|
113
|
+
file: packaging/designer/Dockerfile
|
|
114
|
+
platforms: linux/amd64,linux/arm64
|
|
115
|
+
push: true
|
|
116
|
+
tags: ${{ steps.tags.outputs.tags }}
|
|
117
|
+
build-args: |
|
|
118
|
+
VERSION=${{ steps.version.outputs.version }}
|
|
119
|
+
REVISION=${{ github.sha }}
|
|
@@ -2,9 +2,19 @@ name: Build device-check preview
|
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
workflow_dispatch:
|
|
5
|
+
# only when something that affects the packaged executable changes
|
|
5
6
|
push:
|
|
6
7
|
branches:
|
|
7
8
|
- '**'
|
|
9
|
+
paths:
|
|
10
|
+
- 'beqforge_device_check/**'
|
|
11
|
+
- 'beqforge-device-check.spec'
|
|
12
|
+
- 'tools/prepare_device_check_helper.py'
|
|
13
|
+
- 'tools/smoke_test_device_check.py'
|
|
14
|
+
- 'packaging/device-check/**'
|
|
15
|
+
- '.github/workflows/build-device-check.yml'
|
|
16
|
+
- 'pyproject.toml'
|
|
17
|
+
- 'uv.lock'
|
|
8
18
|
pull_request:
|
|
9
19
|
paths:
|
|
10
20
|
- 'beqforge_device_check/**'
|
|
@@ -16,6 +26,11 @@ on:
|
|
|
16
26
|
- 'pyproject.toml'
|
|
17
27
|
- 'uv.lock'
|
|
18
28
|
|
|
29
|
+
# a newer push to a branch supersedes its in-progress preview build
|
|
30
|
+
concurrency:
|
|
31
|
+
group: device-check-preview-${{ github.event_name }}-${{ github.ref }}
|
|
32
|
+
cancel-in-progress: true
|
|
33
|
+
|
|
19
34
|
permissions:
|
|
20
35
|
contents: read
|
|
21
36
|
|
|
@@ -46,7 +61,7 @@ jobs:
|
|
|
46
61
|
executable: beqforge-device-check
|
|
47
62
|
steps:
|
|
48
63
|
- uses: actions/checkout@v7
|
|
49
|
-
- uses: astral-sh/setup-uv@v10.
|
|
64
|
+
- uses: astral-sh/setup-uv@v10.3.0
|
|
50
65
|
with:
|
|
51
66
|
enable-cache: true
|
|
52
67
|
- name: Install Linux audio and USB libraries
|
|
@@ -25,7 +25,7 @@ jobs:
|
|
|
25
25
|
steps:
|
|
26
26
|
- name: Find successful tests for the tagged commit
|
|
27
27
|
id: check
|
|
28
|
-
uses: actions/github-script@
|
|
28
|
+
uses: actions/github-script@v9
|
|
29
29
|
with:
|
|
30
30
|
script: |
|
|
31
31
|
core.setOutput('reusable', 'false');
|
|
@@ -72,6 +72,8 @@ jobs:
|
|
|
72
72
|
needs: previous_tests
|
|
73
73
|
if: needs.previous_tests.outputs.reusable != 'true'
|
|
74
74
|
uses: ./.github/workflows/tests.yml
|
|
75
|
+
# CODECOV_TOKEN, for the coverage upload
|
|
76
|
+
secrets: inherit
|
|
75
77
|
permissions:
|
|
76
78
|
contents: read
|
|
77
79
|
|
|
@@ -102,7 +104,7 @@ jobs:
|
|
|
102
104
|
contents: read
|
|
103
105
|
steps:
|
|
104
106
|
- uses: actions/checkout@v7
|
|
105
|
-
- uses: astral-sh/setup-uv@v10.
|
|
107
|
+
- uses: astral-sh/setup-uv@v10.3.0
|
|
106
108
|
with:
|
|
107
109
|
enable-cache: true
|
|
108
110
|
- name: Build wheel and source distribution
|
|
@@ -191,7 +193,7 @@ jobs:
|
|
|
191
193
|
- uses: actions/checkout@v7
|
|
192
194
|
|
|
193
195
|
- name: Install uv
|
|
194
|
-
uses: astral-sh/setup-uv@v10.
|
|
196
|
+
uses: astral-sh/setup-uv@v10.3.0
|
|
195
197
|
with:
|
|
196
198
|
enable-cache: true
|
|
197
199
|
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
# Fits already control their own process parallelism. Avoid multiplying each worker
|
|
4
|
+
# by a BLAS thread pool on the small hosted runners.
|
|
5
|
+
env:
|
|
6
|
+
OPENBLAS_NUM_THREADS: '1'
|
|
7
|
+
OMP_NUM_THREADS: '1'
|
|
8
|
+
MKL_NUM_THREADS: '1'
|
|
9
|
+
VECLIB_MAXIMUM_THREADS: '1'
|
|
10
|
+
|
|
11
|
+
on:
|
|
12
|
+
workflow_call:
|
|
13
|
+
push:
|
|
14
|
+
branches:
|
|
15
|
+
- '**'
|
|
16
|
+
|
|
17
|
+
# A newer push to a feature branch supersedes its in-progress run. main and tag (release) runs are
|
|
18
|
+
# grouped per commit so they are never cancelled or queued behind each other: a release reuses
|
|
19
|
+
# main's result for the tagged commit.
|
|
20
|
+
concurrency:
|
|
21
|
+
group: tests-${{ github.event_name }}-${{ github.ref }}-${{ (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')) && github.sha || 'latest' }}
|
|
22
|
+
cancel-in-progress: ${{ github.ref != 'refs/heads/main' && !startsWith(github.ref, 'refs/tags/') }}
|
|
23
|
+
|
|
24
|
+
jobs:
|
|
25
|
+
test:
|
|
26
|
+
name: Test (${{ matrix.os }})
|
|
27
|
+
runs-on: ${{ matrix.os }}
|
|
28
|
+
strategy:
|
|
29
|
+
fail-fast: false
|
|
30
|
+
matrix:
|
|
31
|
+
# feature branch pushes only test on Linux; main, tags (releases) and manual runs test all three
|
|
32
|
+
os: ${{ fromJSON(github.event_name == 'push' && startsWith(github.ref, 'refs/heads/') && github.ref != 'refs/heads/main' && '["ubuntu-latest"]' || '["ubuntu-latest", "macos-latest", "windows-latest"]') }}
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/checkout@v7
|
|
35
|
+
|
|
36
|
+
# brew's and choco's default ffmpeg both lack the libsoxr resampler tools/extract.py
|
|
37
|
+
# prefers, unlike Ubuntu's apt package — but extract.py now detects that at runtime and
|
|
38
|
+
# falls back to ffmpeg's default resampler, so any ordinary ffmpeg install is fine here.
|
|
39
|
+
# apt mirrors have repeatedly taken from 8 to over 50 minutes to serve ffmpeg's dependencies on
|
|
40
|
+
# hosted runners, so use a static build from GitHub releases (which also includes libsoxr) and
|
|
41
|
+
# only fall back to apt if that download fails.
|
|
42
|
+
- name: Install ffmpeg (Linux)
|
|
43
|
+
if: runner.os == 'Linux'
|
|
44
|
+
timeout-minutes: 10
|
|
45
|
+
run: |
|
|
46
|
+
if command -v ffmpeg >/dev/null && command -v ffprobe >/dev/null; then
|
|
47
|
+
exit 0
|
|
48
|
+
fi
|
|
49
|
+
url=https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-linux64-gpl.tar.xz
|
|
50
|
+
if curl -fsSL --retry 3 --max-time 300 "$url" -o "$RUNNER_TEMP/ffmpeg.tar.xz"; then
|
|
51
|
+
mkdir -p "$RUNNER_TEMP/ffmpeg"
|
|
52
|
+
tar -xJf "$RUNNER_TEMP/ffmpeg.tar.xz" -C "$RUNNER_TEMP/ffmpeg" --strip-components=1
|
|
53
|
+
echo "$RUNNER_TEMP/ffmpeg/bin" >> "$GITHUB_PATH"
|
|
54
|
+
else
|
|
55
|
+
# the Azure mirror is the slowest, use Ubuntu's archive mirror
|
|
56
|
+
sudo sed -i 's|http://azure.archive.ubuntu.com/ubuntu|http://archive.ubuntu.com/ubuntu|g' /etc/apt/apt-mirrors.txt
|
|
57
|
+
sudo apt-get update
|
|
58
|
+
sudo apt-get install -y --no-install-recommends ffmpeg
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
- name: Install ffmpeg (macOS)
|
|
62
|
+
if: runner.os == 'macOS'
|
|
63
|
+
run: brew install ffmpeg
|
|
64
|
+
|
|
65
|
+
- name: Install ffmpeg (Windows)
|
|
66
|
+
if: runner.os == 'Windows'
|
|
67
|
+
run: choco install ffmpeg -y
|
|
68
|
+
|
|
69
|
+
- name: Install uv
|
|
70
|
+
uses: astral-sh/setup-uv@v10.3.0
|
|
71
|
+
with:
|
|
72
|
+
enable-cache: true
|
|
73
|
+
|
|
74
|
+
- name: Install dependencies
|
|
75
|
+
run: uv sync --locked --all-groups --extra designer
|
|
76
|
+
|
|
77
|
+
# the device-check tests run in their own job below, in parallel, with the extra installed
|
|
78
|
+
- name: Run tests
|
|
79
|
+
run: uv run --extra designer pytest -n auto --durations=25 --ignore-glob='tests/test_device_check_*.py' --cov --cov-report=xml
|
|
80
|
+
|
|
81
|
+
- name: Upload coverage
|
|
82
|
+
if: ${{ !cancelled() }}
|
|
83
|
+
uses: codecov/codecov-action@v7.1.1
|
|
84
|
+
with:
|
|
85
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
86
|
+
files: coverage.xml
|
|
87
|
+
disable_search: true
|
|
88
|
+
flags: designer,${{ runner.os }}
|
|
89
|
+
name: designer-${{ runner.os }}
|
|
90
|
+
|
|
91
|
+
device-check:
|
|
92
|
+
name: Device check (${{ matrix.os }})
|
|
93
|
+
runs-on: ${{ matrix.os }}
|
|
94
|
+
strategy:
|
|
95
|
+
fail-fast: false
|
|
96
|
+
matrix:
|
|
97
|
+
# feature branch pushes only test on Linux; main, tags (releases) and manual runs test all three
|
|
98
|
+
os: ${{ fromJSON(github.event_name == 'push' && startsWith(github.ref, 'refs/heads/') && github.ref != 'refs/heads/main' && '["ubuntu-latest"]' || '["ubuntu-latest", "macos-latest", "windows-latest"]') }}
|
|
99
|
+
steps:
|
|
100
|
+
- uses: actions/checkout@v7
|
|
101
|
+
|
|
102
|
+
- name: Install uv
|
|
103
|
+
uses: astral-sh/setup-uv@v10.3.0
|
|
104
|
+
with:
|
|
105
|
+
enable-cache: true
|
|
106
|
+
|
|
107
|
+
# without the extra, most of these tests skip on importorskip('pyfar') rather than fail
|
|
108
|
+
- name: Install dependencies
|
|
109
|
+
run: uv sync --locked --all-groups --extra device-check
|
|
110
|
+
|
|
111
|
+
- name: Run device-check tests
|
|
112
|
+
shell: bash
|
|
113
|
+
run: uv run --extra device-check pytest -n auto --durations=25 tests/test_device_check_*.py --cov --cov-report=xml
|
|
114
|
+
|
|
115
|
+
- name: Upload coverage
|
|
116
|
+
if: ${{ !cancelled() }}
|
|
117
|
+
uses: codecov/codecov-action@v7.1.1
|
|
118
|
+
with:
|
|
119
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
120
|
+
files: coverage.xml
|
|
121
|
+
disable_search: true
|
|
122
|
+
flags: device-check,${{ runner.os }}
|
|
123
|
+
name: device-check-${{ runner.os }}
|
|
124
|
+
|
|
125
|
+
package-profiles:
|
|
126
|
+
name: Install profile (${{ matrix.profile }})
|
|
127
|
+
runs-on: ubuntu-latest
|
|
128
|
+
strategy:
|
|
129
|
+
fail-fast: false
|
|
130
|
+
matrix:
|
|
131
|
+
profile: [base, designer, optimiser, device-check, all]
|
|
132
|
+
steps:
|
|
133
|
+
- uses: actions/checkout@v7
|
|
134
|
+
- uses: astral-sh/setup-uv@v10.3.0
|
|
135
|
+
with:
|
|
136
|
+
enable-cache: true
|
|
137
|
+
- name: Build the single distribution
|
|
138
|
+
run: uv build --out-dir dist
|
|
139
|
+
- name: Verify isolated wheel installation and command
|
|
140
|
+
run: uv run --no-project --python 3.13 python tools/smoke_test_install.py --wheel-dir dist --profile '${{ matrix.profile }}'
|
|
@@ -55,11 +55,16 @@ importing them; use an isolated worktree for simultaneous experiments.
|
|
|
55
55
|
|
|
56
56
|
The root build configuration includes every workflow and the optimiser's bundled seed in one
|
|
57
57
|
wheel and source distribution. `.github/workflows/tests.yml` checks isolated dependency
|
|
58
|
-
profiles
|
|
58
|
+
profiles and runs the suite in parallel (`pytest -n auto`, so tests must not share mutable state
|
|
59
|
+
or fixed ports), with coverage (`--cov`, configured in `pyproject.toml`, `multiprocessing`
|
|
60
|
+
children included) uploaded to Codecov by each test job using the `CODECOV_TOKEN` secret;
|
|
61
|
+
release runs inherit it. Feature branch pushes test on Linux only and a newer push cancels the branch's
|
|
62
|
+
previous run; `main`, tags and manual runs test Linux, macOS and Windows and are never cancelled. `.github/workflows/build-executable.yml` builds release executables and distributions;
|
|
59
63
|
tag pushes publish to PyPI after validation and successful executable builds. Tags must match
|
|
60
64
|
the shared version exactly (`vX.Y.Z`). Manual runs build artifacts without publishing.
|
|
61
65
|
PyPI uses the `pypi` environment and Trusted Publishing; setup details are in the README.
|
|
62
|
-
Device-check preview executable packaging has its own workflow, not a separate Python release
|
|
66
|
+
Device-check preview executable packaging has its own workflow, not a separate Python release;
|
|
67
|
+
it only runs when device-check code, packaging or dependencies change.
|
|
63
68
|
|
|
64
69
|
## Conventions
|
|
65
70
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: beqforge
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
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
|
|
@@ -248,6 +248,42 @@ the same diagnosis, then the build in brackets. `beqforge/designer.py` is the
|
|
|
248
248
|
pure `design(request) -> response` adapter, independently testable without a socket;
|
|
249
249
|
`tools/designer_server.py` is the HTTP transport around it.
|
|
250
250
|
|
|
251
|
+
### Running the designer in a container
|
|
252
|
+
|
|
253
|
+
`ghcr.io/3ll3d00d/beqforge-designer:<version>` runs `beqforge serve-designer` for a beqdesigner
|
|
254
|
+
pipeline service beside it (beqdesigner's `docker/compose.example.yaml` runs both). It is
|
|
255
|
+
published for linux/amd64 and linux/arm64 by `.github/workflows/build-designer-image.yml` on
|
|
256
|
+
each `vX.Y.Z` tag, from the same commit as that release; `latest` follows the newest release.
|
|
257
|
+
A manual run of that workflow with `publish` set publishes the current version's image from a
|
|
258
|
+
later commit, and refuses unless the designer's code is identical to that version's tag (0.2.0's
|
|
259
|
+
image was published this way, the recipe having arrived after the release).
|
|
260
|
+
Pin a version: a catalogue designed over several days should come from one designer build.
|
|
261
|
+
|
|
262
|
+
The image's defaults, all overridable with the environment variables above:
|
|
263
|
+
|
|
264
|
+
| Setting | Default | Mount |
|
|
265
|
+
| --- | --- | --- |
|
|
266
|
+
| `BEQFORGE_HOST`, `BEQFORGE_PORT` | `0.0.0.0`, `8420` | |
|
|
267
|
+
| `BEQFORGE_SHARED_ROOT` | `/work` | the pipeline's work directory, so audio is sent by reference |
|
|
268
|
+
| `BEQFORGE_CACHE_DIR` | `/cache` | a volume, so a restart keeps the stage cache |
|
|
269
|
+
| `BEQFORGE_RECORD_DIR` | unset | set it (for example to `/records`, and mount that) to keep run records |
|
|
270
|
+
|
|
271
|
+
It runs as uid/gid 1000 (`--build-arg PUID=… PGID=…` to change), which must be able to write
|
|
272
|
+
the shared root and the cache: the server checks both with a write at startup. Its
|
|
273
|
+
`HEALTHCHECK` asks `/health`, which answers while a design runs: designs run in a separate
|
|
274
|
+
worker process, one at a time, so a caller sending several at once queues them; size the
|
|
275
|
+
caller's timeout for that. `docker stop` stops a design in progress.
|
|
276
|
+
|
|
277
|
+
Build and check it locally from the repository root:
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
docker build -f packaging/designer/Dockerfile -t beqforge-designer .
|
|
281
|
+
python3 packaging/designer/smoke.py beqforge-designer
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
The smoke script checks `/health` and one design inline and one by reference;
|
|
285
|
+
`tests/test_designer_image.py` runs the same check against an in-process server.
|
|
286
|
+
|
|
251
287
|
For a machine without Python/`uv`, a standalone `beqforge` executable (every subcommand,
|
|
252
288
|
including `serve-designer`) is built for Linux/macOS/Windows by
|
|
253
289
|
`.github/workflows/build-executable.yml` on every `vX.Y.Z` tag, and attached to the matching
|
|
@@ -222,6 +222,42 @@ the same diagnosis, then the build in brackets. `beqforge/designer.py` is the
|
|
|
222
222
|
pure `design(request) -> response` adapter, independently testable without a socket;
|
|
223
223
|
`tools/designer_server.py` is the HTTP transport around it.
|
|
224
224
|
|
|
225
|
+
### Running the designer in a container
|
|
226
|
+
|
|
227
|
+
`ghcr.io/3ll3d00d/beqforge-designer:<version>` runs `beqforge serve-designer` for a beqdesigner
|
|
228
|
+
pipeline service beside it (beqdesigner's `docker/compose.example.yaml` runs both). It is
|
|
229
|
+
published for linux/amd64 and linux/arm64 by `.github/workflows/build-designer-image.yml` on
|
|
230
|
+
each `vX.Y.Z` tag, from the same commit as that release; `latest` follows the newest release.
|
|
231
|
+
A manual run of that workflow with `publish` set publishes the current version's image from a
|
|
232
|
+
later commit, and refuses unless the designer's code is identical to that version's tag (0.2.0's
|
|
233
|
+
image was published this way, the recipe having arrived after the release).
|
|
234
|
+
Pin a version: a catalogue designed over several days should come from one designer build.
|
|
235
|
+
|
|
236
|
+
The image's defaults, all overridable with the environment variables above:
|
|
237
|
+
|
|
238
|
+
| Setting | Default | Mount |
|
|
239
|
+
| --- | --- | --- |
|
|
240
|
+
| `BEQFORGE_HOST`, `BEQFORGE_PORT` | `0.0.0.0`, `8420` | |
|
|
241
|
+
| `BEQFORGE_SHARED_ROOT` | `/work` | the pipeline's work directory, so audio is sent by reference |
|
|
242
|
+
| `BEQFORGE_CACHE_DIR` | `/cache` | a volume, so a restart keeps the stage cache |
|
|
243
|
+
| `BEQFORGE_RECORD_DIR` | unset | set it (for example to `/records`, and mount that) to keep run records |
|
|
244
|
+
|
|
245
|
+
It runs as uid/gid 1000 (`--build-arg PUID=… PGID=…` to change), which must be able to write
|
|
246
|
+
the shared root and the cache: the server checks both with a write at startup. Its
|
|
247
|
+
`HEALTHCHECK` asks `/health`, which answers while a design runs: designs run in a separate
|
|
248
|
+
worker process, one at a time, so a caller sending several at once queues them; size the
|
|
249
|
+
caller's timeout for that. `docker stop` stops a design in progress.
|
|
250
|
+
|
|
251
|
+
Build and check it locally from the repository root:
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
docker build -f packaging/designer/Dockerfile -t beqforge-designer .
|
|
255
|
+
python3 packaging/designer/smoke.py beqforge-designer
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
The smoke script checks `/health` and one design inline and one by reference;
|
|
259
|
+
`tests/test_designer_image.py` runs the same check against an in-process server.
|
|
260
|
+
|
|
225
261
|
For a machine without Python/`uv`, a standalone `beqforge` executable (every subcommand,
|
|
226
262
|
including `serve-designer`) is built for Linux/macOS/Windows by
|
|
227
263
|
`.github/workflows/build-executable.yml` on every `vX.Y.Z` tag, and attached to the matching
|
|
@@ -32,7 +32,7 @@ are retained so records and code comments remain traceable. Completed items are
|
|
|
32
32
|
| 13 | 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. |
|
|
33
33
|
| 14 | Authored features | parked | Manual `--exclude` works; no validated automatic detector exists. Reopen with real labelled features and negatives showing that detection preserves legitimate bass. |
|
|
34
34
|
| 15 | 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. |
|
|
35
|
-
| 16 | 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. |
|
|
35
|
+
| 16 | 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). |
|
|
36
36
|
| 17 | Mixed-channel policy | parked | `channel_scope` is currently an unpopulated optional response field, not a computed `mixed` finding awaiting a gate. Reopen with a case where existing per-channel restoration and common pricing produce an unsuitable mix correction. |
|
|
37
37
|
| 18 | Fraction dial (former TODO 7) | parked | No user/title has needed partial licensed restoration as a preference; tilt and tolerance already ship. Reopen with a concrete requested outcome, then price and validate it like every target. |
|
|
38
38
|
| 19 | Protective corner (former TODO 9) | parked | The corner is already derived from boost cap and implied order, floored by `lowest_frequency_hz`. No case requires an independent override or different fallback alignment; reopen with one. |
|
|
@@ -43,7 +43,7 @@ still holds: no other API, no other service, and the server is a thin transport
|
|
|
43
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
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. |
|
|
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.
|
|
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 one 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. One worker keeps designs serial (the contract is one synchronous POST per `design()` call); a worker that dies answers 500 and is replaced. 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` and `shared_root`. |
|
|
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. |
|