Skip to content

Model firmware-dependent capability limits (option A) - #15

Merged
emuehlstein merged 1 commit into
mainfrom
dm32-firmware-limits
Aug 12, 2026
Merged

Model firmware-dependent capability limits (option A)#15
emuehlstein merged 1 commit into
mainfrom
dm32-firmware-limits

Conversation

@emuehlstein

Copy link
Copy Markdown
Contributor

Refs #3. Does not close it — this covers only the firmware axis, not the rest of the constraint block proposed there.

Problem

capabilities.json is keyed on radio id alone: one flat limit set per radio, no firmware axis. Meanwhile instance-registry-0.1.schema.json already records a firmware string per physical radio, and validation never read it. grep -i firmware src/ schemas/ radios/ returned exactly one hit before this change — that unused field.

Known case, DM-32UV ROW line:

  • DM32.01.L01.048 — record function removed, CSV contacts 150,000
  • DM32.01.01.049 — record restored, contacts back to 50,000

A 3× swing in a hard limit across one version bump on the same hardware, and 048 → 049 is the real upgrade path for the green/white pair. A profile sized for 048 would validate clean against a 049 radio.

Design

Optional firmware_limits in capabilities 0.2 — firmware version string → partial limit overrides. Override objects reuse the limits property set via $defs/firmwareLimitOverrides, so additionalProperties: false still catches a typo'd key inside an override, and minProperties: 1 rejects empty entries. Both verified in the test run below.

_resolve_firmware_limits():

firmware result
declared in firmware_limits base limits updated by that entry
unknown, or none declared minimum of base and every declared override
radio has no firmware_limits unchanged — today's behaviour exactly

The conservative floor is the whole point. Assuming the larger 048 ceiling for a radio nobody has identified is precisely how a profile validates clean and then overflows on hardware. An uncharacterised radio gets the floor across all known firmware, not the optimistic base.

Violation messages now cite their basis — limit for firmware DM32.01.01.049, or conservative limit across all known firmware (no firmware declared) — instead of a bare radio limit, so an unexpected rejection explains itself.

⚠️ Scope limit — read this before assuming it's done

max_contacts is now correct and firmware-aware, but still not enforced anywhere. Profile 0.1 carries no contact list, so there is nothing to count against it. This commit fixes the data; the check needs a profile-schema change to have anything to check.

So the 048/049 case that motivated the work is not yet caught at validation time. What is caught end-to-end is any firmware-varying limit that profile 0.1 does express — test_instance_firmware_drives_enforcement_end_to_end proves the registry's firmware field reaches the limit check by using a fixture firmware that lowers max_channels. The plumbing is real and tested; the contact-specific enforcement is blocked on profile 0.2.

I'd rather land the mechanism with an honest gap than imply the overflow is now impossible.

Tests

uv run pytest -q30 passed (was 24).

  • declared firmware selects its own limits (048 → 150k, 049 → 50k)
  • unknown firmware falls back to the conservative floor, including a fixture firmware that lowers max_channels below base
  • firmware present but absent from firmware_limits is treated as unknown, and says so
  • radio without firmware_limits is byte-identical in behaviour
  • instance firmware reaches enforcement end-to-end
  • the real DM-32 file records both entries, and base max_contacts equals the minimum across them

Schema self-validates; all three radios/*/capabilities.json still pass; unknown-key and empty-override cases both correctly rejected.

Not covered

  • HR Vocoder (DM32.00.*) has no 049 at all, and DM32.NRF.* is uncharacterised for contacts. Neither is declared — both fall to the conservative floor, which is the safe default. Noted in the radio file's notes.
  • hardware_family is also in the instance registry. Limits really vary on family × firmware; this models firmware only. Worth a follow-up if HRV limits ever get characterised.

Some radio limits change across firmware versions on the same hardware, and
nothing modelled that. capabilities.json was keyed only on radio id, so one
flat limit set covered every firmware. The instance registry already records
a `firmware` string per physical radio (instance-registry-0.1.schema.json)
and validation never read it.

Known case, DM-32UV ROW line: DM32.01.L01.048 removed the record function and
raised CSV contacts to 150,000; DM32.01.01.049 restored record and returned to
50,000. That is a 3x swing in a hard limit across one version bump, and 048 ->
049 is our actual upgrade path for the green/white pair.

Adds an optional `firmware_limits` block to capabilities 0.2: a map of firmware
version string -> partial limit overrides, reusing the `limits` property set via
$defs/firmwareLimitOverrides so a typo'd or unknown key is still rejected
(limits keeps additionalProperties: false).

Resolution in _resolve_firmware_limits():
  - firmware declared in firmware_limits -> base limits updated by that entry
  - firmware unknown or absent -> the MINIMUM of the base value and every
    declared override, so an uncharacterised radio cannot validate against a
    capacity no shipped firmware provides
  - radio with no firmware_limits -> unchanged behaviour

The conservative floor is the point of the design: silently assuming the larger
048 ceiling for a radio nobody has identified is how a profile validates clean
and then overflows on hardware.

Limit violation messages now cite the basis ("limit for firmware X" /
"conservative limit across all known firmware") instead of a bare
"radio limit", so an unexpected rejection is self-explaining.

DM-32 capabilities.json declares both firmware entries and sets base
max_contacts to the smaller 50,000. Note that max_contacts still has no
enforcement site -- profile 0.1 carries no contact list to count -- so this
commit makes the limit *correct and firmware-aware*, not yet enforced.

Refs #3.
@emuehlstein
emuehlstein merged commit 869de9a into main Aug 12, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant