feat: command-descriptor registry (additive, parity-tested) — Phase 1 step 1#906
Conversation
Size Report
Startup median (7 runs, lower is better):
Top changed chunks:
|
|
Finding: the descriptor registry is currently rooted under
That makes this foundation hard to consume as-is. Before merging the foundation, please either move the descriptor root/projections to a neutral shared layer that daemon may import, or update the layering rule with an explicit ADR 0008 exception that still preserves ADR 0003's daemon-owned facet boundary and prevents public projections from reading daemon-only traits. Otherwise #906 lands a dormant registry whose first real consumer cannot pass CI. I did verify the parity test locally in an isolated worktree: |
|
Good catch — addressing the layering decision before going further, taking option 1. I'm relocating the shared descriptor registry + projections out of
Relocating across the stack (#906 → #907 → #908), re-running the Layering Guard ( |
… step 1 Introduce a single dormant CommandDescriptor registry as the strangler-fig spine for ADR-0008. Per command it carries the daemon route + request-policy traits (incl. closure traits), an optional capability entry, and batchable/mcpExposed flags — copied verbatim from today's hand tables. Pure derive folds reconstruct DAEMON_COMMAND_DESCRIPTORS, the capability matrix, and the structured-batch names; parity tests prove each derivation byte-equal to the live hand table. Nothing reads the registry yet. Purely additive: no consumers changed, nothing deleted, no import inversion. The only edits outside the new directory expose three existing tables (DAEMON_COMMAND_DESCRIPTORS, DaemonCommandDescriptor, BASE_COMMAND_CAPABILITY_MATRIX) so the parity tests can read them.
97b88ff to
0acefbf
Compare
… — Phase 1 step 3 Build BASE_COMMAND_CAPABILITY_MATRIX from deriveCapabilityMatrix(commandDescriptors) and delete the hand-authored literal (ADR-0008, Phase 1 step 3). Behaviorless: #906 proved the derived matrix is byte-equal to the literal, including the supports/unsupportedHint closures across the sample-device matrix. Delete the now-dead local predicate helpers and capability bundles that only the literal referenced (isNotMacOs, isMacOsOrAppleSimulator, isIosMobileSimulator, synthesisGestureUnsupportedHint, ALL_DEVICE_COMMAND_CAPABILITY, APP_* bundles, LINUX_DEVICE, LINUX_NONE); their copies now live in registry.ts. WEB_* command sets and addWebCommandCapabilities are kept. The registry/derive/types modules only type-import CommandCapability from capabilities.ts, so the new value-level dependency forms no runtime cycle (tsc clean + runtime import() of capabilities.ts loads with no TDZ/circular-init error). Rewrite the capability parity tests (now tautologies) into an admission invariant: every matrix entry is selectable (platform bucket or supports predicate) and the public-command coverage floor is unchanged. Daemon and batch-name parity tests kept.
|
… — Phase 1 step 3 Build BASE_COMMAND_CAPABILITY_MATRIX from deriveCapabilityMatrix(commandDescriptors) and delete the hand-authored literal (ADR-0008, Phase 1 step 3). Behaviorless: #906 proved the derived matrix is byte-equal to the literal, including the supports/unsupportedHint closures across the sample-device matrix. Delete the now-dead local predicate helpers and capability bundles that only the literal referenced (isNotMacOs, isMacOsOrAppleSimulator, isIosMobileSimulator, synthesisGestureUnsupportedHint, ALL_DEVICE_COMMAND_CAPABILITY, APP_* bundles, LINUX_DEVICE, LINUX_NONE); their copies now live in registry.ts. WEB_* command sets and addWebCommandCapabilities are kept. The registry/derive/types modules only type-import CommandCapability from capabilities.ts, so the new value-level dependency forms no runtime cycle (tsc clean + runtime import() of capabilities.ts loads with no TDZ/circular-init error). Rewrite the capability parity tests (now tautologies) into an admission invariant: every matrix entry is selectable (platform bucket or supports predicate) and the public-command coverage floor is unchanged. Daemon and batch-name parity tests kept.
… — Phase 1 step 3 Build BASE_COMMAND_CAPABILITY_MATRIX from deriveCapabilityMatrix(commandDescriptors) and delete the hand-authored literal (ADR-0008, Phase 1 step 3). Behaviorless: #906 proved the derived matrix is byte-equal to the literal, including the supports/unsupportedHint closures across the sample-device matrix. Delete the now-dead local predicate helpers and capability bundles that only the literal referenced (isNotMacOs, isMacOsOrAppleSimulator, isIosMobileSimulator, synthesisGestureUnsupportedHint, ALL_DEVICE_COMMAND_CAPABILITY, APP_* bundles, LINUX_DEVICE, LINUX_NONE); their copies now live in registry.ts. WEB_* command sets and addWebCommandCapabilities are kept. The registry/derive/types modules only type-import CommandCapability from capabilities.ts, so the new value-level dependency forms no runtime cycle (tsc clean + runtime import() of capabilities.ts loads with no TDZ/circular-init error). Rewrite the capability parity tests (now tautologies) into an admission invariant: every matrix entry is selectable (platform bucket or supports predicate) and the public-command coverage floor is unchanged. Daemon and batch-name parity tests kept.
… — Phase 1 step 3 (#908) Build BASE_COMMAND_CAPABILITY_MATRIX from deriveCapabilityMatrix(commandDescriptors) and delete the hand-authored literal (ADR-0008, Phase 1 step 3). Behaviorless: #906 proved the derived matrix is byte-equal to the literal, including the supports/unsupportedHint closures across the sample-device matrix. Delete the now-dead local predicate helpers and capability bundles that only the literal referenced (isNotMacOs, isMacOsOrAppleSimulator, isIosMobileSimulator, synthesisGestureUnsupportedHint, ALL_DEVICE_COMMAND_CAPABILITY, APP_* bundles, LINUX_DEVICE, LINUX_NONE); their copies now live in registry.ts. WEB_* command sets and addWebCommandCapabilities are kept. The registry/derive/types modules only type-import CommandCapability from capabilities.ts, so the new value-level dependency forms no runtime cycle (tsc clean + runtime import() of capabilities.ts loads with no TDZ/circular-init error). Rewrite the capability parity tests (now tautologies) into an admission invariant: every matrix entry is selectable (platform bucket or supports predicate) and the public-command coverage floor is unchanged. Daemon and batch-name parity tests kept.
What
ADR-0008 Phase 1 step 1: introduce a single
CommandDescriptorregistry as the strangler-fig foundation for the command-descriptor migration. This is purely additive and dormant — nothing reads the new registry yet; it exists only to be proven byte-equal to today's hand tables.New files (
src/commands/descriptor/)types.ts—CommandDescriptortype +defineCommandDescriptor(). Per command it carries the daemon route + request-policy traits (reusing the existingDaemonCommandDescriptorshape including the closure traitsallowSessionlessDefaultDevice/skipSessionlessProviderDevice, not flattened), an optionalCommandCapabilityentry, andbatchable/mcpExposedflags.registry.ts—commandDescriptors, the additive single source. Covers the full daemon key space (public + internal + gesture-alias names) plus the two capability/batch-only commands (app-switcher,install-from-source). Daemon traits, capability, andbatchableare copied verbatim fromDAEMON_COMMAND_DESCRIPTORS,BASE_COMMAND_CAPABILITY_MATRIX, andSTRUCTURED_BATCH_COMMAND_NAMES.derive.ts— pure folds:deriveDaemonCommandDescriptors,deriveCapabilityMatrix,deriveStructuredBatchCommandNames.__tests__/parity.test.ts— asserts each derived table matches the live hand table. Function-valued traits (daemon closures + capabilitysupports/unsupportedHint) are compared by presence + behavior on a representative request/device sample (mirroringdaemon-command-registry.test.ts); every other field isdeepEqual'd.Derivations proven byte-equal
deepEqualagainstDAEMON_COMMAND_DESCRIPTORS(closures by behavior).deepEqualagainstBASE_COMMAND_CAPABILITY_MATRIX(supports/unsupportedHint by behavior across 11 sample devices).STRUCTURED_BATCH_COMMAND_NAMES. (Order is not reproduced: the daemon and batch hand tables are independently ordered, so one registry table cannot reproduce both array orders; the consumer dedupes into aSet, so ordering is cosmetic and deferred to a later slice.)Additive / dormant guarantee
No consumers changed, nothing deleted, no import-graph inversion. The only edits outside
src/commands/descriptor/add anexportkeyword to three existing tables so the parity tests can read them:DAEMON_COMMAND_DESCRIPTORS+ theDaemonCommandDescriptortype (daemon-command-registry.ts) andBASE_COMMAND_CAPABILITY_MATRIX(capabilities.ts). No behavior change.Verification
tsc --noEmit✅oxfmt --write+oxlint --deny-warnings✅vitest run commands/descriptor daemon-command-registry capabilities batch→ 7 files / 42 tests ✅