Skip to content

An Android model's class comes from the listed profile, not the one its name resolves to #341

Closed

Description

@V3RON

What happens

On Android, a model's class in the catalog comes from the profile it was listed from (src/drivers/android/device-profile-source.ts:122). customModels (:116) and leasing use the profile the name resolves to. If a devices.xml profile has the same name as an SDK profile id, such as tv_1080p or wearos_large_round, the catalog lists it as phone. But leasing it creates the SDK TV or Wear OS profile.

Why it matters

Today nothing picks a device by class, so nothing breaks yet. Once a class request picks a model (#334), such a name could be picked for phone and come up as a TV or a watch.

Expected

A model's class is the class of the profile a lease of that name creates.

Found in the review of #340 (#332).

Written by an agent.

Activity

  1. added
    bug:newReported. Nobody has looked yet.
    on Oct 5, 2026
  2. V3RON commented on Oct 9, 2026

    @V3RON
    ContributorAuthor

    Update: leasing by class has shipped (#343, #357), so a model listed under the wrong class can now be granted to a class request. The note that nothing picks by class yet no longer holds. The code is unchanged: src/drivers/android/device-profile-source.ts:119-122 takes the class from the listed profile, while customModels (:113-118) uses the profile the name resolves to.

    Written by an agent.

  3. added
    bug:triageAn agent may reproduce it and write the triage report.
    and removed
    bug:newReported. Nobody has looked yet.
    on Oct 16, 2026
  4. self-assigned this
    on Oct 16, 2026
  5. added a commit that references this issue on Oct 16, 2026
  6. V3RON commented on Oct 16, 2026

    @V3RON
    ContributorAuthor

    Reproduction

    Confirmed on current main (the 2026-10-09 note holds: the code is unchanged, and class matching now consumes modelClasses in leasing — classCandidates/modelClass at src/leasing/lease-acquisition-coordinator.ts:477-500 — and in gateway routing — src/gateway/routing/request-match.ts:57,83).

    • Test: src/drivers/android/device-profile-source.test.ts — "classes a listed model by the profile its name resolves to, not the one it was listed from" (branch bug/341-repro, commit 2617fb61)
    • Command: pnpm exec vitest run --project unit src/drivers/android/device-profile-source.test.ts
    • Failing assertion (expect(catalog.modelClasses).toEqual(expected), expected built from registry.resolve per model):
    -   "tv_1080p": "tv",
    -   "tv_720p": "tv",
    -   "wearos_large_round": "watch",
    +   "tv_1080p": "phone",
    +   "tv_720p": "phone",
    +   "wearos_large_round": "phone",

    The fixture is a devices.xml with three profiles named after SDK profile ids — two the library also ships, one (tv_720p) the SDK only — proving both the library-shipped and the SDK-only collision shapes. The test also asserts resolve on each name really returns the SDK/library TV or Wear OS profile. Read-only check of a real SDK (avdmanager list device, cmdline-tools latest): id: 58 or "tv_1080p" Tag android-tv, id: 61 or "wearos_large_round" Tag android-wear, so the collision is realistic; nothing in parseDevicesXml validates names against profile ids.

    Root cause

    Defect. src/drivers/android/device-profile-source.ts:135-138 builds modelClasses by iterating listed.values() — the profile each name was listed from — while customModels (:130-133) and modelProfiles/modelRuntimes (:140-144) project every name through matchProfile, and resolve — what provisioning creates — resolves the same way. A name that a library or SDK profile also answers to is therefore classified from the wrong profile, and every class-based reader of the catalog (leasing's classCandidates, the gateway's class filter, MCP/CLI) sees, e.g., a TV-listed tv_1080p as phone.

    Simplest fix

    Class by the resolved profile, like the other catalog fields: iterate models, take matchProfile(profiles, model), keep the existing classification expression (properties → classOfTag(imageKind), else deviceClass). With exactly that change the new test passes and all 13 unit files of src/drivers/android (617 tests) stay green.

    Alternatives rejected

    • Enrich the listed profile's class from the resolving profile's details — fixes only the devices.xml shape, not the library/SDK collisions, and re-implements matchProfile.
    • Refuse to list a model whose name is another profile's alias — contradicts the listing rule pinned by "does not list another name that an earlier profile answers to", which keeps such models listed on purpose.
    • Re-resolve the class through the driver at lease/route time — routing sees only the catalog record; this pushes driver knowledge into leasing and the gateway.

    Risk

    • The change alters modelClasses only where listed profile ≠ resolved profile; confirm the leasing/gateway/core preference-list and classDefaults tests stay green (the drivers' own suite does).
    • A host with such a colliding name on android.defaultModels.phone will stop granting it a phone-class lease and take the next candidate or UnknownModelError — that is the fix working.

    Written by an agent.

  7. removed their assignment
    on Oct 16, 2026
  8. added
    bug:readyAn agent may fix it.
    and removed
    bug:triageAn agent may reproduce it and write the triage report.
    on Oct 16, 2026
  9. self-assigned this
    on Oct 16, 2026
  10. V3RON commented on Oct 16, 2026

    @V3RON
    ContributorAuthor

    Delivery run

    Merged: #646 (#341)
    Parked: none
    Waiting: none

    Reviews: round 1 (spec/code/claims) — 1 blocking claims finding on the triage test's comment, verified and fixed as text; fix check on the post-review commits: clean. Manual updated within its line budget; docs/CLI.md states the classing rule.

    Written by an agent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bug:readyAn agent may fix it.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions