From 02c46dd4b0908c917b0a8ada842ef8cec5432f48 Mon Sep 17 00:00:00 2001 From: Test Date: Mon, 25 May 2026 19:27:28 -0500 Subject: [PATCH 1/2] feat(commands): add typed execution scope --- .../send/browser/AircSendBrowserCommand.ts | 5 ++- .../airc/send/server/AircSendServerCommand.ts | 5 ++- .../server/DecisionProposeServerCommand.ts | 2 +- .../propose/shared/DecisionProposeTypes.ts | 3 +- .../send/browser/GridSendBrowserCommand.ts | 6 ++- .../grid/send/server/GridSendServerCommand.ts | 6 ++- .../list/server/SkillListServerCommand.ts | 4 +- .../skill/list/shared/SkillListTypes.ts | 10 ++--- .../server/SkillProposeServerCommand.ts | 4 +- .../skill/propose/shared/SkillProposeTypes.ts | 6 +-- .../command-daemon/shared/CommandBase.ts | 26 +++++++++++-- .../command-daemon/shared/CommandDaemon.ts | 17 ++++++--- src/system/core/types/JTAGTypes.ts | 38 ++++++++++++++++++- 13 files changed, 103 insertions(+), 29 deletions(-) diff --git a/src/commands/airc/send/browser/AircSendBrowserCommand.ts b/src/commands/airc/send/browser/AircSendBrowserCommand.ts index 76d80d595..1a10d30e8 100644 --- a/src/commands/airc/send/browser/AircSendBrowserCommand.ts +++ b/src/commands/airc/send/browser/AircSendBrowserCommand.ts @@ -5,10 +5,13 @@ */ import { CommandBase, type ICommandDaemon } from '@daemons/command-daemon/shared/CommandBase'; -import type { JTAGContext } from '@system/core/types/JTAGTypes'; +import type { CommandScope, JTAGContext } from '@system/core/types/JTAGTypes'; import type { AircSendParams, AircSendResult } from '../shared/AircSendTypes'; export class AircSendBrowserCommand extends CommandBase { + protected static override get naturalScope(): CommandScope { + return { type: 'room' }; + } constructor(context: JTAGContext, subpath: string, commander: ICommandDaemon) { super('airc/send', context, subpath, commander); diff --git a/src/commands/airc/send/server/AircSendServerCommand.ts b/src/commands/airc/send/server/AircSendServerCommand.ts index 35b42a08e..a2267e290 100644 --- a/src/commands/airc/send/server/AircSendServerCommand.ts +++ b/src/commands/airc/send/server/AircSendServerCommand.ts @@ -33,12 +33,15 @@ import { spawn } from 'node:child_process'; import { existsSync, readFileSync } from 'node:fs'; import * as path from 'node:path'; import { CommandBase, type ICommandDaemon } from '@daemons/command-daemon/shared/CommandBase'; -import type { JTAGContext } from '@system/core/types/JTAGTypes'; +import type { CommandScope, JTAGContext } from '@system/core/types/JTAGTypes'; import { ValidationError } from '@system/core/types/ErrorTypes'; import type { AircSendParams, AircSendResult } from '../shared/AircSendTypes'; import { createAircSendResultFromParams } from '../shared/AircSendTypes'; export class AircSendServerCommand extends CommandBase { + protected static override get naturalScope(): CommandScope { + return { type: 'room' }; + } constructor(context: JTAGContext, subpath: string, commander: ICommandDaemon) { super('airc/send', context, subpath, commander); diff --git a/src/commands/collaboration/decision/propose/server/DecisionProposeServerCommand.ts b/src/commands/collaboration/decision/propose/server/DecisionProposeServerCommand.ts index 1e7fa103a..8b5cbfa49 100644 --- a/src/commands/collaboration/decision/propose/server/DecisionProposeServerCommand.ts +++ b/src/commands/collaboration/decision/propose/server/DecisionProposeServerCommand.ts @@ -305,7 +305,7 @@ export class DecisionProposeServerCommand extends DecisionProposeCommand { const proposerId: UUID = params.userId; const proposerName: string = proposerResult.data.displayName; - const scope = params.scope || 'all'; + const scope = params.proposalScope || 'all'; const significanceLevel = params.significanceLevel || 'medium'; const proposalId = generateUUID(); diff --git a/src/commands/collaboration/decision/propose/shared/DecisionProposeTypes.ts b/src/commands/collaboration/decision/propose/shared/DecisionProposeTypes.ts index 7e75c6968..f211cdf59 100644 --- a/src/commands/collaboration/decision/propose/shared/DecisionProposeTypes.ts +++ b/src/commands/collaboration/decision/propose/shared/DecisionProposeTypes.ts @@ -35,7 +35,7 @@ export interface DecisionProposeParams extends CommandParams { }>; /** Who should vote on this? */ - scope?: ProposalScope; // Default: 'all' + proposalScope?: ProposalScope; // Default: 'all' /** How urgent is this? Determines response window */ significanceLevel?: SignificanceLevel; // Default: 'medium' @@ -102,4 +102,3 @@ export const createCollaborationDecisionProposeResultFromParams = ( params: DecisionProposeParams, differences: Omit ): DecisionProposeResult => transformPayload(params, differences); - diff --git a/src/commands/grid/send/browser/GridSendBrowserCommand.ts b/src/commands/grid/send/browser/GridSendBrowserCommand.ts index 0ae36c7cf..ce849d39f 100644 --- a/src/commands/grid/send/browser/GridSendBrowserCommand.ts +++ b/src/commands/grid/send/browser/GridSendBrowserCommand.ts @@ -5,10 +5,14 @@ */ import { CommandBase, type ICommandDaemon } from '@daemons/command-daemon/shared/CommandBase'; -import type { JTAGContext } from '@system/core/types/JTAGTypes'; +import type { CommandScope, JTAGContext } from '@system/core/types/JTAGTypes'; import type { GridSendParams, GridSendResult } from '../shared/GridSendTypes'; export class GridSendBrowserCommand extends CommandBase { + protected static override get naturalScope(): CommandScope { + return { type: 'grid' }; + } + constructor(context: JTAGContext, subpath: string, commander: ICommandDaemon) { super('grid/send', context, subpath, commander); } diff --git a/src/commands/grid/send/server/GridSendServerCommand.ts b/src/commands/grid/send/server/GridSendServerCommand.ts index 1685f40f1..2a848bfea 100644 --- a/src/commands/grid/send/server/GridSendServerCommand.ts +++ b/src/commands/grid/send/server/GridSendServerCommand.ts @@ -7,13 +7,17 @@ */ import { CommandBase, type ICommandDaemon } from '@daemons/command-daemon/shared/CommandBase'; -import type { JTAGContext } from '@system/core/types/JTAGTypes'; +import type { CommandScope, JTAGContext } from '@system/core/types/JTAGTypes'; import type { GridSendParams, GridSendResult } from '../shared/GridSendTypes'; import { RustCoreIPCClient, getContinuumCoreSocketPath } from '../../../../workers/continuum-core/bindings/RustCoreIPC'; export class GridSendServerCommand extends CommandBase { private rustClient: RustCoreIPCClient; + protected static override get naturalScope(): CommandScope { + return { type: 'grid' }; + } + constructor(context: JTAGContext, subpath: string, commander: ICommandDaemon) { super('grid/send', context, subpath, commander); this.rustClient = new RustCoreIPCClient(getContinuumCoreSocketPath()); diff --git a/src/commands/skill/list/server/SkillListServerCommand.ts b/src/commands/skill/list/server/SkillListServerCommand.ts index 35240fb82..8d91f9cdb 100644 --- a/src/commands/skill/list/server/SkillListServerCommand.ts +++ b/src/commands/skill/list/server/SkillListServerCommand.ts @@ -27,8 +27,8 @@ export class SkillListServerCommand extends CommandBase createPayload(context, sessionId, { userId: SYSTEM_SCOPES.SYSTEM, status: data.status ?? '', - scope: data.scope ?? '', + skillScope: data.skillScope ?? '', createdById: data.createdById ?? '', limit: data.limit ?? 0, ...data diff --git a/src/commands/skill/propose/server/SkillProposeServerCommand.ts b/src/commands/skill/propose/server/SkillProposeServerCommand.ts index 0a87ba91d..1d0c3af0e 100644 --- a/src/commands/skill/propose/server/SkillProposeServerCommand.ts +++ b/src/commands/skill/propose/server/SkillProposeServerCommand.ts @@ -25,7 +25,7 @@ export class SkillProposeServerCommand extends CommandBase { const { name, description, implementation, personaId } = params; - const scope: SkillScope = (params.scope === 'team' ? 'team' : 'personal'); + const scope: SkillScope = (params.skillScope === 'team' ? 'team' : 'personal'); if (!name?.trim()) { throw new ValidationError('name', "Missing required parameter 'name'. Provide the command name (e.g., 'analysis/complexity')."); @@ -99,7 +99,7 @@ export class SkillProposeServerCommand extends CommandBase[]; // AI persona proposing this skill @@ -51,7 +51,7 @@ export const createSkillProposeParams = ( // Natural language description of the implementation logic implementation: string; // Who can use it: 'personal' (default) or 'team' (requires approval) - scope?: string; + skillScope?: string; // Usage examples array [{description, command, expectedResult?}] examples?: Record[]; // AI persona proposing this skill @@ -59,7 +59,7 @@ export const createSkillProposeParams = ( } ): SkillProposeParams => createPayload(context, sessionId, { userId: SYSTEM_SCOPES.SYSTEM, - scope: data.scope ?? '', + skillScope: data.skillScope ?? '', examples: data.examples ?? undefined, ...data }); diff --git a/src/daemons/command-daemon/shared/CommandBase.ts b/src/daemons/command-daemon/shared/CommandBase.ts index d565e10bf..ae3f6ab89 100644 --- a/src/daemons/command-daemon/shared/CommandBase.ts +++ b/src/daemons/command-daemon/shared/CommandBase.ts @@ -6,7 +6,7 @@ */ import { JTAGModule } from '../../../system/core/shared/JTAGModule'; -import type { JTAGContext, CommandParams, CommandResult } from '../../../system/core/types/JTAGTypes'; +import type { CommandScope, JTAGContext, CommandParams, CommandResult } from '../../../system/core/types/JTAGTypes'; import { JTAG_ENVIRONMENTS, JTAGMessageFactory } from '../../../system/core/types/JTAGTypes'; import { type UUID } from '../../../system/core/types/CrossPlatformUUID'; import { SYSTEM_SCOPES } from '../../../system/core/types/SystemScopes'; @@ -82,6 +82,17 @@ export abstract class CommandBase, + requestSessionId, + requestContext, + ); + + // Check if timeout is specified in command params + const timeout = scopedParams.timeout; + // Grid routing: check if this command should execute on a remote node. // Uses the same interceptor registered on Commands (server-side only). // Skip for grid/* commands to avoid infinite recursion. if (!commandName.startsWith('grid/')) { const interceptor = (Commands as unknown as { _gridInterceptor: { tryRouteRemote: (cmd: string, params: unknown) => Promise } | null })._gridInterceptor; if (interceptor) { - const remoteResult = await interceptor.tryRouteRemote(commandName, message.payload); + const remoteResult = await interceptor.tryRouteRemote(commandName, scopedParams); if (remoteResult !== null) { return createCommandSuccessResponse(remoteResult as CommandResult, requestContext, undefined, requestSessionId); } @@ -166,7 +172,7 @@ export abstract class CommandDaemon extends DaemonBase { // Execute command with session context for dual logging const executionPromise = globalSessionContext.withSession(requestSessionId, async () => { - return await command.execute({ userId: resolvedUserId, ...message.payload } as CommandParams); + return await command.execute(scopedParams); }); // Apply timeout if specified @@ -302,4 +308,3 @@ export abstract class CommandDaemon extends DaemonBase { }); } } - diff --git a/src/system/core/types/JTAGTypes.ts b/src/system/core/types/JTAGTypes.ts index 4177f1473..0a75ad808 100644 --- a/src/system/core/types/JTAGTypes.ts +++ b/src/system/core/types/JTAGTypes.ts @@ -184,6 +184,35 @@ export interface JTAGPayload { readonly sessionId: UUID; } +/** + * Command execution scope. + * + * Scope is the typed routing/audit boundary for commands. It lets callers and + * command infrastructure describe where work belongs without parsing command + * names, stdout, or ad-hoc params. Recipe rooms, project workspaces, persona + * turns, and grid nodes can all map to this shape. + */ +export type CommandScopeType = + | 'system' + | 'user' + | 'session' + | 'room' + | 'project' + | 'persona' + | 'grid' + | 'resource'; + +export interface CommandScope { + /** Scope class used by routers/projections for partitioning. */ + readonly type: CommandScopeType; + + /** Stable scope identifier, such as room id, repo slug, persona id, or node id. */ + readonly id?: string; + + /** Human-readable label for diagnostics and UI projections. */ + readonly label?: string; +} + /** * Functional factory for creating payloads - eliminates constructor complexity * Rust-like inheritance: creates payload from source + differences @@ -548,6 +577,13 @@ export interface CommandParams extends JTAGPayload { */ readonly userId: UUID; + /** + * Typed execution scope for routing, event projection, audit, and work + * alignment. CommandBase injects the command's natural scope when callers + * don't provide one; explicit caller scope wins. + */ + readonly scope?: CommandScope; + /** * Optional execution timeout in milliseconds. * If command execution exceeds this timeout, behavior is controlled by onTimeout. @@ -609,4 +645,4 @@ export type CommandMessage = JTAGMessag /** * Session and context propagation through explicit payload parameters * No global state - everything flows through payload chain - */ \ No newline at end of file + */ From 0325e786d04bb87e02a10e4e36d63194e4bb6fb2 Mon Sep 17 00:00:00 2001 From: Test Date: Mon, 25 May 2026 19:32:50 -0500 Subject: [PATCH 2/2] chore: lower linux eslint baseline --- src/eslint-baseline.linux.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eslint-baseline.linux.txt b/src/eslint-baseline.linux.txt index 38627a6f0..7e30bed39 100644 --- a/src/eslint-baseline.linux.txt +++ b/src/eslint-baseline.linux.txt @@ -1 +1 @@ -5432 +5431