Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions docs/dependency-graph-findings.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ with `command`/`positionals` `Pick`ed from the wire so they cannot drift from it
resolver already read only those three, in two spellings (the full type and a `Pick` of it); one
narrow name replaced both.

**The remaining 5 are positions, not debt** — each for a mechanical reason, not an appeal to an ADR:
**The remaining 4 are positions, not debt** — each for a mechanical reason, not an appeal to an ADR:

- **4 × `AgentDeviceClient`** (`commands/command-contract.ts`, `commands/command-surface.ts`,
`commands/family/types.ts`, `mcp/command-tools.ts`). The zone-level type cycle this bullet used to
Expand All @@ -120,11 +120,13 @@ narrow name replaced both.
facade above `commands/` is the remaining argument: a narrower port does not exist — 4 files
*name* the facade, but 26 call sites use methods across 13 of its namespaces, so any port would
re-declare it.
- **1 × `DaemonCommandRoute`** (`commands/command-explain.ts`). The union lives in core so
descriptors can name a route without importing the daemon, and the handler table covers it with
`satisfies Record<DaemonCommandRoute, …>`. `command-explain.ts` still type-imports the re-export
from `daemon-command-registry.ts` to key an exhaustive owner-file map; that remaining inversion
is the commands-zone consumer, not a second source of truth for the union.

Retired by #2543: the **1 × `DaemonCommandRoute`** inversion whose only commands-zone consumer was
`command-explain.ts`. The union lives in core so descriptors can name a route without importing the
daemon; the explainer only type-imported the `daemon-command-registry.ts` re-export to key an
exhaustive owner-file map. #2543 relocated `command-explain.ts` to the `cli/` zone — above
`daemon-server` — so that consumer's type import no longer outranks its target, and the
`commands -> daemon-server` R6 pair fell to zero.

All remaining inversions are argued here. R6 (`scripts/layering/type-inversion-ratchet.ts`) records
no numbers of its own: its reference is the same count taken at the merge-base with `origin/main`,
Expand Down
4 changes: 4 additions & 0 deletions packages/command-registry/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@
"types": "./src/command-text.ts",
"default": "./src/command-text.ts"
},
"./command-schema": {
"types": "./src/command-schema.ts",
"default": "./src/command-schema.ts"
},
"./cli-command-aliases": {
"types": "./src/cli-command-aliases.ts",
"default": "./src/cli-command-aliases.ts"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { FlagKey } from '@agent-device/command-registry/flag-types';
import type { CommandText } from '@agent-device/command-registry/command-text';
import type { FlagKey } from './flag-types.ts';
import type { CommandText } from './command-text.ts';

/**
* Command grammar plus its resolved text. Prose lives entirely in `text`; everything else here
Expand Down
2 changes: 1 addition & 1 deletion scripts/explain-command.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { explainCommand, formatCommandExplanation } from '../src/commands/command-explain.ts';
import { explainCommand, formatCommandExplanation } from '../src/cli/command-explain.ts';
import { getDaemonRouteOwnerFiles } from '../src/daemon/route-owner-files.ts';

const repoRoot = path.resolve(import.meta.dirname, '..');
Expand Down
3 changes: 3 additions & 0 deletions scripts/layering/check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
// ◄ { client, daemon-server } ◄ daemon-client ◄ cli
// (authoritative ranks: `TARGET_DAG_RANK` in model.ts. The former rank-0 kernel
// zone lives in packages/kernel since #1490 W0; R11 owns its boundary.)
// `commands` and `cli-schema` share a rank, so the spine cannot order them; R2 declares the
// direction instead — cli-schema renders the command facets and reads them, commands never
// imports cli-schema (#2543).
//
// This gate enforces five things, across four scopes:
// - GLOBALLY, across every production source file: the remaining R2 move rule and
Expand Down
16 changes: 16 additions & 0 deletions scripts/layering/zone-policy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,22 @@ test('R2 commands-floor closes the remaining zones below the command surface, wh
assert.deepEqual(firing(edge('src/mcp/tools.ts', 'mcp', 'commands')), []);
});

test('R2 commands-floor places commands below the cli-schema layer, whatever the kind', () => {
// #2543: cli-schema renders the facets commands authors, so it reads commands and commands never
// reads it back. The direction holds for value, type-only, and dynamic forms alike.
for (const kind of [{}, { typeOnly: true }, { dynamic: true }]) {
assert.ok(
firing(edge('src/commands/thing.ts', 'commands', 'cli-schema', kind)).includes(
'R2 commands-floor',
),
`commands -> cli-schema ${JSON.stringify(kind)} must violate R2`,
);
}

// The declared direction — cli-schema reading the facets — must stay silent.
assert.deepEqual(firing(edge('src/cli-schema/command-schema.ts', 'cli-schema', 'commands')), []);
});

test('the retired R3 platforms seam has no zone-policy declaration', () => {
assert.equal(
ZONE_POLICIES.some(({ rule }) => rule === 'R3 platforms-seam'),
Expand Down
14 changes: 14 additions & 0 deletions scripts/layering/zone-policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ export type ZonePolicy = {
* R1 kernel-sink retired 2026-07-30 (#1490 W0): the kernel moved to
* packages/kernel, where package resolution and R11 package-boundaries enforce
* the sink property physically — a package cannot import root src at all.
*
* The two rows share `R2 commands-floor`: both state that commands sits at a floor and is read
* from above. The first keeps `core`/`daemon` below it; the second places `cli-schema` above it,
* so commands may not import the schema layer that renders its facets (#2543 declared the
* direction after the shared schema grammar moved down to the command registry).
*/
export const ZONE_POLICIES: readonly ZonePolicy[] = [
{
Expand All @@ -53,6 +58,15 @@ export const ZONE_POLICIES: readonly ZonePolicy[] = [
'commands/ is the command surface, above these zones. Depend on shared kernel/contracts ' +
'instead; if two zones need the same rule, put the rule below both of them.',
},
{
rule: 'R2 commands-floor',
from: ['commands'],
to: ['cli-schema'],
hint:
'cli-schema is the CLI/MCP schema layer that renders the command facets, so it sits above ' +
'commands and reads them; commands must not import it back. The shared CommandSchema type and ' +
'flag grammar live in @agent-device/command-registry, below both zones.',
},
];

function kindOf(imp: ImportEdge): 'type-only' | 'dynamic' | 'value' {
Expand Down
2 changes: 1 addition & 1 deletion src/cli-schema/cli-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import path from 'node:path';
import { AppError } from '@agent-device/kernel/errors';
import { mergeDefinedFlags } from './merge-flags.ts';
import { type FlagKey } from '@agent-device/command-registry/flag-types';
import { projectConfigFlagKeys } from './command-schema.ts';
import { projectConfigFlagKeys } from '@agent-device/command-registry/flag-registry';
import { expandUserHomePath, resolveUserPath } from '@agent-device/host-kit/file';
import {
getConfigurableOptionSpecs,
Expand Down
2 changes: 1 addition & 1 deletion src/cli-schema/command-overrides.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { CommandName } from '../commands/command-metadata.ts';
import { listCommandFamilyCliSchemas } from '../commands/family/registry.ts';
import type { LocalCliCommandName } from '@agent-device/command-registry/catalog';
import type { CommandSchema } from './types.ts';
import type { CommandSchema } from '@agent-device/command-registry/command-schema';
import {
COMMON_COMMAND_SUPPORTED_FLAG_KEYS,
METRO_PREPARE_FLAGS,
Expand Down
11 changes: 2 additions & 9 deletions src/cli-schema/command-schema.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import type { CliCommandName } from '@agent-device/command-registry/catalog';
import { listCommandMetadata } from '../commands/command-metadata.ts';
import type { CommandSchema } from './types.ts';
import type { CommandSchema } from '@agent-device/command-registry/command-schema';
import { getCliCommandOverride, getSchemaOnlyCliCommandSchema } from './command-overrides.ts';
import {
getFlagDefinition,
getFlagDefinitions,
projectConfigFlagKeys,
} from '@agent-device/command-registry/flag-registry';
import {
COMMON_COMMAND_SUPPORTED_FLAG_KEYS,
Expand All @@ -17,13 +16,7 @@ import { AppError } from '@agent-device/kernel/errors';

export type { FlagDefinition, FlagKey };
export type { CommandSchema };
export {
DEVICE_SELECTION_FLAG_KEYS,
getFlagDefinition,
getFlagDefinitions,
GLOBAL_FLAG_KEYS,
projectConfigFlagKeys,
};
export { DEVICE_SELECTION_FLAG_KEYS, getFlagDefinition, getFlagDefinitions, GLOBAL_FLAG_KEYS };

// Bases hold only the flags every command supports; prose arrives with the facet's schema,
// which always carries a complete `text`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
type FlagDefinition,
type FlagKey,
} from '../cli-schema/command-schema.ts';
import { commandFamilies, type CommandFamilyMetadata } from './family/registry.ts';
import { commandFamilies, type CommandFamilyMetadata } from '../commands/family/registry.ts';

export type CommandFlagExplanation = {
key: FlagKey;
Expand Down
2 changes: 1 addition & 1 deletion src/commands/__tests__/command-surface-metadata.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
} from '../family/registry.ts';
import { listExecutableCommandNames } from '../command-surface.ts';
import { helpBody, mcpBody } from '@agent-device/command-registry/command-text';
import { explainCommand } from '../command-explain.ts';
import { explainCommand } from '../../cli/command-explain.ts';
import { getDaemonRouteOwnerFiles } from '../../daemon/route-owner-files.ts';

test('MCP exposed command names have metadata and executable command definitions', () => {
Expand Down
2 changes: 1 addition & 1 deletion src/commands/batch/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { BatchRunOptions } from '@agent-device/contracts/client';
import type { CommandSchemaOverride } from '../../cli-schema/types.ts';
import type { CommandSchemaOverride } from '@agent-device/command-registry/command-schema';
import { commonInputFromFlags } from '../cli-grammar/common.ts';
import type { CliReader } from '../cli-grammar/types.ts';
import { defineCommandFacet, defineCommandFamilyFromFacets } from '../family/types.ts';
Expand Down
2 changes: 1 addition & 1 deletion src/commands/capture/settings.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { PUBLIC_COMMANDS } from '@agent-device/command-registry/catalog';
import type { SettingsUpdateOptions } from '@agent-device/contracts/client';
import { SETTINGS_USAGE_OVERRIDE } from '@agent-device/contracts/settings';
import type { CommandSchemaOverride } from '../../cli-schema/types.ts';
import type { CommandSchemaOverride } from '@agent-device/command-registry/command-schema';
import type { CliFlags } from '@agent-device/contracts/command';
import { AppError } from '@agent-device/kernel/errors';
import { readLocationCoordinate } from '@agent-device/kernel/location-coordinates';
Expand Down
2 changes: 1 addition & 1 deletion src/commands/debugging/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { AppError } from '@agent-device/kernel/errors';
import type { CommandSchemaOverride } from '../../cli-schema/types.ts';
import type { CommandSchemaOverride } from '@agent-device/command-registry/command-schema';
import { enumField, requiredField, stringField } from '../command-input.ts';
import { defineCommandFacet, defineCommandFamilyFromFacets } from '../family/types.ts';
import { defineFieldCommandMetadata } from '../field-command-contract.ts';
Expand Down
2 changes: 1 addition & 1 deletion src/commands/family/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { reactNativeCommandFamily } from '../react-native/index.ts';
import { recordingCommandFamily } from '../recording/index.ts';
import { replayCommandFamily } from '../replay/index.ts';
import { systemCommandFamily } from '../system/index.ts';
import type { CommandSchema } from '../../cli-schema/types.ts';
import type { CommandSchema } from '@agent-device/command-registry/command-schema';
import { type CommandFamilyFacet } from './types.ts';

type CommandFamilyRecordMap = {
Expand Down
5 changes: 4 additions & 1 deletion src/commands/family/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import type { AgentDeviceClient } from '../../client/client-types.ts';
import type { CommandSchema, CommandSchemaOverride } from '../../cli-schema/types.ts';
import type {
CommandSchema,
CommandSchemaOverride,
} from '@agent-device/command-registry/command-schema';
import type { AnyDaemonWriter, CliReader } from '../cli-grammar/types.ts';
import type { CommandMetadata, JsonSchema } from '../command-contract.ts';
import type { CliOutputFormatter } from '../output-common.ts';
Expand Down
2 changes: 1 addition & 1 deletion src/commands/interaction/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import type {
TransformGestureOptions,
TypeTextOptions,
} from '@agent-device/contracts/client';
import type { CommandSchemaOverride } from '../../cli-schema/types.ts';
import type { CommandSchemaOverride } from '@agent-device/command-registry/command-schema';
import {
REPEATED_TOUCH_FLAGS,
SELECTOR_SNAPSHOT_FLAGS,
Expand Down
2 changes: 1 addition & 1 deletion src/commands/management/app.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { PUBLIC_COMMANDS } from '@agent-device/command-registry/catalog';
import type { AppCloseOptions, AppOpenOptions } from '@agent-device/contracts/client';
import { SESSION_SURFACES } from '@agent-device/contracts/session';
import type { CommandSchemaOverride } from '../../cli-schema/types.ts';
import type { CommandSchemaOverride } from '@agent-device/command-registry/command-schema';
import { assertResolvedAppsFilter } from './app-inventory-contract.ts';
import {
booleanField,
Expand Down
2 changes: 1 addition & 1 deletion src/commands/management/artifacts.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PUBLIC_COMMANDS } from '@agent-device/command-registry/catalog';
import type { CommandSchemaOverride } from '../../cli-schema/types.ts';
import type { CommandSchemaOverride } from '@agent-device/command-registry/command-schema';
import { stringField } from '../command-input.ts';
import { commonInputFromFlags, direct } from '../cli-grammar/common.ts';
import type { CliReader, DaemonWriter } from '../cli-grammar/types.ts';
Expand Down
2 changes: 1 addition & 1 deletion src/commands/management/device.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PUBLIC_COMMANDS } from '@agent-device/command-registry/catalog';
import type { CommandSchemaOverride } from '../../cli-schema/types.ts';
import type { CommandSchemaOverride } from '@agent-device/command-registry/command-schema';
import { booleanField } from '../command-input.ts';
import { commonInputFromFlags, direct } from '../cli-grammar/common.ts';
import type { CliReader, DaemonWriter } from '../cli-grammar/types.ts';
Expand Down
2 changes: 1 addition & 1 deletion src/commands/management/doctor.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PUBLIC_COMMANDS } from '@agent-device/command-registry/catalog';
import type { CommandSchemaOverride } from '../../cli-schema/types.ts';
import type { CommandSchemaOverride } from '@agent-device/command-registry/command-schema';
import * as commandInput from '../command-input.ts';
import { commonInputFromFlags, direct } from '../cli-grammar/common.ts';
import type { CliReader, DaemonWriter } from '../cli-grammar/types.ts';
Expand Down
2 changes: 1 addition & 1 deletion src/commands/management/install.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { INTERNAL_COMMANDS, PUBLIC_COMMANDS } from '@agent-device/command-registry/catalog';
import type { DaemonInstallSource } from '@agent-device/kernel/contracts';
import type { CliFlags } from '@agent-device/contracts/command';
import type { CommandSchemaOverride } from '../../cli-schema/types.ts';
import type { CommandSchemaOverride } from '@agent-device/command-registry/command-schema';
import { AppError } from '@agent-device/kernel/errors';
import { parseGitHubActionsArtifactInstallSourceSpec } from '@agent-device/provision-kit/install-source-config';
import {
Expand Down
2 changes: 1 addition & 1 deletion src/commands/management/prepare.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PUBLIC_COMMANDS } from '@agent-device/command-registry/catalog';
import type { CommandSchemaOverride } from '../../cli-schema/types.ts';
import type { CommandSchemaOverride } from '@agent-device/command-registry/command-schema';
import { enumField, integerField, requiredField } from '../command-input.ts';
import {
commonInputFromFlags,
Expand Down
2 changes: 1 addition & 1 deletion src/commands/management/push.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type {
AppTriggerEventOptions,
JsonObject,
} from '@agent-device/contracts/client';
import type { CommandSchemaOverride } from '../../cli-schema/types.ts';
import type { CommandSchemaOverride } from '@agent-device/command-registry/command-schema';
import { PUBLIC_COMMANDS } from '@agent-device/command-registry/catalog';
import {
commonInputFromFlags,
Expand Down
2 changes: 1 addition & 1 deletion src/commands/management/session.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { AppError } from '@agent-device/kernel/errors';
import type { CommandSchemaOverride } from '../../cli-schema/types.ts';
import type { CommandSchemaOverride } from '@agent-device/command-registry/command-schema';
import { booleanField, enumField, stringField } from '../command-input.ts';
import { commonInputFromFlags } from '../cli-grammar/common.ts';
import type { CliReader } from '../cli-grammar/types.ts';
Expand Down
2 changes: 1 addition & 1 deletion src/commands/management/viewport.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { PUBLIC_COMMANDS } from '@agent-device/command-registry/catalog';
import type { ViewportCommandOptions } from '@agent-device/contracts/client';
import { readViewportDimensions } from '@agent-device/contracts/capture';
import type { CommandSchemaOverride } from '../../cli-schema/types.ts';
import type { CommandSchemaOverride } from '@agent-device/command-registry/command-schema';
import { integerField, requiredField } from '../command-input.ts';
import { commonInputFromFlags, direct } from '../cli-grammar/common.ts';
import type { CliReader, DaemonWriter } from '../cli-grammar/types.ts';
Expand Down
2 changes: 1 addition & 1 deletion src/commands/metro/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type {
MetroReloadResult,
} from '@agent-device/contracts/remote';
import { AppError } from '@agent-device/kernel/errors';
import type { CommandSchemaOverride } from '../../cli-schema/types.ts';
import type { CommandSchemaOverride } from '@agent-device/command-registry/command-schema';
import {
booleanField,
enumField,
Expand Down
2 changes: 1 addition & 1 deletion src/commands/observability/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type {
import { NETWORK_INCLUDE_MODES, type NetworkIncludeMode } from '@agent-device/kernel/contracts';
import { AppError } from '@agent-device/kernel/errors';
import { parseStringMember } from './string-enum.ts';
import type { CommandSchemaOverride } from '../../cli-schema/types.ts';
import type { CommandSchemaOverride } from '@agent-device/command-registry/command-schema';
import { defineCommandFacet, defineCommandFamilyFromFacets } from '../family/types.ts';
import { booleanField, enumField, integerField, stringField } from '../command-input.ts';
import { defineFieldCommandMetadata } from '../field-command-contract.ts';
Expand Down
2 changes: 1 addition & 1 deletion src/commands/perf/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { PerfOptions } from '@agent-device/contracts/client';
import { AppError } from '@agent-device/kernel/errors';
import type { CommandSchemaOverride } from '../../cli-schema/types.ts';
import type { CommandSchemaOverride } from '@agent-device/command-registry/command-schema';
import { enumField, requiredField, stringField } from '../command-input.ts';
import { defineCommandFacet, defineCommandFamilyFromFacets } from '../family/types.ts';
import { defineFieldCommandMetadata } from '../field-command-contract.ts';
Expand Down
2 changes: 1 addition & 1 deletion src/commands/react-native/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { AppError } from '@agent-device/kernel/errors';
import type { CommandSchemaOverride } from '../../cli-schema/types.ts';
import type { CommandSchemaOverride } from '@agent-device/command-registry/command-schema';
import { defineCommandFacet, defineCommandFamilyFromFacets } from '../family/types.ts';
import { enumField, requiredField } from '../command-input.ts';
import { defineFieldCommandMetadata } from '../field-command-contract.ts';
Expand Down
2 changes: 1 addition & 1 deletion src/commands/recording/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
RECORDING_SCOPE_VALUES,
} from '@agent-device/contracts/recording';
import { AppError } from '@agent-device/kernel/errors';
import type { CommandSchemaOverride } from '../../cli-schema/types.ts';
import type { CommandSchemaOverride } from '@agent-device/command-registry/command-schema';
import { commonInputFromFlags, direct, optionalString } from '../cli-grammar/common.ts';
import type { CliReader, DaemonWriter } from '../cli-grammar/types.ts';
import {
Expand Down
2 changes: 1 addition & 1 deletion src/commands/replay/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { CommandSchemaOverride } from '../../cli-schema/types.ts';
import type { CommandSchemaOverride } from '@agent-device/command-registry/command-schema';
import { defineCommandFacet, defineCommandFamilyFromFacets } from '../family/types.ts';
import {
booleanField,
Expand Down
2 changes: 1 addition & 1 deletion src/commands/system/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
tvRemoteDurationMode,
} from '@agent-device/contracts/tv-remote';
import { AppError } from '@agent-device/kernel/errors';
import type { CommandSchemaOverride } from '../../cli-schema/types.ts';
import type { CommandSchemaOverride } from '@agent-device/command-registry/command-schema';
import {
commonInputFromFlags,
direct,
Expand Down
Loading