From 0215f888f8e444b5797aeb51762b2e0ce8dab90a Mon Sep 17 00:00:00 2001 From: Matthew Baranov Date: Thu, 20 Aug 2026 17:38:33 +0000 Subject: [PATCH] feat: add device acceptance protocol --- .../ISSUE_TEMPLATE/device-compatibility.yml | 30 ++- .github/workflows/site.yml | 7 + docs/README.md | 2 + docs/README_RU.md | 2 + docs/operations/device-acceptance.md | 60 ++++++ docs/operations/device-acceptance_RU.md | 62 +++++++ docs/research/device-acceptance-sources.md | 21 +++ docs/research/device-acceptance-sources_RU.md | 23 +++ docs/roadmap.md | 3 +- docs/roadmap_RU.md | 3 +- .../device-acceptance-report-v1.schema.json | 89 +++++++++ site/index.html | 2 +- site/ru/index.html | 2 +- tools/device-acceptance/README.md | 10 + .../invalid-measurement-missing-evidence.json | 7 + .../invalid-template-with-evidence.json | 13 ++ .../fixtures/report-template-v1.json | 7 + tools/device-acceptance/test-validator.mjs | 21 +++ tools/device-acceptance/validate-report.mjs | 175 ++++++++++++++++++ 19 files changed, 533 insertions(+), 6 deletions(-) create mode 100644 docs/operations/device-acceptance.md create mode 100644 docs/operations/device-acceptance_RU.md create mode 100644 docs/research/device-acceptance-sources.md create mode 100644 docs/research/device-acceptance-sources_RU.md create mode 100644 schemas/device-acceptance-report-v1.schema.json create mode 100644 tools/device-acceptance/README.md create mode 100644 tools/device-acceptance/fixtures/invalid-measurement-missing-evidence.json create mode 100644 tools/device-acceptance/fixtures/invalid-template-with-evidence.json create mode 100644 tools/device-acceptance/fixtures/report-template-v1.json create mode 100644 tools/device-acceptance/test-validator.mjs create mode 100644 tools/device-acceptance/validate-report.mjs diff --git a/.github/ISSUE_TEMPLATE/device-compatibility.yml b/.github/ISSUE_TEMPLATE/device-compatibility.yml index 006c5c6..7986bbe 100644 --- a/.github/ISSUE_TEMPLATE/device-compatibility.yml +++ b/.github/ISSUE_TEMPLATE/device-compatibility.yml @@ -5,7 +5,7 @@ labels: ["device-compatibility", "needs-triage"] body: - type: markdown attributes: - value: "Do not attach recordings, user content, callsigns, keys or serial numbers." + value: "Read docs/operations/device-acceptance.md first. Do not attach recordings, user content, callsigns, keys, serial numbers, MAC addresses or account names. A schema-valid report is format evidence only, not a support claim." - type: input id: app_version attributes: @@ -26,9 +26,35 @@ body: placeholder: e.g. Acoustic-1 / Balanced validations: required: true + - type: input + id: revision_and_fixture + attributes: + label: App revision, fixture and SHA-256 + placeholder: e.g. 52a5aa3; acoustic-1-v1-text-balanced.wav; SHA-256 + validations: + required: true + - type: textarea + id: reproducible_settings + attributes: + label: Reproducible settings + description: Include source/sink device classes (not serial numbers), OS version, carrier/profile, command or app settings, nominal sample rate and run count. + validations: + required: true + - type: dropdown + id: recording_availability + attributes: + label: Recording availability + options: + - not collected + - private and available on request + - public redacted + - public unrestricted + validations: + required: true - type: textarea id: result attributes: - label: Result and anonymised metrics + label: Result and anonymised totals + description: State accepted, rejected and inconclusive runs. Do not report BER, SNR, range or compatibility without a dedicated reviewed method. validations: required: true diff --git a/.github/workflows/site.yml b/.github/workflows/site.yml index 43e8b79..0ade696 100644 --- a/.github/workflows/site.yml +++ b/.github/workflows/site.yml @@ -25,6 +25,11 @@ jobs: grep -q 'docs/guides/getting-started_RU.md' site/ru/index.html grep -q 'README_RU.md' README.md grep -q 'README.md' README_RU.md + - name: Validate device-acceptance tooling fixtures + run: | + node tools/device-acceptance/validate-report.mjs \ + tools/device-acceptance/fixtures/report-template-v1.json + node tools/device-acceptance/test-validator.mjs - name: Verify English and Russian document pairs run: | pairs=( @@ -37,6 +42,8 @@ jobs: "docs/roadmap.md:docs/roadmap_RU.md" "docs/research/acoustic-1-phy-sources.md:docs/research/acoustic-1-phy-sources_RU.md" "docs/research/acoustic-2-measurement-sources.md:docs/research/acoustic-2-measurement-sources_RU.md" + "docs/research/device-acceptance-sources.md:docs/research/device-acceptance-sources_RU.md" + "docs/operations/device-acceptance.md:docs/operations/device-acceptance_RU.md" "spec/protocol-v1.md:spec/protocol-v1_RU.md" "spec/acoustic-1.md:spec/acoustic-1_RU.md" "spec/acoustic-2.md:spec/acoustic-2_RU.md" diff --git a/docs/README.md b/docs/README.md index 4e54d87..a30c8bd 100644 --- a/docs/README.md +++ b/docs/README.md @@ -11,11 +11,13 @@ This directory holds the human-readable technical documentation for AudioModem. | Reference | [Platform support](reference/platform-support.md) | [Поддержка платформ](reference/platform-support_RU.md) | Runners versus supported features | | Reference | [Transmission presets](reference/presets.md) | [Пресеты передачи](reference/presets_RU.md) | Profile identifiers and intent | | Operations | [Troubleshooting](troubleshooting.md) | [Диагностика](troubleshooting_RU.md) | Reproducible observations and reports | +| Operations | [Device acceptance](operations/device-acceptance.md) | [Приёмка устройств](operations/device-acceptance_RU.md) | Evidence contract and decision gates for future live routes | | Project | [Roadmap](roadmap.md) | [Дорожная карта](roadmap_RU.md) | Verified work and upcoming milestones | | Architecture | [Flutter ↔ Rust WAV bridge](architecture/flutter-rust-bridge.md) | [Flutter ↔ Rust WAV bridge на русском](architecture/flutter-rust-bridge_RU.md) | Native facade and in-memory WAV verification boundary | | Research | [Flutter Rust Bridge integration](research/flutter-rust-bridge-integration.md) | [Flutter Rust Bridge integration на русском](research/flutter-rust-bridge-integration_RU.md) | Generated-code layout and regeneration command | | Research | [Acoustic-1 PHY sources](research/acoustic-1-phy-sources.md) | [Источники Acoustic-1 PHY](research/acoustic-1-phy-sources_RU.md) | Design sources and explicit receiver/FEC constraints | | Research | [Acoustic-2 measurement sources](research/acoustic-2-measurement-sources.md) | [Источники измерений Acoustic-2](research/acoustic-2-measurement-sources_RU.md) | Controlled PCM transform and timing-acquisition constraints | +| Research | [Device-acceptance sources](research/device-acceptance-sources.md) | [Источники device acceptance](research/device-acceptance-sources_RU.md) | Reproducible evidence, metadata and privacy constraints | | Specification | [ADLP v1](../spec/protocol-v1.md) | [ADLP v1 на русском](../spec/protocol-v1_RU.md) | Normative wire object and WAV bootstrap carrier | | Specification | [Acoustic-1](../spec/acoustic-1.md) | [Acoustic-1 на русском](../spec/acoustic-1_RU.md) | Experimental B-FSK carrier and compatibility boundary | | Specification | [Acoustic-2](../spec/acoustic-2.md) | [Acoustic-2 на русском](../spec/acoustic-2_RU.md) | Experimental controlled PCM measurement contract | diff --git a/docs/README_RU.md b/docs/README_RU.md index e83ac90..345990a 100644 --- a/docs/README_RU.md +++ b/docs/README_RU.md @@ -13,11 +13,13 @@ | Reference | [Platform support](reference/platform-support.md) | [Поддержка платформ](reference/platform-support_RU.md) | Runners и supported features | | Reference | [Transmission presets](reference/presets.md) | [Пресеты передачи](reference/presets_RU.md) | Profile identifiers и назначение | | Operations | [Troubleshooting](troubleshooting.md) | [Диагностика](troubleshooting_RU.md) | Воспроизводимые наблюдения и отчёты | +| Operations | [Device acceptance](operations/device-acceptance.md) | [Приёмка устройств](operations/device-acceptance_RU.md) | Evidence contract и decision gates для будущих live routes | | Project | [Roadmap](roadmap.md) | [Дорожная карта](roadmap_RU.md) | Проверенная работа и ближайшие milestones | | Architecture | [Flutter ↔ Rust WAV bridge](architecture/flutter-rust-bridge.md) | [Flutter ↔ Rust WAV bridge на русском](architecture/flutter-rust-bridge_RU.md) | Граница native facade и проверки WAV в памяти | | Research | [Flutter Rust Bridge integration](research/flutter-rust-bridge-integration.md) | [Flutter Rust Bridge integration на русском](research/flutter-rust-bridge-integration_RU.md) | Структура generated code и команда повторной генерации | | Research | [Acoustic-1 PHY sources](research/acoustic-1-phy-sources.md) | [Источники Acoustic-1 PHY](research/acoustic-1-phy-sources_RU.md) | Источники дизайна и явные receiver/FEC constraints | | Research | [Acoustic-2 measurement sources](research/acoustic-2-measurement-sources.md) | [Источники измерений Acoustic-2](research/acoustic-2-measurement-sources_RU.md) | Ограничения controlled PCM transforms и timing acquisition | +| Research | [Device-acceptance sources](research/device-acceptance-sources.md) | [Источники device acceptance](research/device-acceptance-sources_RU.md) | Ограничения reproducible evidence, metadata и privacy | | Specification | [ADLP v1](../spec/protocol-v1.md) | [ADLP v1 на русском](../spec/protocol-v1_RU.md) | Нормативный wire object и WAV bootstrap carrier | | Specification | [Acoustic-1](../spec/acoustic-1.md) | [Acoustic-1 на русском](../spec/acoustic-1_RU.md) | Экспериментальный B-FSK carrier и граница compatibility | | Specification | [Acoustic-2](../spec/acoustic-2.md) | [Acoustic-2 на русском](../spec/acoustic-2_RU.md) | Экспериментальный controlled PCM measurement contract | diff --git a/docs/operations/device-acceptance.md b/docs/operations/device-acceptance.md new file mode 100644 index 0000000..96fa138 --- /dev/null +++ b/docs/operations/device-acceptance.md @@ -0,0 +1,60 @@ +# Device-acceptance protocol + +**Status:** Experimental operations contract · **Last reviewed:** 2026-08-20 · **English (canonical)** · [Русский](device-acceptance_RU.md) + +This protocol governs future evidence for a live audio route. It does **not** make any platform, device, cable, Bluetooth path, radio interface or Acoustic-1/Acoustic-2 experiment supported. A single report is an observation awaiting review; it is not a performance claim. + +> The protocol curates the evidence bundle behind a claim: input artifact, method, environment, outputs and provenance. Structured metadata and provenance are required for independent evaluation, while raw audio is optional because it may be sensitive.[1] [2] + +## Report classes + +| Report class | What it establishes | What it does not establish | +| --- | --- | --- | +| `template` | The repository has a schema-valid, unexecuted reporting form. | Any device measurement or support claim. | +| `codec_reproduction` | A named WAV fixture and command reproduce a codec result. | A physical audio path. | +| `controlled_pcm` | A declared Acoustic-2 PCM transform produced its specified codec-observable result. | A device, room, SNR, BER or live-route result. | +| `physical_route` | A reviewable run was observed over the named physical route and settings. | Broad compatibility, range, reliability or “supported” status. | + +## Required measurement evidence + +Only a `measurement` record can contain physical-route evidence. It must identify the app revision, operating system/version, route type, source and sink device classes, selected carrier/profile, fixture path and SHA-256, exact command/settings, run count and accepted/rejected totals. The report schema deliberately excludes callsigns, serial numbers, personal content and raw-audio upload requirements. + +The sidecar JSON report is authoritative for configuration metadata. The WAV bytes and their SHA-256 are the primary recording artifact because audio metadata can fail to persist across recording applications.[3] + +| Evidence field | Required purpose | Privacy requirement | +| --- | --- | --- | +| App revision and command | Binds behavior to a reviewable implementation and invocation. | Never include credentials or paths containing personal identifiers. | +| Device class and optional public model | Distinguishes broad hardware category from device identity. | Do not record serial numbers, MAC addresses or account names. | +| Carrier, profile, fixture and SHA-256 | Pins the input and protocol interpretation. | Use public fixtures or a hash only. | +| Run totals and outcome class | Makes success, rejection and inconclusive outcomes visible. | Do not convert totals into BER/SNR/range without a dedicated method. | +| Recording availability state | States whether raw evidence can be reviewed. | Use `not_collected`, `private_available_on_request`, `public_redacted` or `public_unrestricted`; never upload sensitive audio by default. | + +## Procedure + +The contributor starts from a committed fixture and a clean app/CLI revision, records the exact route settings, runs the declared trial count, and preserves output logs plus SHA-256 values. A report may be submitted even when every trial is rejected or inconclusive. It must not omit those outcomes, substitute another fixture, or describe an unexecuted plan as a measurement. + +Before opening a compatibility issue, validate the JSON sidecar locally: + +```bash +node tools/device-acceptance/validate-report.mjs \ + tools/device-acceptance/fixtures/report-template-v1.json +``` + +The committed fixture above is intentionally an **unexecuted template**, not a device result. A contributor creates a new measurement sidecar only after obtaining real hardware observations. + +## Decision gates + +| Gate | Required evidence | Permitted label | +| --- | --- | --- | +| Schema gate | Report passes repository validator. | `report format valid` only. | +| Observation gate | A reviewed `physical_route` record has complete fields and no privacy violation. | `observed`, with its exact scope. | +| Candidate-route gate | Repeated real runs, declared device/route settings, fixture hashes, failures and a maintainer review are available. | `experimental route candidate`. | +| Supported-route gate | A separate adapter RFC, reproducible target build, route-specific acceptance tests, published compatibility note and maintainer approval all exist. | `supported` for only the documented scope. | + +The schema cannot establish the last three gates by itself. It only prevents incomplete evidence from being mistaken for a measurement. The existing [audio-route boundaries](../guides/audio-routes.md) and [platform matrix](../reference/platform-support.md) remain authoritative for current support status. + +## References + +[1]: https://pmc.ncbi.nlm.nih.gov/articles/PMC8441584/ "The role of metadata in reproducible computational research" +[2]: https://www.rd-alliance.org/wp-content/uploads/2022/04/1020Things20for20Curating20Reproducible20and20FAIR20Research20v1.1.pdf "10 Things for Curating Reproducible and FAIR Research" +[3]: https://www.weareavp.com/a-study-of-embedded-metadata-support-in-audio-recording-software/ "A Study of Embedded Metadata Support in Audio Recording Software" diff --git a/docs/operations/device-acceptance_RU.md b/docs/operations/device-acceptance_RU.md new file mode 100644 index 0000000..53f959a --- /dev/null +++ b/docs/operations/device-acceptance_RU.md @@ -0,0 +1,62 @@ +# Протокол device acceptance + +[English (canonical)](device-acceptance.md) · **Русский перевод** + +> **Translation of:** [docs/operations/device-acceptance.md](device-acceptance.md). **Last synced:** 2026-08-20. Английский оригинал определяет contract implementation. + +Этот protocol регулирует будущие evidence для live audio route. Он **не** делает supported ни платформу, ни устройство, ни кабель, ни Bluetooth path, ни radio interface, ни Acoustic-1/Acoustic-2 experiment. Один report — observation, ожидающее review, а не performance claim. + +> Protocol курирует evidence bundle за claim: input artifact, method, environment, outputs и provenance. Structured metadata и provenance нужны для independent evaluation, тогда как raw audio опционален, поскольку может быть sensitive.[1] [2] + +## Классы отчётов + +| Report class | Что он устанавливает | Чего он не устанавливает | +| --- | --- | --- | +| `template` | В repository существует schema-valid, unexecuted reporting form. | Любое device measurement или support claim. | +| `codec_reproduction` | Named WAV fixture и command воспроизводят codec result. | Physical audio path. | +| `controlled_pcm` | Declared Acoustic-2 PCM transform дал specified codec-observable result. | Device, room, SNR, BER или live-route result. | +| `physical_route` | Reviewable run наблюдался через named physical route и settings. | Broad compatibility, range, reliability или статус “supported”. | + +## Обязательные evidence измерения + +Только record класса `measurement` может содержать physical-route evidence. Он обязан указывать app revision, operating system/version, route type, source и sink device classes, selected carrier/profile, fixture path и SHA-256, exact command/settings, run count и accepted/rejected totals. Report schema намеренно исключает callsigns, serial numbers, personal content и обязательную загрузку raw audio. + +Sidecar JSON report является авторитетным для configuration metadata. WAV bytes и их SHA-256 — primary recording artifact, поскольку audio metadata может не сохраняться между recording applications.[3] + +| Evidence field | Обязательное назначение | Privacy requirement | +| --- | --- | --- | +| App revision и command | Связывает behavior с reviewable implementation и invocation. | Никогда не включайте credentials или paths с personal identifiers. | +| Device class и optional public model | Отличает broad hardware category от device identity. | Не записывайте serial numbers, MAC addresses или account names. | +| Carrier, profile, fixture и SHA-256 | Фиксирует input и protocol interpretation. | Используйте public fixtures или только hash. | +| Run totals и outcome class | Делает success, rejection и inconclusive outcomes видимыми. | Не превращайте totals в BER/SNR/range без отдельной method. | +| Recording availability state | Сообщает, можно ли review raw evidence. | Используйте `not_collected`, `private_available_on_request`, `public_redacted` или `public_unrestricted`; не загружайте sensitive audio по умолчанию. | + +## Процедура + +Contributor начинает с committed fixture и clean app/CLI revision, записывает exact route settings, выполняет declared trial count и сохраняет output logs вместе с SHA-256 values. Report может быть отправлен, даже если все trials rejected или inconclusive. Он не должен скрывать эти outcomes, подменять fixture или описывать unexecuted plan как measurement. + +Перед открытием compatibility issue проверьте JSON sidecar локально: + +```bash +node tools/device-acceptance/validate-report.mjs \ + tools/device-acceptance/fixtures/report-template-v1.json +``` + +Committed fixture выше намеренно является **unexecuted template**, а не device result. Новый measurement sidecar создаётся только после получения реальных hardware observations. + +## Decision gates + +| Gate | Required evidence | Permitted label | +| --- | --- | --- | +| Schema gate | Report проходит repository validator. | Только `report format valid`. | +| Observation gate | Reviewed `physical_route` record имеет complete fields и не нарушает privacy. | `observed` с его exact scope. | +| Candidate-route gate | Доступны repeated real runs, declared device/route settings, fixture hashes, failures и maintainer review. | `experimental route candidate`. | +| Supported-route gate | Существуют separate adapter RFC, reproducible target build, route-specific acceptance tests, published compatibility note и maintainer approval. | `supported` только для documented scope. | + +Schema сама по себе не устанавливает последние три gates. Она лишь не даёт принять incomplete evidence за measurement. Текущие статусы остаются в [границах audio routes](../guides/audio-routes_RU.md) и [platform matrix](../reference/platform-support_RU.md). + +## Ссылки + +[1]: https://pmc.ncbi.nlm.nih.gov/articles/PMC8441584/ "The role of metadata in reproducible computational research" +[2]: https://www.rd-alliance.org/wp-content/uploads/2022/04/1020Things20for20Curating20Reproducible20and20FAIR20Research20v1.1.pdf "10 Things for Curating Reproducible and FAIR Research" +[3]: https://www.weareavp.com/a-study-of-embedded-metadata-support-in-audio-recording-software/ "A Study of Embedded Metadata Support in Audio Recording Software" diff --git a/docs/research/device-acceptance-sources.md b/docs/research/device-acceptance-sources.md new file mode 100644 index 0000000..ba18a98 --- /dev/null +++ b/docs/research/device-acceptance-sources.md @@ -0,0 +1,21 @@ +# Device-acceptance protocol research notes + +**Last reviewed:** 2026-08-20 · **English (canonical)** + +The future AudioModem device-acceptance protocol will curate an evidence bundle rather than treat one user report as a compatibility result. A report must distinguish a reproducible codec result from a separate physical-device observation and must not turn missing or private recordings into fabricated performance data. + +| Source | Relevant finding | Design consequence | +| --- | --- | --- | +| Leipzig et al., *The role of metadata in reproducible computational research* | Metadata describe provenance and method context across inputs, tools, reports and pipelines; missing method details obstruct reproduction and evaluation.[1] | Require machine-readable report metadata for app revision, OS, device class, route settings, fixture identity, commands and outcome. | +| Research Data Alliance, *10 Things for Curating Reproducible and FAIR Research* | A reproducibility bundle needs data, code, outputs, documentation, provenance and automation sufficient to recreate a predefined outcome.[2] | Require a manifest, input/output hashes, reproducible command, report schema version and an explicit evidence availability state. | +| ARSC Technical Committee metadata study | Audio recording applications can have embedded-metadata persistence and integrity problems; reference files and test methods aid independent verification.[3] | Treat WAV bytes and SHA-256 as primary evidence; record capture/playback metadata in a sidecar report rather than rely on embedded audio tags. | + +## Privacy and evidence policy + +Raw recordings may contain speech, environmental audio or personal data. A report must therefore state one of `not_collected`, `private_available_on_request`, `public_redacted` or `public_unrestricted` instead of requiring upload. Any claim stronger than a reproducible local codec run requires declared device metadata, route settings, fixtures, run count and a reviewable evidence bundle. + +## References + +[1]: https://pmc.ncbi.nlm.nih.gov/articles/PMC8441584/ "The role of metadata in reproducible computational research" +[2]: https://www.rd-alliance.org/wp-content/uploads/2022/04/1020Things20for20Curating20Reproducible20and20FAIR20Research20v1.1.pdf "10 Things for Curating Reproducible and FAIR Research" +[3]: https://www.weareavp.com/a-study-of-embedded-metadata-support-in-audio-recording-software/ "A Study of Embedded Metadata Support in Audio Recording Software" diff --git a/docs/research/device-acceptance-sources_RU.md b/docs/research/device-acceptance-sources_RU.md new file mode 100644 index 0000000..0509555 --- /dev/null +++ b/docs/research/device-acceptance-sources_RU.md @@ -0,0 +1,23 @@ +# Исследовательские заметки по протоколу device acceptance + +[English (canonical)](device-acceptance-sources.md) · **Русский перевод** + +> **Translation of:** [docs/research/device-acceptance-sources.md](device-acceptance-sources.md). **Last synced:** 2026-08-20. + +Будущий AudioModem device-acceptance protocol будет курировать evidence bundle, а не считать один user report compatibility result. Report обязан отличать reproducible codec result от отдельного physical-device observation и не должен превращать отсутствующие или private recordings в вымышленные performance data. + +| Источник | Значимое наблюдение | Следствие для дизайна | +| --- | --- | --- | +| Leipzig и соавт., *The role of metadata in reproducible computational research* | Metadata описывают provenance и method context для inputs, tools, reports и pipelines; отсутствие method details мешает reproduction и evaluation.[1] | Требовать machine-readable report metadata для app revision, OS, device class, route settings, fixture identity, commands и outcome. | +| Research Data Alliance, *10 Things for Curating Reproducible and FAIR Research* | Reproducibility bundle требует data, code, outputs, documentation, provenance и automation, достаточных для recreation predefined outcome.[2] | Требовать manifest, input/output hashes, reproducible command, report schema version и explicit evidence availability state. | +| ARSC Technical Committee metadata study | Audio recording applications могут иметь проблемы persistence и integrity embedded metadata; reference files и test methods помогают independent verification.[3] | Считать WAV bytes и SHA-256 primary evidence; capture/playback metadata хранить в sidecar report, а не полагаться на embedded audio tags. | + +## Privacy и evidence policy + +Raw recordings могут содержать speech, environmental audio или personal data. Поэтому report обязан указывать одно из `not_collected`, `private_available_on_request`, `public_redacted` или `public_unrestricted`, а не требовать upload. Любой claim сильнее reproducible local codec run требует declared device metadata, route settings, fixtures, run count и reviewable evidence bundle. + +## Ссылки + +[1]: https://pmc.ncbi.nlm.nih.gov/articles/PMC8441584/ "The role of metadata in reproducible computational research" +[2]: https://www.rd-alliance.org/wp-content/uploads/2022/04/1020Things20for20Curating20Reproducible20and20FAIR20Research20v1.1.pdf "10 Things for Curating Reproducible and FAIR Research" +[3]: https://www.weareavp.com/a-study-of-embedded-metadata-support-in-audio-recording-software/ "A Study of Embedded Metadata Support in Audio Recording Software" diff --git a/docs/roadmap.md b/docs/roadmap.md index 1c3dbef..1af9a7c 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -12,13 +12,14 @@ This roadmap describes intended order, not dates or guaranteed delivery. It dist | Local WAV file workflow | Completed | User-selected save/open dialogs export/import verified WAV bytes; file-object payloads remain future work. | | Acoustic-1 | Experimental controlled carrier | B-FSK framing, bounded synchronisation, Hamming(7,4), profile-driven symbols and golden vectors exist for PCM/WAV tests; no speaker-to-microphone claim. | | Acoustic-2 | Experimental measurement harness | Declared integer PCM transforms, bounded acquisition observables and golden measurement contract exist around Acoustic-1; no device or channel metric claim. | +| Device-acceptance infrastructure | Completed reporting/tooling contract | Schema, validator, privacy-preserving intake template and decision gates prepare future evidence; no physical-route observation or supported device claim exists. | | Audio adapters | Planned | Capture/playback, cable, Bluetooth and radio-interface adapters with per-platform acceptance tests. | | Trust and encryption | Planned RFC | Key lifecycle, manual/QR exchange, authenticated encryption, verification UX and independent security review. | | Release engineering | Planned | Signed packages, compatibility matrix, changelog, SBOM/checksums and clear support policy. | ## Decision gates -The project should not declare live delivery before an Acoustic-1 profile, a route adapter and an acceptance test exist together. It should not declare encryption before a reviewed RFC defines key ownership, exchange, verification, recovery and failure behavior. It should not claim cross-platform support before a target has a reproducible release artifact and documented limits. +The project should not declare live delivery before an Acoustic-1 profile, a route adapter and an acceptance test exist together. A schema-valid device report establishes format only; a support claim additionally requires reviewable physical-route evidence and the published supported-route gate. The project should not declare encryption before a reviewed RFC defines key ownership, exchange, verification, recovery and failure behavior. It should not claim cross-platform support before a target has a reproducible release artifact and documented limits. ## Participation diff --git a/docs/roadmap_RU.md b/docs/roadmap_RU.md index 07f1a34..873cf95 100644 --- a/docs/roadmap_RU.md +++ b/docs/roadmap_RU.md @@ -14,13 +14,14 @@ | Локальный WAV file workflow | Завершён | User-selected save/open dialogs экспортируют/импортируют проверенные WAV bytes; file-object payloads остаются будущей работой. | | Acoustic-1 | Экспериментальный controlled carrier | B-FSK framing, bounded synchronisation, Hamming(7,4), profile-driven symbols и golden vectors существуют для PCM/WAV tests; speaker-to-microphone claim отсутствует. | | Acoustic-2 | Экспериментальный measurement harness | Вокруг Acoustic-1 существуют declared integer PCM transforms, bounded acquisition observables и golden measurement contract; device или channel metric claim отсутствует. | +| Device-acceptance infrastructure | Завершён reporting/tooling contract | Schema, validator, privacy-preserving intake template и decision gates готовят future evidence; physical-route observation или supported device claim отсутствуют. | | Audio adapters | Планируется | Capture/playback, cable, Bluetooth и radio-interface adapters с per-platform acceptance tests. | | Trust и encryption | Planned RFC | Key lifecycle, manual/QR exchange, authenticated encryption, verification UX и independent security review. | | Release engineering | Планируется | Signed packages, compatibility matrix, changelog, SBOM/checksums и clear support policy. | ## Decision gates -Проект не должен заявлять live delivery, пока Acoustic-1 profile, route adapter и acceptance test не существуют вместе. Он не должен заявлять encryption, пока reviewed RFC не определит key ownership, exchange, verification, recovery и failure behavior. Он не должен заявлять cross-platform support, пока target не получил reproducible release artifact и documented limits. +Проект не должен заявлять live delivery, пока Acoustic-1 profile, route adapter и acceptance test не существуют вместе. Schema-valid device report устанавливает только format; support claim дополнительно требует reviewable physical-route evidence и опубликованного supported-route gate. Проект не должен заявлять encryption, пока reviewed RFC не определит key ownership, exchange, verification, recovery и failure behavior. Он не должен заявлять cross-platform support, пока target не получил reproducible release artifact и documented limits. ## Участие diff --git a/schemas/device-acceptance-report-v1.schema.json b/schemas/device-acceptance-report-v1.schema.json new file mode 100644 index 0000000..fa18ede --- /dev/null +++ b/schemas/device-acceptance-report-v1.schema.json @@ -0,0 +1,89 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/matveyb9/AudioModem/schemas/device-acceptance-report-v1.schema.json", + "title": "AudioModem device acceptance report v1", + "type": "object", + "required": ["schema_version", "report_type", "report_id", "privacy_acknowledged"], + "properties": { + "schema_version": { "const": "audio-modem-device-acceptance/v1" }, + "report_type": { "enum": ["template", "measurement"] }, + "report_id": { "type": "string", "pattern": "^DARP-[a-z0-9-]{3,64}$" }, + "privacy_acknowledged": { "const": true }, + "notes": { "type": "string", "maxLength": 1000 }, + "device": { + "type": "object", + "required": ["route", "source", "sink"], + "properties": { + "route": { "enum": ["speaker_microphone", "audio_cable", "bluetooth", "radio_interface"] }, + "source": { "$ref": "#/$defs/endpoint" }, + "sink": { "$ref": "#/$defs/endpoint" } + }, + "additionalProperties": false + }, + "test": { + "type": "object", + "required": ["app_revision", "carrier", "profile", "fixture_path", "fixture_sha256", "command"], + "properties": { + "app_revision": { "type": "string", "pattern": "^[0-9a-f]{7,40}$" }, + "carrier": { "const": "acoustic1" }, + "profile": { "enum": ["reliable", "balanced", "fast", "narrowband"] }, + "fixture_path": { "type": "string", "pattern": "^[A-Za-z0-9._/-]+$" }, + "fixture_sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" }, + "command": { "type": "string", "minLength": 1, "maxLength": 2000 } + }, + "additionalProperties": false + }, + "evidence": { + "type": "object", + "required": ["evidence_class", "run_count", "accepted_runs", "rejected_runs", "recording_availability"], + "properties": { + "evidence_class": { "enum": ["codec_reproduction", "controlled_pcm", "physical_route"] }, + "run_count": { "type": "integer", "minimum": 1 }, + "accepted_runs": { "type": "integer", "minimum": 0 }, + "rejected_runs": { "type": "integer", "minimum": 0 }, + "recording_availability": { "enum": ["not_collected", "private_available_on_request", "public_redacted", "public_unrestricted"] }, + "recording_sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" } + }, + "additionalProperties": false + }, + "outcome": { + "type": "object", + "required": ["classification", "summary"], + "properties": { + "classification": { "enum": ["observed", "inconclusive", "rejected"] }, + "summary": { "type": "string", "minLength": 1, "maxLength": 1000 } + }, + "additionalProperties": false + } + }, + "$defs": { + "endpoint": { + "type": "object", + "required": ["platform", "device_class"], + "properties": { + "platform": { "enum": ["android", "ios", "windows", "macos", "linux", "web", "external"] }, + "device_class": { "type": "string", "minLength": 1, "maxLength": 80 }, + "public_model": { "type": "string", "minLength": 1, "maxLength": 120 } + }, + "additionalProperties": false + } + }, + "oneOf": [ + { + "properties": { "report_type": { "const": "template" } }, + "not": { + "anyOf": [ + { "required": ["device"] }, + { "required": ["test"] }, + { "required": ["evidence"] }, + { "required": ["outcome"] } + ] + } + }, + { + "properties": { "report_type": { "const": "measurement" } }, + "required": ["device", "test", "evidence", "outcome"] + } + ], + "additionalProperties": false +} diff --git a/site/index.html b/site/index.html index 08075e9..bde3040 100644 --- a/site/index.html +++ b/site/index.html @@ -15,7 +15,7 @@

First test

Start with a reproducible WAV round trip.

WAV removes unknown live-audio characteristics. The native app can export a verified WAV and import it for Rust-side validation; Acoustic-1 has a controlled B-FSK carrier and Acoustic-2 measures declared PCM transforms, neither of which is a live-audio claim.

CLI / bootstrapcargo run -p adlp-cli -- encode-text hello.wav N1 "Hello" reliable
  cargo run -p adlp-cli -- decode hello.wav
Open the short guide →

Architecture

A small app over an independent Rust core.

FlutterUI, routes, file selection and diagnostics
→
Rust bridgeOne codec implementation for the client
→
ADLP + DSPContainer, WAV and future PHY profiles
Architecture and stack →
-

Status

Current slice—without promises beyond the implementation.

✓ ADLP v1 + CRC-32C
done
✓ Deterministic PCM/WAV bootstrap
done
✓ Flutter ↔ Rust bridge
native text-to-WAV verification
✓ Local WAV import / export
native file dialogs + Rust validation
✓ Experimental Acoustic-1
B-FSK controlled PCM/WAV codec + golden vector
✓ Acoustic-2 measurement
declared PCM transforms + acquisition observables
• Live audio and encryption
planned
See roadmap →
+

Status

Current slice—without promises beyond the implementation.

✓ ADLP v1 + CRC-32C
done
✓ Deterministic PCM/WAV bootstrap
done
✓ Flutter ↔ Rust bridge
native text-to-WAV verification
✓ Local WAV import / export
native file dialogs + Rust validation
✓ Experimental Acoustic-1
B-FSK controlled PCM/WAV codec + golden vector
✓ Acoustic-2 measurement
declared PCM transforms + acquisition observables
✓ Device-acceptance protocol
evidence schema and review gates; no device result
• Live audio and encryption
planned
See roadmap →
diff --git a/site/ru/index.html b/site/ru/index.html index 6e72f77..01d092e 100644 --- a/site/ru/index.html +++ b/site/ru/index.html @@ -15,7 +15,7 @@

Первый тест

Начните с воспроизводимого WAV round trip.

WAV исключает неизвестные свойства live-аудиотракта. Native app может экспортировать проверенный WAV и импортировать его для Rust-side validation; Acoustic-1 имеет controlled B-FSK carrier, а Acoustic-2 измеряет declared PCM transforms — ни один из них не является live-audio claim.

CLI / bootstrapcargo run -p adlp-cli -- encode-text hello.wav N1 "Привет" reliable
  cargo run -p adlp-cli -- decode hello.wav
Открыть краткое руководство →

Архитектура

Небольшое приложение поверх независимого Rust core.

FlutterUI, маршруты, выбор файла и диагностика
→
Rust bridgeОдна реализация codec для клиента
→
ADLP + DSPКонтейнер, WAV и будущие PHY profiles
Архитектура и стек →
-

Статус

Текущий срез — без обещаний сверх реализации.

✓ ADLP v1 + CRC-32C
готово
✓ Детерминированный PCM/WAV bootstrap
готово
✓ Flutter ↔ Rust bridge
native text-to-WAV verification
✓ Локальный WAV import / export
native file dialogs + Rust validation
✓ Experimental Acoustic-1
B-FSK controlled PCM/WAV codec + golden vector
✓ Acoustic-2 measurement
declared PCM transforms + acquisition observables
• Live audio и encryption
план
Посмотреть дорожную карту →
+

Статус

Текущий срез — без обещаний сверх реализации.

✓ ADLP v1 + CRC-32C
готово
✓ Детерминированный PCM/WAV bootstrap
готово
✓ Flutter ↔ Rust bridge
native text-to-WAV verification
✓ Локальный WAV import / export
native file dialogs + Rust validation
✓ Experimental Acoustic-1
B-FSK controlled PCM/WAV codec + golden vector
✓ Acoustic-2 measurement
declared PCM transforms + acquisition observables
✓ Device-acceptance protocol
evidence schema и review gates; device result отсутствует
• Live audio и encryption
план
Посмотреть дорожную карту →
diff --git a/tools/device-acceptance/README.md b/tools/device-acceptance/README.md new file mode 100644 index 0000000..de7db1d --- /dev/null +++ b/tools/device-acceptance/README.md @@ -0,0 +1,10 @@ +# Device-acceptance report validator + +`validate-report.mjs` checks the repository's narrow v1 intake contract. It validates a template or measurement sidecar; it does not calculate acoustic quality, verify hardware, read a WAV, attest a result, or grant route support. + +```bash +node tools/device-acceptance/validate-report.mjs \ + tools/device-acceptance/fixtures/report-template-v1.json +``` + +The committed template is deliberately unexecuted. Do not copy it into an issue as a real device result. See [the device-acceptance protocol](../../docs/operations/device-acceptance.md) for evidence and privacy requirements. diff --git a/tools/device-acceptance/fixtures/invalid-measurement-missing-evidence.json b/tools/device-acceptance/fixtures/invalid-measurement-missing-evidence.json new file mode 100644 index 0000000..e95961d --- /dev/null +++ b/tools/device-acceptance/fixtures/invalid-measurement-missing-evidence.json @@ -0,0 +1,7 @@ +{ + "schema_version": "audio-modem-device-acceptance/v1", + "report_type": "measurement", + "report_id": "DARP-invalid-missing-evidence", + "privacy_acknowledged": true, + "notes": "Intentionally invalid schema fixture; no physical route was executed." +} diff --git a/tools/device-acceptance/fixtures/invalid-template-with-evidence.json b/tools/device-acceptance/fixtures/invalid-template-with-evidence.json new file mode 100644 index 0000000..329cb52 --- /dev/null +++ b/tools/device-acceptance/fixtures/invalid-template-with-evidence.json @@ -0,0 +1,13 @@ +{ + "schema_version": "audio-modem-device-acceptance/v1", + "report_type": "template", + "report_id": "DARP-invalid-template", + "privacy_acknowledged": true, + "evidence": { + "evidence_class": "physical_route", + "run_count": 1, + "accepted_runs": 1, + "rejected_runs": 0, + "recording_availability": "not_collected" + } +} diff --git a/tools/device-acceptance/fixtures/report-template-v1.json b/tools/device-acceptance/fixtures/report-template-v1.json new file mode 100644 index 0000000..67ccfe4 --- /dev/null +++ b/tools/device-acceptance/fixtures/report-template-v1.json @@ -0,0 +1,7 @@ +{ + "schema_version": "audio-modem-device-acceptance/v1", + "report_type": "template", + "report_id": "DARP-template-v1", + "privacy_acknowledged": true, + "notes": "Unexecuted reporting template. It is not a device measurement or compatibility result." +} diff --git a/tools/device-acceptance/test-validator.mjs b/tools/device-acceptance/test-validator.mjs new file mode 100644 index 0000000..8c2f0e6 --- /dev/null +++ b/tools/device-acceptance/test-validator.mjs @@ -0,0 +1,21 @@ +import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; +import { resolve } from 'node:path'; + +import { validateReport } from './validate-report.mjs'; + +function fixture(name) { + return JSON.parse(readFileSync(resolve('tools/device-acceptance/fixtures', name), 'utf8')); +} + +validateReport(fixture('report-template-v1.json')); +assert.throws( + () => validateReport(fixture('invalid-template-with-evidence.json')), + /template report must not contain evidence/, +); +assert.throws( + () => validateReport(fixture('invalid-measurement-missing-evidence.json')), + /missing device/, +); + +console.log('device-acceptance validator tests passed'); diff --git a/tools/device-acceptance/validate-report.mjs b/tools/device-acceptance/validate-report.mjs new file mode 100644 index 0000000..43e4d9f --- /dev/null +++ b/tools/device-acceptance/validate-report.mjs @@ -0,0 +1,175 @@ +import { readFileSync } from 'node:fs'; +import { basename, resolve } from 'node:path'; + +const SCHEMA_VERSION = 'audio-modem-device-acceptance/v1'; +const REPORT_ID = /^DARP-[a-z0-9-]{3,64}$/; +const REVISION = /^[0-9a-f]{7,40}$/; +const SHA256 = /^[0-9a-f]{64}$/; +const PROFILES = new Set(['reliable', 'balanced', 'fast', 'narrowband']); +const ROUTES = new Set(['speaker_microphone', 'audio_cable', 'bluetooth', 'radio_interface']); +const PLATFORMS = new Set(['android', 'ios', 'windows', 'macos', 'linux', 'web', 'external']); +const EVIDENCE_CLASSES = new Set(['codec_reproduction', 'controlled_pcm', 'physical_route']); +const RECORDING_AVAILABILITY = new Set([ + 'not_collected', + 'private_available_on_request', + 'public_redacted', + 'public_unrestricted', +]); +const OUTCOME_CLASSES = new Set(['observed', 'inconclusive', 'rejected']); + +function fail(message) { + throw new Error(message); +} + +function object(value, field) { + if (value === null || typeof value !== 'object' || Array.isArray(value)) { + fail(`${field} must be an object`); + } + return value; +} + +function string(value, field, { min = 1, max = Number.MAX_SAFE_INTEGER, pattern } = {}) { + if (typeof value !== 'string' || value.length < min || value.length > max) { + fail(`${field} must be a string with length ${min}..${max}`); + } + if (pattern && !pattern.test(value)) { + fail(`${field} has an invalid format`); + } +} + +function integer(value, field, minimum = 0) { + if (!Number.isInteger(value) || value < minimum) { + fail(`${field} must be an integer >= ${minimum}`); + } +} + +function onlyKeys(value, field, allowed) { + for (const key of Object.keys(value)) { + if (!allowed.has(key)) { + fail(`${field}.${key} is not allowed`); + } + } +} + +function required(value, field) { + if (!(field in value)) { + fail(`missing ${field}`); + } + return value[field]; +} + +function validateEndpoint(value, field) { + const endpoint = object(value, field); + onlyKeys(endpoint, field, new Set(['platform', 'device_class', 'public_model'])); + const platform = required(endpoint, 'platform'); + if (!PLATFORMS.has(platform)) { + fail(`${field}.platform is unsupported`); + } + string(required(endpoint, 'device_class'), `${field}.device_class`, { min: 1, max: 80 }); + if ('public_model' in endpoint) { + string(endpoint.public_model, `${field}.public_model`, { min: 1, max: 120 }); + } +} + +function validateMeasurement(report) { + for (const field of ['device', 'test', 'evidence', 'outcome']) { + required(report, field); + } + const device = object(report.device, 'device'); + onlyKeys(device, 'device', new Set(['route', 'source', 'sink'])); + if (!ROUTES.has(required(device, 'route'))) { + fail('device.route is unsupported'); + } + validateEndpoint(required(device, 'source'), 'device.source'); + validateEndpoint(required(device, 'sink'), 'device.sink'); + + const test = object(report.test, 'test'); + onlyKeys(test, 'test', new Set(['app_revision', 'carrier', 'profile', 'fixture_path', 'fixture_sha256', 'command'])); + string(required(test, 'app_revision'), 'test.app_revision', { pattern: REVISION }); + if (required(test, 'carrier') !== 'acoustic1') { + fail('test.carrier must be acoustic1'); + } + if (!PROFILES.has(required(test, 'profile'))) { + fail('test.profile is unsupported'); + } + string(required(test, 'fixture_path'), 'test.fixture_path', { pattern: /^[A-Za-z0-9._/-]+$/ }); + string(required(test, 'fixture_sha256'), 'test.fixture_sha256', { pattern: SHA256 }); + string(required(test, 'command'), 'test.command', { min: 1, max: 2000 }); + + const evidence = object(report.evidence, 'evidence'); + onlyKeys(evidence, 'evidence', new Set(['evidence_class', 'run_count', 'accepted_runs', 'rejected_runs', 'recording_availability', 'recording_sha256'])); + if (!EVIDENCE_CLASSES.has(required(evidence, 'evidence_class'))) { + fail('evidence.evidence_class is unsupported'); + } + integer(required(evidence, 'run_count'), 'evidence.run_count', 1); + integer(required(evidence, 'accepted_runs'), 'evidence.accepted_runs'); + integer(required(evidence, 'rejected_runs'), 'evidence.rejected_runs'); + if (evidence.accepted_runs + evidence.rejected_runs !== evidence.run_count) { + fail('evidence accepted_runs + rejected_runs must equal run_count'); + } + if (!RECORDING_AVAILABILITY.has(required(evidence, 'recording_availability'))) { + fail('evidence.recording_availability is unsupported'); + } + if (evidence.recording_availability !== 'not_collected') { + string(required(evidence, 'recording_sha256'), 'evidence.recording_sha256', { pattern: SHA256 }); + } + + const outcome = object(report.outcome, 'outcome'); + onlyKeys(outcome, 'outcome', new Set(['classification', 'summary'])); + if (!OUTCOME_CLASSES.has(required(outcome, 'classification'))) { + fail('outcome.classification is unsupported'); + } + string(required(outcome, 'summary'), 'outcome.summary', { min: 1, max: 1000 }); +} + +export function validateReport(report) { + const value = object(report, 'report'); + onlyKeys(value, 'report', new Set(['schema_version', 'report_type', 'report_id', 'privacy_acknowledged', 'notes', 'device', 'test', 'evidence', 'outcome'])); + if (required(value, 'schema_version') !== SCHEMA_VERSION) { + fail('schema_version is unsupported'); + } + const reportType = required(value, 'report_type'); + if (reportType !== 'template' && reportType !== 'measurement') { + fail('report_type must be template or measurement'); + } + string(required(value, 'report_id'), 'report_id', { pattern: REPORT_ID }); + if (required(value, 'privacy_acknowledged') !== true) { + fail('privacy_acknowledged must be true'); + } + if ('notes' in value) { + string(value.notes, 'notes', { min: 1, max: 1000 }); + } + if (reportType === 'template') { + for (const field of ['device', 'test', 'evidence', 'outcome']) { + if (field in value) { + fail(`template report must not contain ${field}`); + } + } + return; + } + validateMeasurement(value); +} + +function main() { + const reportPath = process.argv[2]; + if (!reportPath || process.argv.length !== 3) { + console.error('Usage: node tools/device-acceptance/validate-report.mjs '); + process.exitCode = 2; + return; + } + const absolutePath = resolve(reportPath); + let report; + try { + report = JSON.parse(readFileSync(absolutePath, 'utf8')); + validateReport(report); + } catch (error) { + console.error(`invalid device-acceptance report ${basename(absolutePath)}: ${error.message}`); + process.exitCode = 1; + return; + } + console.log(`valid device-acceptance report: ${basename(absolutePath)} (${report.report_type})`); +} + +if (import.meta.url === `file://${process.argv[1]}`) { + main(); +}