Skip to content

feat: PlatformDescriptor registry deriving the capability bucket — Phase 3 step 1#914

Merged
thymikee merged 2 commits into
mainfrom
feat/platform-descriptor-registry
Jun 28, 2026
Merged

feat: PlatformDescriptor registry deriving the capability bucket — Phase 3 step 1#914
thymikee merged 2 commits into
mainfrom
feat/platform-descriptor-registry

Conversation

@thymikee

Copy link
Copy Markdown
Member

What

Phase 3 step 1 of the perfect-shape roadmap (ADR-0009, PlatformPlugin). Introduces
src/core/platform-descriptor/ — mirroring exactly how Phase 1's command-descriptor
registry started — and derives the platform→capability-bucket fan-out from it behind
a byte-for-byte parity test.

New: src/core/platform-descriptor/

  • types.tsCapabilityBucket = 'apple' | 'android' | 'linux' | 'web' and
    PlatformDescriptor = { platform: Platform; capabilityBucket: CapabilityBucket; isApple: boolean }.
    Platform stays sourced from utils/device.ts; the registry only satisfies-checks
    against it (it does not become its source), avoiding a utils↔core import cycle.
  • registry.ts — a 5-row as const satisfies readonly PlatformDescriptor[]
    (ios/macosapple, androidandroid, linuxlinux, webweb), in
    PLATFORMS order. A compile-time totality alias preserves the prior never
    safety: adding a new Platform without a row fails the build.
  • derive.ts — pure folds: deriveCapabilityForPlatform(descriptors, capability, platform)
    reproduces the hand switch exactly (maps platform→bucket via the registry, reads that
    family off the capability), and deriveApplePlatforms(descriptors). Imports only
    type-level CommandCapability from ../capabilities.ts and Platform from
    ../../utils/device.ts — nothing from commands/daemon/platforms.
  • __tests__/parity.test.ts — proves (a) the derive is value-identical to an
    independent VERBATIM copy of the old switch for every platform in PLATFORMS
    (dense + sparse capabilities, reference-identity comparison), (b) the descriptor
    Apple rows equal the leaf platforms where isApplePlatform() is true and
    isApple === (capabilityBucket === 'apple') for every row, (c) totality:
    platformDescriptors.map(d => d.platform) deep-equals [...PLATFORMS] in order.

The one flip (behaviorless, parity-proven)

selectCapabilityForPlatform in src/core/capabilities.ts now folds the registry via
deriveCapabilityForPlatform; the hand switch is deleted. Signature + behavior are
identical — the consumer suite (core/__tests__/capabilities.test.ts, exercising all
five platforms through isCommandSupportedOnDevice) stays green unchanged.

Layering

All new code lives in src/core. The derive.ts only type-imports from capabilities.ts
(erased under verbatimModuleSyntax), so wiring it into capabilities.ts forms no runtime
cycle — same pattern as command-descriptor/derive.ts. The CI Layering Guard
(src/daemon & src/platforms must not import commands/) is unaffected.

Deliberately deferred (per ADR-0009)

  • No change to isApplePlatform in utils/device.ts (re-expressing it via the core
    registry would invert utils→core layering).
  • No ios+macosapple collapse.
  • No directory moves; no touch to the interactor factory, device discovery
    (platform-inventory.ts), the Apple runner-profile Record, or the
    supports()/unsupportedHint() closures.

Verification

  • tsc -p tsconfig.json --noEmit — exit 0
  • oxfmt --write + oxlint --deny-warnings on changed files — exit 0
  • vitest run for core/platform-descriptor, core/capabilities, command-descriptor,
    and the utils/args consumer — all green
  • Layering guard grep — empty

Introduce src/core/platform-descriptor/ (mirroring src/core/command-descriptor/):
a 5-row PlatformDescriptor registry carrying each leaf platform's capability
bucket and isApple flag, pure derive folds, and a byte-for-byte parity test.

Rewrite selectCapabilityForPlatform to fold the registry via the derive fn and
delete the hand switch. Behaviorless and parity-proven; layering-safe (all in
core, no utils->core inversion). Defers the ios/macos->apple collapse and the
interactor/discovery/runner-profile tables to later per ADR-0009.
@github-actions

github-actions Bot commented Jun 27, 2026

Copy link
Copy Markdown

Size Report

Metric Base Current Diff
JS raw 1.4 MB 1.4 MB +268 B
JS gzip 445.5 kB 445.6 kB +77 B
npm tarball 584.7 kB 584.7 kB +58 B
npm unpacked 2.0 MB 2.0 MB +268 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 27.7 ms 27.5 ms -0.2 ms
CLI --help 48.9 ms 48.6 ms -0.3 ms

Top changed chunks:

Chunk Raw diff Gzip diff
dist/src/9722.js +268 B +77 B

@thymikee

Copy link
Copy Markdown
Member Author

Reviewed — behaviorless and parity-proven. selectCapabilityForPlatform now folds the 5-row platformDescriptors registry (deriveCapabilityForPlatform), hand switch deleted. The parity test keeps an independent verbatim copy of the old switch and asserts reference-identity equality for every platform in PLATFORMS (distinct per-bucket object identities so a wrong bucket fails the ===, plus a sparse capability to pin the undefined path) — so it stays a real check after the flip, not a tautology. The compile-time totality alias preserves the old never exhaustiveness (a new Platform without a row fails the build). Layering-safe (all in src/core, imports only Platform from utils + capability types), isApplePlatform/PLATFORMS left as the source (no utils→core inversion), and the load-bearing tables (interactor/discovery/runner-profile) + the ios+macosapple collapse correctly deferred per ADR-0009. tsc/lint/tests green, layering guard empty. LGTM.

The identity helper had no consumers (the registry uses `as const satisfies`),
so Fallow flagged it as a newly-added unused export. Remove it.
@thymikee

Copy link
Copy Markdown
Member Author

Reviewed this against plans/perfect-shape.md and ADR 0009/0008. I do not see blockers.

What I checked:

  • The new platform-descriptor layer keeps Platform sourced from utils/device.ts; it does not make utils import core or start the final ios/macos collapse early.
  • capabilities.ts only swaps the old platform switch for deriveCapabilityForPlatform(platformDescriptors, ...).
  • The parity test compares against an independent copy of the deleted switch, covers dense/sparse capability rows, verifies Apple rows against isApplePlatform, and pins descriptor order to PLATFORMS.
  • CI is green, including typecheck, unit, integration, layering, fallow, smoke, and iOS runner compatibility.

Residual risk is low and mostly architectural: this is still an additive registry foothold, not the full PlatformPlugin migration. I would merge after maintainers are happy with the new CapabilityBucket/isApple descriptor vocabulary.

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Jun 28, 2026
@thymikee thymikee merged commit c49e4fc into main Jun 28, 2026
20 checks passed
@thymikee thymikee deleted the feat/platform-descriptor-registry branch June 28, 2026 12:10
@github-actions

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-06-28 12:11 UTC

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

Labels

ready-for-human Valid work that needs human implementation, judgment, or maintainer merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant