diff --git a/.agents/skills/orchestrating-agent-relay/SKILL.md b/.agents/skills/orchestrating-agent-relay/SKILL.md index ffeae9eb5..cca358472 100644 --- a/.agents/skills/orchestrating-agent-relay/SKILL.md +++ b/.agents/skills/orchestrating-agent-relay/SKILL.md @@ -265,10 +265,7 @@ available via SDK subscriptions): #### When you coordinate across nodes rather than only the local broker, capabilities ```bash -# Enable fleet nodes for the workspace FIRST — it is off by default, and a -# node you bring up before enabling will not register/list -agent-relay fleet enable -agent-relay fleet config # inspect workspace fleet config +# Fleet nodes need no per-workspace enablement. agent-relay fleet status # local broker status + this node's provider attachment # Bring this node up, serving its node definition (advertises its capabilities). diff --git a/.agentworkforce/agents/relay-feature-guardian/manifest-contract.test.ts b/.agentworkforce/agents/relay-feature-guardian/manifest-contract.test.ts index 5156735ef..44b842eca 100644 --- a/.agentworkforce/agents/relay-feature-guardian/manifest-contract.test.ts +++ b/.agentworkforce/agents/relay-feature-guardian/manifest-contract.test.ts @@ -136,10 +136,6 @@ describe('feature manifest contract', () => { 'relay fleet nodes', 'relay fleet spawn', 'relay fleet release', - 'relay fleet config', - 'relay fleet enable', - 'relay fleet disable', - 'relay fleet inherit', 'relay fleet status', // Mounted product surfaces. Each product's real tree lives in its own SDK // spec, so the manifest documents the group and the surface owns the rest. diff --git a/.agentworkforce/features/manifest.yaml b/.agentworkforce/features/manifest.yaml index 46ab5eced..f95f9a189 100644 --- a/.agentworkforce/features/manifest.yaml +++ b/.agentworkforce/features/manifest.yaml @@ -731,34 +731,6 @@ categories: location: packages/cli/src/cli/commands/fleet.ts verify_tier: 5 - - id: fleet-config - name: Fleet Config - cli: relay fleet config - description: Show the workspace fleet-node configuration - location: packages/cli/src/cli/commands/fleet.ts - verify_tier: 5 - - - id: fleet-enable - name: Enable Fleet - cli: relay fleet enable - description: Enable fleet nodes for the workspace (modifies cloud state) - location: packages/cli/src/cli/commands/fleet.ts - verify_tier: 5 - - - id: fleet-disable - name: Disable Fleet - cli: relay fleet disable - description: Disable fleet nodes for the workspace (modifies cloud state) - location: packages/cli/src/cli/commands/fleet.ts - verify_tier: 5 - - - id: fleet-inherit - name: Inherit Fleet Config - cli: relay fleet inherit - description: Use deployment defaults for fleet node configuration (modifies cloud state) - location: packages/cli/src/cli/commands/fleet.ts - verify_tier: 5 - - id: fleet-status name: Fleet Node Status cli: relay fleet status diff --git a/.agentworkforce/features/verify/procedures.md b/.agentworkforce/features/verify/procedures.md index 931e55b74..31240a07a 100644 --- a/.agentworkforce/features/verify/procedures.md +++ b/.agentworkforce/features/verify/procedures.md @@ -329,19 +329,17 @@ The CLI persists the daemon PID and log path in its isolated local worker state, ## fleet-management -**Features:** `fleet-nodes`, `fleet-nodes-pretty`, `fleet-config`, `fleet-enable`, `fleet-disable`, `fleet-inherit`, `fleet-status`. +**Features:** `fleet-nodes`, `fleet-nodes-pretty`, `fleet-status`. -**Prerequisites:** disposable workspace; `fleet-status` additionally benefits from a local broker. +**Prerequisites:** workspace credentials; `fleet-status` additionally benefits from a local broker. ```bash relay fleet nodes | jq -e '.nodes' relay fleet nodes list --pretty | grep -Eiq 'NODE|No fleet nodes found' -BEFORE="$(relay fleet config)" -relay fleet enable; relay fleet config | jq -e 'type == "object"' -relay fleet disable; relay fleet inherit; relay fleet status | jq -e '.broker' +relay fleet status | jq -e '.broker' ``` -Snapshot and restore configuration or discard the workspace. For full two-node dispatch/enrollment coverage, run `npm run test:e2e` with `tests/e2e/fleet/README.md` prerequisites. +For full two-node dispatch/enrollment coverage, run `npm run test:e2e` with `tests/e2e/fleet/README.md` prerequisites. ## workspace-management diff --git a/.agentworkforce/trajectories/compacted/compact_7kbovjgptr28_2026-09-22.json b/.agentworkforce/trajectories/compacted/compact_7kbovjgptr28_2026-09-22.json new file mode 100644 index 000000000..aab925004 --- /dev/null +++ b/.agentworkforce/trajectories/compacted/compact_7kbovjgptr28_2026-09-22.json @@ -0,0 +1,48 @@ +{ + "id": "compact_7kbovjgptr28", + "version": 1, + "type": "compacted", + "compactedAt": "2026-09-22T18:39:39.513Z", + "sourceTrajectories": [ + "traj_xjrrec3f7hzx" + ], + "dateRange": { + "start": "2026-09-22T18:32:56.391Z", + "end": "2026-09-22T18:39:20.897Z" + }, + "summary": { + "totalDecisions": 2, + "totalEvents": 2, + "uniqueAgents": [ + "default" + ] + }, + "decisionGroups": [ + { + "category": "api", + "decisions": [ + { + "question": "Keep hidden exit-0 compatibility commands and retain their cleanroom coverage", + "chosen": "Keep hidden exit-0 compatibility commands and retain their cleanroom coverage", + "reasoning": "Reviewed plan Option A preserves shipped scripts without invoking the removed API.", + "fromTrajectory": "traj_xjrrec3f7hzx" + } + ] + }, + { + "category": "testing", + "decisions": [ + { + "question": "Regenerate the full CLI inventory and reconcile existing option drift", + "chosen": "Regenerate the full CLI inventory and reconcile existing option drift", + "reasoning": "The live inventory includes new release/spawn options and removes obsolete node-agent options; matrix coverage must match the generated inventory exactly.", + "fromTrajectory": "traj_xjrrec3f7hzx" + } + ] + } + ], + "keyLearnings": [], + "keyFindings": [], + "filesAffected": [], + "commits": [] +} \ No newline at end of file diff --git a/.agentworkforce/trajectories/compacted/compact_7kbovjgptr28_2026-09-22.md b/.agentworkforce/trajectories/compacted/compact_7kbovjgptr28_2026-09-22.md new file mode 100644 index 000000000..07800c74a --- /dev/null +++ b/.agentworkforce/trajectories/compacted/compact_7kbovjgptr28_2026-09-22.md @@ -0,0 +1,21 @@ +# Trajectory Compaction: Sep 22, 2026 - Sep 22, 2026 + +## Summary +- Sessions: 1 +- Decisions: 2 +- Events: 2 +- Agents: default +- Files: 0 +- Commits: 0 + +## Api +- Keep hidden exit-0 compatibility commands and retain their cleanroom coverage -> Keep hidden exit-0 compatibility commands and retain their cleanroom coverage (traj_xjrrec3f7hzx) + +## Testing +- Regenerate the full CLI inventory and reconcile existing option drift -> Regenerate the full CLI inventory and reconcile existing option drift (traj_xjrrec3f7hzx) + +## Key Learnings +- None + +## Key Findings +- None \ No newline at end of file diff --git a/.claude/skills/orchestrating-agent-relay/SKILL.md b/.claude/skills/orchestrating-agent-relay/SKILL.md index 8e548c204..98ec970b6 100644 --- a/.claude/skills/orchestrating-agent-relay/SKILL.md +++ b/.claude/skills/orchestrating-agent-relay/SKILL.md @@ -512,10 +512,7 @@ When you coordinate across nodes rather than only the local broker, capabilities and placement come into play: ```bash -# Enable fleet nodes for the workspace FIRST — it is off by default, and a -# node you bring up before enabling will not register/list -agent-relay fleet enable -agent-relay fleet config # inspect workspace fleet config +# Fleet nodes need no per-workspace enablement. agent-relay fleet status # local broker status + this node's provider attachment # Bring this node up, serving its node definition (advertises its capabilities). diff --git a/CHANGELOG.md b/CHANGELOG.md index 44ca92659..bae4b1393 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,18 +5,31 @@ All notable changes to Agent Relay will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased - Minor] +## [Unreleased - Major] ### Changed - Targeted `fleet spawn` now waits for harness readiness; the broker releases workers that miss its 90-second readiness window. Confirmed targeted spawns require `--confirm-timeout` of at least 95000ms. +### Removed + +- `@agent-relay/sdk`: removed `workspace.fleetNodes` and `RelayWorkspaceFleetNodesConfig`, whose underlying service API no longer exists. + ### Fixed +- `agent-relay fleet config|enable|disable|inherit` now exit successfully as hidden compatibility no-ops instead of failing on the removed workspace rollout API. - Targeted `fleet spawn` requests explicit readiness proof, preventing healthy launches from being rejected for missing proof; unconfirmed launches report `ready:false` while obsolete handlers remain rejected. - `@agent-relay/sdk` `placement.spawn` only asks a node to verify readiness when it will wait for the answer, and confirms against the contract it requested, so `confirm` omitted no longer arms a 90-second readiness kill switch and `verifyReady: false` no longer fails a healthy launch. - Broker `manual_flush` recovery now replays a missing cumulative-ACK predecessor without duplicating an already-completed PTY injection, restores it ahead of parked successors, and reports the head/ACK/received sequence gap plus the reconciliation action in `message flush` and `message auto` results. +### Breaking Changes + +- SDK consumers must remove references to `workspace.fleetNodes` and `RelayWorkspaceFleetNodesConfig`. + +### Migration Guidance + +- Fleet nodes need no per-workspace enablement. Remove `fleet enable` from provisioning scripts; `fleet disable` no longer disables nodes. + ## [12.4.1] - 2026-09-22 ### Added diff --git a/flows/verify/features.spec.ts b/flows/verify/features.spec.ts index 4a7914a9d..9447639bb 100644 --- a/flows/verify/features.spec.ts +++ b/flows/verify/features.spec.ts @@ -1010,10 +1010,10 @@ skip_check "cloud enroll" "requires interactive browser auth" gated_check cloud "fleet nodes" "relay fleet nodes" "." gated_check cloud "fleet nodes list --pretty" \ "relay fleet nodes list --pretty | grep -E '^(NODE|No fleet nodes found\.)'" "." -gated_check cloud "fleet config" "relay fleet config" "." -gated_check cloud "fleet inherit" "relay fleet inherit --help" "Usage" -skip_check "fleet enable" "mutates workspace cloud state" -skip_check "fleet disable" "mutates workspace cloud state" +run_check "fleet config" "relay fleet config" "deprecated" +run_check "fleet inherit" "relay fleet inherit 2>&1" "no-op" +run_check "fleet enable" "relay fleet enable 2>&1" "no-op" +run_check "fleet disable" "relay fleet disable 2>&1" "no-op" # fleet spawn/release were undocumented in the manifest until this change and # are still unverified: spawning burns provider credits on a remote node. diff --git a/flows/verify/fleet-daytona.spec.ts b/flows/verify/fleet-daytona.spec.ts index 52d128eeb..4999562b4 100644 --- a/flows/verify/fleet-daytona.spec.ts +++ b/flows/verify/fleet-daytona.spec.ts @@ -30,8 +30,8 @@ * The flag is still accepted on Node 24 and 26, so one invocation works * everywhere. * - * Workspace-wide enable/disable/inherit probes are safety-skipped unless the - * active workspace is disposable and VERIFY_FLEET_DISPOSABLE_WORKSPACE=1. + * Legacy fleet config/enable/disable/inherit probes verify deprecated no-ops. + * VERIFY_FLEET_DISPOSABLE_WORKSPACE=1 still requires a clean baseline. */ import { randomBytes } from 'node:crypto'; diff --git a/packages/cli/src/cli/bootstrap.test.ts b/packages/cli/src/cli/bootstrap.test.ts index e22a855d8..f07483fdb 100644 --- a/packages/cli/src/cli/bootstrap.test.ts +++ b/packages/cli/src/cli/bootstrap.test.ts @@ -41,12 +41,8 @@ const expectedLeafCommands = [ 'flows', // `session` (singular) is the hidden alias of `sessions`, so it is absent here. 'sessions', - // fleet (serve is a hidden error stub, filtered out below) + // fleet (serve is a hidden error stub; config/enable/disable/inherit are hidden no-ops) 'fleet agent list', - 'fleet config', - 'fleet disable', - 'fleet enable', - 'fleet inherit', 'fleet nodes list', 'fleet release', 'fleet spawn', diff --git a/packages/cli/src/cli/commands/fleet.test.ts b/packages/cli/src/cli/commands/fleet.test.ts index 311528d09..7236424cb 100644 --- a/packages/cli/src/cli/commands/fleet.test.ts +++ b/packages/cli/src/cli/commands/fleet.test.ts @@ -109,58 +109,64 @@ describe('spawn lifecycle receipts', () => { }); describe('fleet command support', () => { - it.each([ - ['config', 'get', undefined], - ['enable', 'set', true], - ['disable', 'set', false], - ['inherit', 'inherit', undefined], - ] as const)('fleet %s delegates to workspace fleet node config API', async (command, method, value) => { - const fleetNodes = { - get: vi.fn(async () => ({ enabled: false, defaultEnabled: false, override: null })), - set: vi.fn(async (enabled: boolean) => ({ enabled, defaultEnabled: false, override: enabled })), - inherit: vi.fn(async () => ({ enabled: false, defaultEnabled: false, override: null })), - }; - const createWorkspaceRelay = vi.fn(() => ({ workspace: { fleetNodes } })); - const logs: string[] = []; - const program = new Command(); - program.exitOverride(); - registerFleetCommands(program, { - resolveSandboxRepository: () => undefined, - sdk: { - createAgentRelay: vi.fn() as never, - createWorkspaceRelay: createWorkspaceRelay as never, - createWorkspace: vi.fn() as never, - log: (message: unknown) => logs.push(String(message)), - error: vi.fn(), - exit: vi.fn(() => { - throw new Error('__exit__'); - }) as never, - }, - log: () => undefined, - warn: () => undefined, - error: () => undefined, - }); - - await program.parseAsync( - ['fleet', command, '--workspace-key', 'rk_live_test', '--base-url', 'https://relay.example'], - { from: 'user' } - ); - - expect(createWorkspaceRelay).toHaveBeenCalledWith({ - workspaceKey: 'rk_live_test', - token: undefined, - baseUrl: 'https://relay.example', - }); - if (method === 'set') { - expect(fleetNodes.set).toHaveBeenCalledWith(value); - } else { - expect(fleetNodes[method]).toHaveBeenCalledTimes(1); + it.each(['config', 'enable', 'disable', 'inherit'])( + 'fleet %s is a hidden no-op without workspace access', + async (command) => { + const createWorkspaceRelay = vi.fn(() => { + throw new Error('workspace access forbidden'); + }); + const createFleetWorkspaceClient = vi.fn(() => { + throw new Error('fleet access forbidden'); + }); + const logs: string[] = []; + const errors: string[] = []; + const exit = vi.fn(); + const program = new Command(); + program.exitOverride(); + registerFleetCommands(program, { + createFleetWorkspaceClient: createFleetWorkspaceClient as never, + sdk: { + createAgentRelay: vi.fn() as never, + createWorkspaceRelay: createWorkspaceRelay as never, + createWorkspace: vi.fn() as never, + log: (message: unknown) => logs.push(String(message)), + error: vi.fn(), + exit: exit as never, + }, + log: () => undefined, + warn: () => undefined, + error: (message: string) => errors.push(message), + exit: exit as never, + }); + // Both credential-free fresh workspaces and older scripts with SDK options work. + for (const options of [[], ['--workspace-key', 'rk_live_test', '--base-url', 'http://127.0.0.1:1']]) { + await program.parseAsync(['fleet', command, ...options], { from: 'user' }); + } + expect(createWorkspaceRelay).not.toHaveBeenCalled(); + expect(createFleetWorkspaceClient).not.toHaveBeenCalled(); + expect(exit).not.toHaveBeenCalled(); + expect(errors.join('\n')).toContain( + 'Fleet nodes need no per-workspace enablement; this command is a no-op.' + ); + expect(errors.join('\n')).not.toContain('requires @relaycast/sdk'); + const group = program.commands.find((entry) => entry.name() === 'fleet')!; + expect( + (group.commands.find((entry) => entry.name() === command) as Command & { _hidden: boolean })._hidden + ).toBe(true); + expect(group.helpInformation()).not.toMatch(new RegExp(`\\b${command}\\b`)); + if (command === 'config') { + expect(logs).toHaveLength(2); + for (const log of logs) + expect(JSON.parse(log)).toEqual({ + command: 'fleet config', + status: 'deprecated', + effect: 'none', + message: 'Fleet nodes need no per-workspace enablement; this command is a no-op.', + }); + } else expect(logs).toEqual([]); + if (command === 'disable') expect(errors.join('\n')).toContain('Fleet nodes have not been disabled.'); } - expect(JSON.parse(logs[0]!)).toMatchObject({ - enabled: method === 'set' ? value : false, - defaultEnabled: false, - }); - }); + ); it('fleet nodes accepts --wk as an alias for --workspace-key', async () => { const nodes = { list: vi.fn(async () => []) }; diff --git a/packages/cli/src/cli/commands/fleet.ts b/packages/cli/src/cli/commands/fleet.ts index 1f607bb06..ba8a884e1 100644 --- a/packages/cli/src/cli/commands/fleet.ts +++ b/packages/cli/src/cli/commands/fleet.ts @@ -59,6 +59,9 @@ import { type SdkCommandDeps, } from '../lib/sdk-command.js'; +const FLEET_NODES_ALWAYS_ON_MESSAGE = + 'Fleet nodes need no per-workspace enablement; this command is a no-op.'; + const SERVE_REPLACEMENT_MESSAGE = "'fleet serve' has been replaced. Run 'relay node up' (with an optional --config ); " + "for Cloud-managed nodes run 'relay cloud enroll --token ' first."; @@ -1195,41 +1198,24 @@ export function registerFleetCommands( }); }); - addSdkOptions(group.command('config').description('Show workspace fleet node configuration')).action( - async (options: Record) => { - await runSdk(deps.sdk, async () => { - const relay = deps.sdk.createWorkspaceRelay(sdkOptionsFromOpts(options)); - printJson(deps.sdk, await relay.workspace.fleetNodes.get()); - }); - } - ); - - addSdkOptions(group.command('enable').description('Enable fleet nodes for the workspace')).action( - async (options: Record) => { - await runSdk(deps.sdk, async () => { - const relay = deps.sdk.createWorkspaceRelay(sdkOptionsFromOpts(options)); - printJson(deps.sdk, await relay.workspace.fleetNodes.set(true)); - }); - } - ); - - addSdkOptions(group.command('disable').description('Disable fleet nodes for the workspace')).action( - async (options: Record) => { - await runSdk(deps.sdk, async () => { - const relay = deps.sdk.createWorkspaceRelay(sdkOptionsFromOpts(options)); - printJson(deps.sdk, await relay.workspace.fleetNodes.set(false)); - }); - } - ); - - addSdkOptions( - group.command('inherit').description('Use the deployment default for workspace fleet nodes') - ).action(async (options: Record) => { - await runSdk(deps.sdk, async () => { - const relay = deps.sdk.createWorkspaceRelay(sdkOptionsFromOpts(options)); - printJson(deps.sdk, await relay.workspace.fleetNodes.inherit()); + for (const command of ['config', 'enable', 'disable', 'inherit']) { + addSdkOptions( + group + .command(command, { hidden: true }) + .description('Deprecated: fleet nodes need no per-workspace enablement') + ).action(() => { + deps.error(FLEET_NODES_ALWAYS_ON_MESSAGE); + if (command === 'disable') deps.error('Fleet nodes have not been disabled.'); + if (command === 'config') { + printJson(deps.sdk, { + command: 'fleet config', + status: 'deprecated', + effect: 'none', + message: FLEET_NODES_ALWAYS_ON_MESSAGE, + }); + } }); - }); + } addSdkOptions( group.command('status').description('Show local broker status and this node’s provider attachment') diff --git a/packages/sdk/src/__tests__/facade.test.ts b/packages/sdk/src/__tests__/facade.test.ts index d76033fd5..f39c67eda 100644 --- a/packages/sdk/src/__tests__/facade.test.ts +++ b/packages/sdk/src/__tests__/facade.test.ts @@ -34,11 +34,6 @@ function createMessagingMock() { }; const workspace = { info: vi.fn(async () => ({ id: 'ws_1', name: 'Ops' })), - fleetNodes: { - get: vi.fn(async () => ({ enabled: false, defaultEnabled: false, override: null })), - set: vi.fn(async (enabled: boolean) => ({ enabled, defaultEnabled: false, override: enabled })), - inherit: vi.fn(async () => ({ enabled: false, defaultEnabled: false, override: null })), - }, }; const messaging = { messages, agents, workspace, events: {} } as unknown as RelayMessaging; return { messaging, messages, agents, workspace }; @@ -80,31 +75,6 @@ describe('AgentRelay facade (Phase A)', () => { expect(client.token).toBe('tok-self'); }); - it('workspace.fleetNodes delegates fleet node config calls', async () => { - const { messaging, workspace } = createMessagingMock(); - const relay = new AgentRelay({ messaging }); - - await expect(relay.workspace.fleetNodes.get()).resolves.toEqual({ - enabled: false, - defaultEnabled: false, - override: null, - }); - await expect(relay.workspace.fleetNodes.set(true)).resolves.toEqual({ - enabled: true, - defaultEnabled: false, - override: true, - }); - await expect(relay.workspace.fleetNodes.inherit()).resolves.toEqual({ - enabled: false, - defaultEnabled: false, - override: null, - }); - - expect(workspace.fleetNodes.get).toHaveBeenCalledTimes(1); - expect(workspace.fleetNodes.set).toHaveBeenCalledWith(true); - expect(workspace.fleetNodes.inherit).toHaveBeenCalledTimes(1); - }); - it('sendMessage routes #channel to messages.send and a bare name to direct', async () => { const { messaging, messages } = createMessagingMock(); const relay = new AgentRelay({ messaging, createAgentMessaging: () => messaging }); diff --git a/packages/sdk/src/__tests__/messaging.test.ts b/packages/sdk/src/__tests__/messaging.test.ts index 1e8f267e1..8175410ac 100644 --- a/packages/sdk/src/__tests__/messaging.test.ts +++ b/packages/sdk/src/__tests__/messaging.test.ts @@ -158,11 +158,6 @@ function createWorkspace() { }, workspace: { info: vi.fn(async () => ({ id: 'ws_1', name: 'Ops' })), - fleetNodes: { - get: vi.fn(async () => ({ enabled: true, default_enabled: false, override: true })), - set: vi.fn(async (enabled: boolean) => ({ enabled, default_enabled: false, override: enabled })), - inherit: vi.fn(async () => ({ enabled: false, default_enabled: false, override: null })), - }, }, }; } @@ -462,31 +457,6 @@ describe('RelaycastMessagingClient', () => { }); }); - it('delegates workspace fleet node config calls to Relaycast', async () => { - const workspace = createWorkspace(); - const client = new RelaycastMessagingClient({ relaycast: workspace }); - - await expect(client.workspace.fleetNodes.get()).resolves.toEqual({ - enabled: true, - defaultEnabled: false, - override: true, - }); - await expect(client.workspace.fleetNodes.set(false)).resolves.toEqual({ - enabled: false, - defaultEnabled: false, - override: false, - }); - await expect(client.workspace.fleetNodes.inherit()).resolves.toEqual({ - enabled: false, - defaultEnabled: false, - override: null, - }); - - expect(workspace.workspace.fleetNodes.get).toHaveBeenCalledTimes(1); - expect(workspace.workspace.fleetNodes.set).toHaveBeenCalledWith(false); - expect(workspace.workspace.fleetNodes.inherit).toHaveBeenCalledTimes(1); - }); - it('normalizes fleet node roster fields and passes node query options through', async () => { const workspace = createWorkspace(); const client = new RelaycastMessagingClient({ relaycast: workspace }); diff --git a/packages/sdk/src/__tests__/observer-source.test.ts b/packages/sdk/src/__tests__/observer-source.test.ts index 6570d0a58..1543840a7 100644 --- a/packages/sdk/src/__tests__/observer-source.test.ts +++ b/packages/sdk/src/__tests__/observer-source.test.ts @@ -490,7 +490,7 @@ describe('AgentRelay observer mode', () => { // A partial messaging fake: observer mode never uses the workspace // client's event stream and register/reconnect throw at the facade. const messaging = { - workspace: { info: vi.fn(async () => ({})), fleetNodes: {} }, + workspace: { info: vi.fn(async () => ({})) }, agents: {}, events: undefined, } as unknown as RelayMessaging; diff --git a/packages/sdk/src/facade.ts b/packages/sdk/src/facade.ts index 8e45433ad..a711c5b37 100644 --- a/packages/sdk/src/facade.ts +++ b/packages/sdk/src/facade.ts @@ -13,7 +13,6 @@ import type { RelayAgentReleaseResult, RelaySendChannelMessageInput, RelayWorkspaceInfo, - RelayWorkspaceFleetNodesConfig, } from './messaging/index.js'; import { actionSchemaToJsonSchema, @@ -204,11 +203,6 @@ export interface RelayWorkspace { release(input: RelayReleaseAgentInput): Promise; reconnect(input: { apiToken: string }): Promise; info(): Promise; - fleetNodes: { - get(): Promise; - set(enabled: boolean): Promise; - inherit(): Promise; - }; } export interface NotifyOptions { @@ -371,7 +365,6 @@ export function createWorkspaceFacade(messaging: RelayMessaging, deps?: Workspac return { info: () => messaging.workspace.info(), - fleetNodes: messaging.workspace.fleetNodes, register: register as RelayWorkspace['register'], release: async (input) => { // async so an unavailable-on-this-client error is always a rejected diff --git a/packages/sdk/src/messaging/relaycast-client.ts b/packages/sdk/src/messaging/relaycast-client.ts index a7ce82019..a6d440074 100644 --- a/packages/sdk/src/messaging/relaycast-client.ts +++ b/packages/sdk/src/messaging/relaycast-client.ts @@ -84,11 +84,6 @@ export type RelaycastWorkspaceLike = { }; workspace?: { info(): Promise; - fleetNodes?: { - get(): Promise; - set(enabled: boolean): Promise; - inherit(): Promise; - }; }; as?: (agentToken: string, options?: AgentClientOptions) => RelaycastAgentLike; // Workspace-scoped realtime stream (relaycast 2.5+): lets a workspace-key diff --git a/packages/sdk/src/messaging/relaycast-translate.ts b/packages/sdk/src/messaging/relaycast-translate.ts index 1d0147824..ecab4a16d 100644 --- a/packages/sdk/src/messaging/relaycast-translate.ts +++ b/packages/sdk/src/messaging/relaycast-translate.ts @@ -22,7 +22,6 @@ import type { RelayTrigger, RelayTriggerInput, RelayWebhookSubscription, - RelayWorkspaceFleetNodesConfig, } from './types.js'; // --- primitive record readers ------------------------------------------------- @@ -201,15 +200,6 @@ export function toRelayTrigger(raw: unknown): RelayTrigger { }; } -export function toRelayWorkspaceFleetNodesConfig(raw: unknown): RelayWorkspaceFleetNodesConfig { - const record = asRecord(raw); - return { - enabled: readBoolean(record, 'enabled') ?? false, - defaultEnabled: readBoolean(record, 'defaultEnabled', 'default_enabled') ?? false, - override: readBoolean(record, 'override') ?? null, - }; -} - /** Normalize a relaycast invoke ack (camelized) into the relay `RelayActionInvocationAck`. */ export function normalizeActionInvocationAck(raw: unknown): RelayActionInvocationAck { const record = asRecord(raw); diff --git a/packages/sdk/src/messaging/relaycast.ts b/packages/sdk/src/messaging/relaycast.ts index 4b8dfe20d..74c4f115e 100644 --- a/packages/sdk/src/messaging/relaycast.ts +++ b/packages/sdk/src/messaging/relaycast.ts @@ -29,7 +29,6 @@ import { toRelayCapability, toRelayNode, toRelayTrigger, - toRelayWorkspaceFleetNodesConfig, toTriggerRequest, } from './relaycast-translate.js'; import { @@ -79,7 +78,6 @@ import type { RelayRegisterCapabilityInput, RelayWebhook, RelayWorkspaceInfo, - RelayWorkspaceFleetNodesConfig, InboxAckInput, InboxDeferInput, InboxFailInput, @@ -1131,17 +1129,6 @@ export class RelaycastMessagingClient implements RelayMessagingClient { } return (await this.relaycast.workspace.info()) as RelayWorkspaceInfo; }, - fleetNodes: { - get: async (): Promise => { - return toRelayWorkspaceFleetNodesConfig(await this.requireWorkspaceFleetNodes().get()); - }, - set: async (enabled: boolean): Promise => { - return toRelayWorkspaceFleetNodesConfig(await this.requireWorkspaceFleetNodes().set(enabled)); - }, - inherit: async (): Promise => { - return toRelayWorkspaceFleetNodesConfig(await this.requireWorkspaceFleetNodes().inherit()); - }, - }, }; private resolvePlacementNode(node: string | 'self' | undefined, selfNodeName?: string): string | undefined { @@ -1355,17 +1342,6 @@ export class RelaycastMessagingClient implements RelayMessagingClient { return this.relaycast.triggers; } - private requireWorkspaceFleetNodes(): NonNullable< - NonNullable['fleetNodes'] - > { - if (!this.relaycast.workspace?.fleetNodes) { - throw new Error( - 'RelaycastMessagingClient.workspace.fleetNodes requires @relaycast/sdk with the workspace fleet nodes API.' - ); - } - return this.relaycast.workspace.fleetNodes; - } - private requireAgentActions(operation: string): NonNullable { const actions = this.agentClient?.actions; if (!actions) { diff --git a/packages/sdk/src/messaging/types.ts b/packages/sdk/src/messaging/types.ts index ac40f60eb..683c36296 100644 --- a/packages/sdk/src/messaging/types.ts +++ b/packages/sdk/src/messaging/types.ts @@ -717,12 +717,6 @@ export interface RelayWorkspaceInfo { [key: string]: unknown; } -export interface RelayWorkspaceFleetNodesConfig { - enabled: boolean; - defaultEnabled: boolean; - override: boolean | null; -} - /** * Relay inbox states built on the canonical delivery-status lifecycle: * `queued`/`delivered`/`failed` surface directly, the terminal `acked` and @@ -1075,11 +1069,6 @@ export interface RelayMessagingClient { }; readonly workspace: { info(): Promise; - fleetNodes: { - get(): Promise; - set(enabled: boolean): Promise; - inherit(): Promise; - }; }; } diff --git a/scripts/verify-features/fleet-daytona.mjs b/scripts/verify-features/fleet-daytona.mjs index e0c5c6103..65b8c9a17 100644 --- a/scripts/verify-features/fleet-daytona.mjs +++ b/scripts/verify-features/fleet-daytona.mjs @@ -3372,7 +3372,7 @@ export function validateFleetEvidence(evidence, matrix) { } } const environment = assertObject(evidence.environment, 'evidence.environment'); - for (const key of ['policyMutationRequested', 'policyMutationAuthorized', 'policyMutationPerformed']) { + for (const key of ['policyMutationRequested']) { if (typeof environment[key] !== 'boolean') throw new Error(`evidence environment.${key} is invalid`); } if (environment.controlPlaneClean !== true) { @@ -3737,22 +3737,6 @@ export function validateFleetEvidence(evidence, matrix) { `cleanup cannot pass after release failure for ${failedReleaseAttempt.target ?? 'unknown target'}` ); } - const mutationOperations = evidence.operations.filter(({ id }) => - ['fleet-enable', 'fleet-disable', 'fleet-inherit'].includes(id) - ); - if (mutationOperations.some(({ status }) => status !== 'safety-skipped')) { - if ( - environment.policyMutationAuthorized !== true || - environment.policyMutationPerformed !== true || - !environment.expectedWorkspaceId || - provenance.resolvedWorkspaceId !== environment.expectedWorkspaceId - ) { - throw new Error('workspace policy mutation was not bound to the explicitly expected workspace'); - } - if (environment.policyRestoration?.status !== 'pass') { - throw new Error('workspace policy mutation was not restored to its exact initial override'); - } - } validateCriticalLifecycleEvidence(evidence.criticalLifecycle, matrix, boardNodes, evidence.nonce); const derived = deriveFleetVerdict(evidence.operations, evidence.cleanup, evidence.criticalLifecycle); if (evidence.verdict !== derived) throw new Error(`evidence verdict must be ${derived}`); @@ -3969,8 +3953,6 @@ class FleetBoard { policyMutationRequested: process.env.VERIFY_FLEET_DISPOSABLE_WORKSPACE === '1', expectedWorkspaceId: process.env.VERIFY_FLEET_EXPECTED_WORKSPACE_ID?.trim() || null, expectedRelayWorkspaceId: process.env.VERIFY_FLEET_EXPECTED_RELAY_WORKSPACE_ID?.trim() || null, - policyMutationAuthorized: false, - policyMutationPerformed: false, controlPlaneClean: false, releaseQualificationRequested: process.env.VERIFY_FLEET_RELEASE_QUALIFICATION === '1', expectedSnapshotId: process.env.VERIFY_FLEET_SNAPSHOT_ID?.trim() || null, @@ -6829,106 +6811,38 @@ class FleetBoard { } } - async fleetPolicyAndStatus() { - let rawConfig; - const configOperation = await this.record('fleet-config', async () => { - rawConfig = await execute(this.cliArgv('fleet', 'config'), { - timeoutMs: 45_000, - maxCaptureBytes: 1024 * 1024, - }); - const payload = tryParseJson(rawConfig._rawStdout); - const schemaValid = - payload && - Object.prototype.hasOwnProperty.call(payload, 'override') && - [true, false, null].includes(payload.override) && - typeof payload.effective === 'boolean'; - return { - ...stripPrivateExecution(rawConfig), - exitCode: rawConfig.exitCode === 0 && schemaValid ? 0 : 1, - summary: `schemaValid=${Boolean(schemaValid)} override=${String(payload?.override)} effective=${String(payload?.effective)}`, - }; - }); - const configPayload = rawConfig ? tryParseJson(rawConfig._rawStdout) : undefined; - const hasRestorableOverride = - configPayload && - Object.prototype.hasOwnProperty.call(configPayload, 'override') && - [true, false, null].includes(configPayload.override); - const initialOverride = hasRestorableOverride ? configPayload.override : undefined; - const expectedWorkspaceId = this.evidence.environment.expectedWorkspaceId; - const actualWorkspaceId = this.evidence.provenance?.resolvedWorkspaceId; - const requested = this.evidence.environment.policyMutationRequested; - const authorized = - requested && - typeof expectedWorkspaceId === 'string' && - expectedWorkspaceId.length > 0 && - actualWorkspaceId === expectedWorkspaceId; - this.evidence.environment.policyMutationAuthorized = authorized; - this.evidence.environment.policyInitialOverride = hasRestorableOverride ? initialOverride : 'unknown'; - await this.checkpoint(); - - if (!authorized || configOperation.status !== 'pass' || !hasRestorableOverride) { - const safetyReason = !requested - ? 'Set both VERIFY_FLEET_DISPOSABLE_WORKSPACE=1 and VERIFY_FLEET_EXPECTED_WORKSPACE_ID to authorize workspace policy mutation.' - : !expectedWorkspaceId - ? 'VERIFY_FLEET_EXPECTED_WORKSPACE_ID is required for workspace policy mutation.' - : actualWorkspaceId !== expectedWorkspaceId - ? `Active workspace ${actualWorkspaceId ?? 'unknown'} does not match the explicitly expected workspace.` - : 'fleet config did not return a restorable override, so mutation was not attempted.'; - for (const id of ['fleet-enable', 'fleet-disable', 'fleet-inherit']) { - await this.derived(id, { safetyReason }); - } - } else { - this.evidence.environment.policyMutationPerformed = true; - const runPolicy = async (id, action, expectedOverride) => - this.record(id, async () => { - const mutation = await execute(this.cliArgv('fleet', action), { timeoutMs: 45_000 }); - const readback = await execute(this.cliArgv('fleet', 'config'), { - timeoutMs: 45_000, - maxCaptureBytes: 1024 * 1024, - }); - const payload = tryParseJson(readback._rawStdout); - const readbackMatches = - readback.exitCode === 0 && - payload && - Object.prototype.hasOwnProperty.call(payload, 'override') && - payload.override === expectedOverride; - return { - ...stripPrivateExecution(mutation), - exitCode: mutation.exitCode === 0 && readbackMatches ? 0 : 1, - summary: `action=${action} expectedOverride=${String(expectedOverride)} observedOverride=${String(payload?.override)} readbackExit=${readback.exitCode}`, - }; - }); - try { - await runPolicy('fleet-enable', 'enable', true); - await runPolicy('fleet-disable', 'disable', false); - await runPolicy('fleet-inherit', 'inherit', null); - } finally { - const restoreArg = - initialOverride === true ? 'enable' : initialOverride === false ? 'disable' : 'inherit'; - const restore = await execute(this.cliArgv('fleet', restoreArg), { timeoutMs: 45_000 }); - const verify = await execute(this.cliArgv('fleet', 'config'), { - timeoutMs: 45_000, - maxCaptureBytes: 1024 * 1024, - }); - const restoredPayload = tryParseJson(verify._rawStdout); - const restoredExactly = - verify.exitCode === 0 && - restoredPayload && - Object.prototype.hasOwnProperty.call(restoredPayload, 'override') && - restoredPayload.override === initialOverride; - this.evidence.environment.policyRestoration = { - targetOverride: initialOverride, - command: restoreArg, - exitCode: restore.exitCode, - timedOut: restore.timedOut === true, - verificationExitCode: verify.exitCode, - restoredExactly: restoredExactly === true, - status: - restore.exitCode === 0 && restore.timedOut !== true && restoredExactly === true ? 'pass' : 'fail', - stderr: redactFleetEvidence(`${restore.stderr ?? ''}\n${verify.stderr ?? ''}`), + async fleetDeprecatedNoopsAndStatus() { + for (const command of ['config', 'enable', 'disable', 'inherit']) { + await this.record(`fleet-${command}`, async () => { + // An unreachable endpoint prevents these compatibility probes from mutating + // the active workspace and proves they need no working service connection. + const result = await execute( + this.cliArgv( + 'fleet', + command, + '--base-url', + 'http://127.0.0.1:1', + '--workspace-key', + 'deprecated-noop' + ), + { timeoutMs: 45_000, maxCaptureBytes: 1024 * 1024 } + ); + const payload = tryParseJson(result._rawStdout); + const notice = result.stderr.includes( + 'Fleet nodes need no per-workspace enablement; this command is a no-op.' + ); + const outputValid = + command === 'config' + ? payload?.command === 'fleet config' && + payload.status === 'deprecated' && + payload.effect === 'none' + : result._rawStdout.trim() === ''; + return { + ...stripPrivateExecution(result), + exitCode: result.exitCode === 0 && notice && outputValid ? 0 : 1, + summary: `deprecated no-op: notice=${notice} outputValid=${outputValid}; unreachable endpoint, no workspace mutation`, }; - await this.checkpoint(); - } + }); } const statusNode = this.availableBoardNodes()[0]; if (!statusNode?.id) { @@ -7515,7 +7429,7 @@ class FleetBoard { await this.targetedFleetSpawns(); await this.fleetProviderMatrix(); await this.mountedSandboxCases(); - await this.fleetPolicyAndStatus(); + await this.fleetDeprecatedNoopsAndStatus(); await this.nodeSpawnMatrix(); await this.criticalLifecycleRepeatability(); await this.nodeWorkflows(); diff --git a/tests/fixtures/verify-fleet-daytona.test.ts b/tests/fixtures/verify-fleet-daytona.test.ts index fc1d2e379..015572b5e 100644 --- a/tests/fixtures/verify-fleet-daytona.test.ts +++ b/tests/fixtures/verify-fleet-daytona.test.ts @@ -543,11 +543,8 @@ function completeEvidence(matrix: { }, environment: { policyMutationRequested: true, - policyMutationAuthorized: true, - policyMutationPerformed: true, expectedWorkspaceId: 'workspace_fixture', controlPlaneClean: true, - policyRestoration: { status: 'pass' }, }, baseline: { agentCount: 0, @@ -2067,6 +2064,12 @@ describe('complete Daytona Fleet board', () => { expect(actual.commands).toHaveLength(36); expect(actual.commands.filter(({ leaf }: { leaf: boolean }) => leaf)).toHaveLength(30); expect(inventorySha256(actual)).toMatch(/^[a-f0-9]{64}$/); + for (const verb of ['config', 'enable', 'disable', 'inherit']) { + expect(actual.commands.find(({ path }: { path: string }) => path === `fleet ${verb}`)).toMatchObject({ + hidden: true, + leaf: true, + }); + } expect(actual.commands.find(({ path }: { path: string }) => path === 'fleet serve')).toMatchObject({ hidden: true, leaf: true, @@ -2096,6 +2099,26 @@ describe('complete Daytona Fleet board', () => { expect(() => compareFleetCliInventory(actual, changedOption)).toThrow('inventory changed'); }, 30_000); + it('runs deprecated fleet probes successfully without a workspace service', async () => { + const records = new Map(); + await FleetBoard.prototype.fleetDeprecatedNoopsAndStatus.call({ + cliArgv: (...args: string[]) => [ + process.execPath, + path.resolve('packages/cli/dist/cli/index.js'), + ...args, + ], + record: async (id: string, run: () => Promise<{ exitCode: number; summary: string }>) => { + records.set(id, await run()); + }, + availableBoardNodes: () => [], + derived: async () => undefined, + }); + for (const verb of ['config', 'enable', 'disable', 'inherit']) { + expect(records.get(`fleet-${verb}`)).toMatchObject({ exitCode: 0 }); + expect(records.get(`fleet-${verb}`)?.summary).toContain('notice=true outputValid=true'); + } + }, 30_000); + it('rejects duplicate operations and an incomplete provider board', async () => { const matrix = await loadFleetMatrix('tests/relayflows/cleanroom/fleet-daytona.matrix.json'); const duplicate = structuredClone(matrix); diff --git a/tests/relayflows/cleanroom/README.md b/tests/relayflows/cleanroom/README.md index 799021a05..df7779b2c 100644 --- a/tests/relayflows/cleanroom/README.md +++ b/tests/relayflows/cleanroom/README.md @@ -157,11 +157,11 @@ node scripts/verify-features/fleet-daytona.mjs cleanup \ --nonce ``` -`fleet enable`, `fleet disable`, and `fleet inherit` affect a whole workspace. -They are evidence-visible safety skips unless the active workspace is disposable, -`VERIFY_FLEET_DISPOSABLE_WORKSPACE=1` is set, and -`VERIFY_FLEET_EXPECTED_WORKSPACE_ID` exactly matches the resolved Cloud workspace. -The runner captures the initial override and restores it in `finally`. `node down +`fleet config`, `fleet enable`, `fleet disable`, and `fleet inherit` are hidden +compatibility no-ops. The runner checks successful exits and deprecation notices +against an unreachable endpoint, with a JSON deprecation object for `fleet config`. +They change no workspace state and need no restoration. Disposable-workspace and +workspace-identity checks still apply to the campaign baseline. `node down --all` runs only inside an exact owned Daytona sandbox, never on the operator host. Each attempt is sealed under diff --git a/tests/relayflows/cleanroom/fleet-cli-inventory.json b/tests/relayflows/cleanroom/fleet-cli-inventory.json index 9f965038a..498bdbd84 100644 --- a/tests/relayflows/cleanroom/fleet-cli-inventory.json +++ b/tests/relayflows/cleanroom/fleet-cli-inventory.json @@ -166,7 +166,7 @@ { "path": "fleet config", "aliases": [], - "hidden": false, + "hidden": true, "leaf": true, "arguments": [], "options": [ @@ -243,7 +243,7 @@ { "path": "fleet disable", "aliases": [], - "hidden": false, + "hidden": true, "leaf": true, "arguments": [], "options": [ @@ -320,7 +320,7 @@ { "path": "fleet enable", "aliases": [], - "hidden": false, + "hidden": true, "leaf": true, "arguments": [], "options": [ @@ -397,7 +397,7 @@ { "path": "fleet inherit", "aliases": [], - "hidden": false, + "hidden": true, "leaf": true, "arguments": [], "options": [ @@ -878,6 +878,23 @@ "defaultValue": null, "presetArg": null }, + { + "flags": "--unsubscribe-bindings", + "short": null, + "long": "--unsubscribe-bindings", + "mandatory": false, + "valueRequired": false, + "valueOptional": false, + "variadic": false, + "negate": false, + "hidden": false, + "choices": null, + "conflictsWith": [], + "implied": null, + "envVar": null, + "defaultValue": null, + "presetArg": null + }, { "flags": "--wk ", "short": null, @@ -945,6 +962,23 @@ } ], "options": [ + { + "flags": "--auto-place", + "short": null, + "long": "--auto-place", + "mandatory": false, + "valueRequired": false, + "valueOptional": false, + "variadic": false, + "negate": false, + "hidden": false, + "choices": null, + "conflictsWith": [], + "implied": null, + "envVar": null, + "defaultValue": null, + "presetArg": null + }, { "flags": "--base-url ", "short": null, @@ -979,6 +1013,23 @@ "defaultValue": null, "presetArg": null }, + { + "flags": "--checkout", + "short": null, + "long": "--checkout", + "mandatory": false, + "valueRequired": false, + "valueOptional": false, + "variadic": false, + "negate": false, + "hidden": false, + "choices": null, + "conflictsWith": [], + "implied": null, + "envVar": null, + "defaultValue": null, + "presetArg": null + }, { "flags": "--confirm-timeout ", "short": null, @@ -2126,23 +2177,6 @@ "defaultValue": null, "presetArg": null }, - { - "flags": "--endpoint ", - "short": null, - "long": "--endpoint", - "mandatory": false, - "valueRequired": true, - "valueOptional": false, - "variadic": false, - "negate": false, - "hidden": false, - "choices": null, - "conflictsWith": [], - "implied": null, - "envVar": null, - "defaultValue": null, - "presetArg": null - }, { "flags": "--exit-after-task", "short": null, @@ -2211,40 +2245,6 @@ "defaultValue": null, "presetArg": null }, - { - "flags": "--protocol ", - "short": null, - "long": "--protocol", - "mandatory": false, - "valueRequired": true, - "valueOptional": false, - "variadic": false, - "negate": false, - "hidden": false, - "choices": null, - "conflictsWith": [], - "implied": null, - "envVar": null, - "defaultValue": null, - "presetArg": null - }, - { - "flags": "--release ", - "short": null, - "long": "--release", - "mandatory": false, - "valueRequired": true, - "valueOptional": false, - "variadic": false, - "negate": false, - "hidden": false, - "choices": null, - "conflictsWith": [], - "implied": null, - "envVar": null, - "defaultValue": null, - "presetArg": null - }, { "flags": "--runtime ", "short": null, @@ -2262,23 +2262,6 @@ "defaultValue": "auto", "presetArg": null }, - { - "flags": "--session-id ", - "short": null, - "long": "--session-id", - "mandatory": false, - "valueRequired": true, - "valueOptional": false, - "variadic": false, - "negate": false, - "hidden": false, - "choices": null, - "conflictsWith": [], - "implied": null, - "envVar": null, - "defaultValue": null, - "presetArg": null - }, { "flags": "--spawn-mode ", "short": null, @@ -2352,25 +2335,7 @@ "defaultValue": null } ], - "options": [ - { - "flags": "--json", - "short": null, - "long": "--json", - "mandatory": false, - "valueRequired": false, - "valueOptional": false, - "variadic": false, - "negate": false, - "hidden": false, - "choices": null, - "conflictsWith": [], - "implied": null, - "envVar": null, - "defaultValue": null, - "presetArg": null - } - ] + "options": [] }, { "path": "node agent spawn", @@ -2421,23 +2386,6 @@ "defaultValue": null, "presetArg": null }, - { - "flags": "--endpoint ", - "short": null, - "long": "--endpoint", - "mandatory": false, - "valueRequired": true, - "valueOptional": false, - "variadic": false, - "negate": false, - "hidden": false, - "choices": null, - "conflictsWith": [], - "implied": null, - "envVar": null, - "defaultValue": null, - "presetArg": null - }, { "flags": "--exit-after-task", "short": null, @@ -2489,40 +2437,6 @@ "defaultValue": null, "presetArg": null }, - { - "flags": "--protocol ", - "short": null, - "long": "--protocol", - "mandatory": false, - "valueRequired": true, - "valueOptional": false, - "variadic": false, - "negate": false, - "hidden": false, - "choices": null, - "conflictsWith": [], - "implied": null, - "envVar": null, - "defaultValue": null, - "presetArg": null - }, - { - "flags": "--release ", - "short": null, - "long": "--release", - "mandatory": false, - "valueRequired": true, - "valueOptional": false, - "variadic": false, - "negate": false, - "hidden": false, - "choices": null, - "conflictsWith": [], - "implied": null, - "envVar": null, - "defaultValue": null, - "presetArg": null - }, { "flags": "--runtime ", "short": null, @@ -2540,23 +2454,6 @@ "defaultValue": "auto", "presetArg": null }, - { - "flags": "--session-id ", - "short": null, - "long": "--session-id", - "mandatory": false, - "valueRequired": true, - "valueOptional": false, - "variadic": false, - "negate": false, - "hidden": false, - "choices": null, - "conflictsWith": [], - "implied": null, - "envVar": null, - "defaultValue": null, - "presetArg": null - }, { "flags": "--spawn-mode ", "short": null, @@ -2900,6 +2797,40 @@ "defaultValue": null, "presetArg": null }, + { + "flags": "--force", + "short": null, + "long": "--force", + "mandatory": false, + "valueRequired": false, + "valueOptional": false, + "variadic": false, + "negate": false, + "hidden": false, + "choices": null, + "conflictsWith": [], + "implied": null, + "envVar": null, + "defaultValue": null, + "presetArg": null + }, + { + "flags": "--local-only", + "short": null, + "long": "--local-only", + "mandatory": false, + "valueRequired": false, + "valueOptional": false, + "variadic": false, + "negate": false, + "hidden": false, + "choices": null, + "conflictsWith": [], + "implied": null, + "envVar": null, + "defaultValue": null, + "presetArg": null + }, { "flags": "--log-file ", "short": null, diff --git a/tests/relayflows/cleanroom/fleet-daytona.matrix.json b/tests/relayflows/cleanroom/fleet-daytona.matrix.json index 3cb764776..15ab20607 100644 --- a/tests/relayflows/cleanroom/fleet-daytona.matrix.json +++ b/tests/relayflows/cleanroom/fleet-daytona.matrix.json @@ -8,7 +8,7 @@ "requiredSnapshotRelayVersion": "11.10.3", "artifactRoot": ".workflow-artifacts/verify-fleet-daytona", "inventoryFile": "fleet-cli-inventory.json", - "inventorySha256": "27dd9c9fd15677894538893e54d4157521bd4562fce2e6c0a5cc404886a8d534", + "inventorySha256": "8bdc4be9341d4efb5a5e04ae8fe601fd30a9d664681b82ab2c4c729f46b4ac16", "commandSurface": { "fleet agent list": [ "fleet-agent-list-json", @@ -169,88 +169,88 @@ { "option": "--base-url", "status": "skipped", - "reason": "The proof uses the credential-bound workspace endpoint; alternate endpoint selection is covered by CLI connection tests without changing the Daytona target." + "reason": "Legacy SDK options remain accepted and ignored; this compatibility command opens no connection." }, { "option": "--token", "status": "skipped", - "reason": "Token argv would duplicate reveal-once credentials; the board proves redaction and uses the bound workspace key." + "reason": "Legacy SDK options remain accepted and ignored; this compatibility command opens no connection." }, { "option": "--wk", "status": "skipped", - "reason": "The legacy --wk credential alias is covered by CLI connection tests; the board must not mint a second credential source." + "reason": "Legacy SDK options remain accepted and ignored; this compatibility command opens no connection." }, { "option": "--workspace-key", "status": "skipped", - "reason": "The bound workspace key is injected only where the operation needs it; repeating it on every Fleet read would multiply secret-bearing argv." + "reason": "Legacy SDK options remain accepted and ignored; this compatibility command opens no connection." } ], "fleet disable": [ { "option": "--base-url", "status": "skipped", - "reason": "The proof uses the credential-bound workspace endpoint; alternate endpoint selection is covered by CLI connection tests without changing the Daytona target." + "reason": "Legacy SDK options remain accepted and ignored; this compatibility command opens no connection." }, { "option": "--token", "status": "skipped", - "reason": "Token argv would duplicate reveal-once credentials; the board proves redaction and uses the bound workspace key." + "reason": "Legacy SDK options remain accepted and ignored; this compatibility command opens no connection." }, { "option": "--wk", "status": "skipped", - "reason": "The legacy --wk credential alias is covered by CLI connection tests; the board must not mint a second credential source." + "reason": "Legacy SDK options remain accepted and ignored; this compatibility command opens no connection." }, { "option": "--workspace-key", "status": "skipped", - "reason": "The bound workspace key is injected only where the operation needs it; repeating it on every Fleet read would multiply secret-bearing argv." + "reason": "Legacy SDK options remain accepted and ignored; this compatibility command opens no connection." } ], "fleet enable": [ { "option": "--base-url", "status": "skipped", - "reason": "The proof uses the credential-bound workspace endpoint; alternate endpoint selection is covered by CLI connection tests without changing the Daytona target." + "reason": "Legacy SDK options remain accepted and ignored; this compatibility command opens no connection." }, { "option": "--token", "status": "skipped", - "reason": "Token argv would duplicate reveal-once credentials; the board proves redaction and uses the bound workspace key." + "reason": "Legacy SDK options remain accepted and ignored; this compatibility command opens no connection." }, { "option": "--wk", "status": "skipped", - "reason": "The legacy --wk credential alias is covered by CLI connection tests; the board must not mint a second credential source." + "reason": "Legacy SDK options remain accepted and ignored; this compatibility command opens no connection." }, { "option": "--workspace-key", "status": "skipped", - "reason": "The bound workspace key is injected only where the operation needs it; repeating it on every Fleet read would multiply secret-bearing argv." + "reason": "Legacy SDK options remain accepted and ignored; this compatibility command opens no connection." } ], "fleet inherit": [ { "option": "--base-url", "status": "skipped", - "reason": "The proof uses the credential-bound workspace endpoint; alternate endpoint selection is covered by CLI connection tests without changing the Daytona target." + "reason": "Legacy SDK options remain accepted and ignored; this compatibility command opens no connection." }, { "option": "--token", "status": "skipped", - "reason": "Token argv would duplicate reveal-once credentials; the board proves redaction and uses the bound workspace key." + "reason": "Legacy SDK options remain accepted and ignored; this compatibility command opens no connection." }, { "option": "--wk", "status": "skipped", - "reason": "The legacy --wk credential alias is covered by CLI connection tests; the board must not mint a second credential source." + "reason": "Legacy SDK options remain accepted and ignored; this compatibility command opens no connection." }, { "option": "--workspace-key", "status": "skipped", - "reason": "The bound workspace key is injected only where the operation needs it; repeating it on every Fleet read would multiply secret-bearing argv." + "reason": "Legacy SDK options remain accepted and ignored; this compatibility command opens no connection." } ], "fleet nodes": [ @@ -352,6 +352,11 @@ } ], "fleet release": [ + { + "option": "--unsubscribe-bindings", + "status": "skipped", + "reason": "Provider-binding retirement is covered by CLI release tests; this board creates no provider subscriptions." + }, { "option": "--base-url", "status": "skipped", @@ -401,6 +406,16 @@ } ], "fleet spawn": [ + { + "option": "--auto-place", + "status": "skipped", + "reason": "Automatic placement is covered by CLI tests; this board pins exact owned nodes for cleanup." + }, + { + "option": "--checkout", + "status": "skipped", + "reason": "Checkout placement is covered by CLI tests; this board uses its provisioned fixture checkout." + }, { "option": "--base-url", "status": "skipped", @@ -750,11 +765,6 @@ "status": "supported", "operationId": "node-agent-new-view" }, - { - "option": "--endpoint", - "status": "skipped", - "reason": "Headless AppServer endpoint options belong to node agent spawn, not the PTY/native agent new path." - }, { "option": "--exit-after-task", "status": "skipped", @@ -775,16 +785,6 @@ "status": "supported", "operationId": "node-agent-new-view" }, - { - "option": "--protocol", - "status": "skipped", - "reason": "Headless AppServer protocol options belong to node agent spawn, not the PTY/native agent new path." - }, - { - "option": "--release", - "status": "skipped", - "reason": "agent new cannot attach to headless AppServer workers; release policy is exercised by node agent spawn headless." - }, { "option": "--runtime", "status": "supported", @@ -807,11 +807,6 @@ } ] }, - { - "option": "--session-id", - "status": "skipped", - "reason": "Headless AppServer session options belong to node agent spawn, not the PTY/native agent new path." - }, { "option": "--spawn-mode", "status": "skipped", @@ -824,14 +819,7 @@ } ], "node agent release": [], - "node agent set-model": [ - { - "option": "--json", - "status": "supported", - "operationId": "node-agent-set-model", - "takesValue": false - } - ], + "node agent set-model": [], "node agent spawn": [ { "option": "--channels", @@ -843,11 +831,6 @@ "status": "supported", "operationId": "node-agent-spawn-codex-pty" }, - { - "option": "--endpoint", - "status": "supported", - "operationId": "node-agent-set-model-app-server-a" - }, { "option": "--exit-after-task", "status": "supported", @@ -864,26 +847,11 @@ "status": "supported", "operationId": "node-agent-spawn-codex-auto-a" }, - { - "option": "--protocol", - "status": "supported", - "operationId": "node-agent-set-model-app-server-a" - }, - { - "option": "--release", - "status": "supported", - "operationId": "node-agent-set-model-app-server-a" - }, { "option": "--runtime", "status": "supported", "operationId": "node-agent-spawn-codex-pty" }, - { - "option": "--session-id", - "status": "supported", - "operationId": "node-agent-set-model-app-server-a" - }, { "option": "--spawn-mode", "status": "supported", @@ -1080,29 +1048,18 @@ "teardownAssertion": "Final cleanup re-verifies the exact sandbox remains absent.", "retryAssertion": "Reconciliation is exact-name/id based and repeated deletion is idempotent." }, - "fleet-policy-read": { + "fleet-deprecated-noop": { "candidateSurface": "operator-candidate", "executionScope": "ephemeral-workspace", "effectAssertions": [ - "Fleet policy returns an explicit nullable override and boolean effective value." + "Exits 0 and prints a deprecation notice without workspace mutation; config emits a JSON deprecation object." ], "negativeAssertions": [ - "Missing SDK support, malformed schema, or an unknown initial override fails." + "An unreachable endpoint must not cause a failure; the removed workspace API must not be called." ], - "lifecycleAssertion": "The policy read is bounded and timed.", - "teardownAssertion": "Read-only; the captured value becomes the exact restoration target for mutation probes.", - "retryAssertion": "A retry must converge on the same authoritative override." - }, - "fleet-policy-mutation": { - "candidateSurface": "operator-candidate", - "executionScope": "ephemeral-workspace", - "effectAssertions": ["The authorized disposable workspace reads back the exact requested override."], - "negativeAssertions": [ - "Wrong workspace identity, missing authorization, failed readback, or failed exact restoration cannot pass." - ], - "lifecycleAssertion": "Each mutation/readback and final restoration is bounded and timed.", - "teardownAssertion": "The original nullable override is restored in finally and independently verified.", - "retryAssertion": "Read-after-write makes repeated mutation idempotent and observable." + "lifecycleAssertion": "Each compatibility command is bounded and timed.", + "teardownAssertion": "No workspace state is changed and no restoration is required.", + "retryAssertion": "Repeated invocations are idempotent no-ops." }, "node-read": { "candidateSurface": "daytona-candidate", @@ -1281,10 +1238,10 @@ "fleet-release": "release-process", "fleet-release-delete-agent": "release-process", "fleet-release-reclaims-owned-sandbox": "release-sandbox", - "fleet-config": "fleet-policy-read", - "fleet-enable": "fleet-policy-mutation", - "fleet-disable": "fleet-policy-mutation", - "fleet-inherit": "fleet-policy-mutation", + "fleet-config": "fleet-deprecated-noop", + "fleet-enable": "fleet-deprecated-noop", + "fleet-disable": "fleet-deprecated-noop", + "fleet-inherit": "fleet-deprecated-noop", "fleet-status": "node-read", "fleet-serve-migration": "expected-rejection", "fleet-serve-migration-default": "expected-rejection", @@ -1532,24 +1489,21 @@ "argvMustContain": ["--delete-agent"] }, { "id": "fleet-release-reclaims-owned-sandbox", "group": "fleet", "expect": "success" }, - { "id": "fleet-config", "group": "fleet-policy", "expect": "success" }, + { "id": "fleet-config", "group": "fleet", "expect": "success" }, { "id": "fleet-enable", - "group": "fleet-policy", - "expect": "success", - "destructiveScope": "workspace-policy" + "group": "fleet", + "expect": "success" }, { "id": "fleet-disable", - "group": "fleet-policy", - "expect": "success", - "destructiveScope": "workspace-policy" + "group": "fleet", + "expect": "success" }, { "id": "fleet-inherit", - "group": "fleet-policy", - "expect": "success", - "destructiveScope": "workspace-policy" + "group": "fleet", + "expect": "success" }, { "id": "fleet-status", "group": "fleet", "expect": "success" }, { diff --git a/tests/relayflows/cleanroom/relay.matrix.json b/tests/relayflows/cleanroom/relay.matrix.json index 379aa1cf2..2e3a46c60 100644 --- a/tests/relayflows/cleanroom/relay.matrix.json +++ b/tests/relayflows/cleanroom/relay.matrix.json @@ -448,10 +448,6 @@ "fleet-nodes-pretty", "fleet-spawn", "fleet-release", - "fleet-config", - "fleet-enable", - "fleet-disable", - "fleet-inherit", "fleet-status" ] },