Complete public fleet lifecycle commands

This commit is contained in:
thePR0M3TH3AN committed 2026-09-27 18:28:49 -04:00
1 parent c714e2602d
commit 17aec17afc
10 files changed
+365 -41

No files matched your search

+55
View File
@@ -37,6 +37,7 @@ function releaseCandidate(root, version) {
'SCN-ai-fleet-planning',
'SCN-fleet-evolution', 'SCN-cli-fleet-evolution',
'SCN-fleet-boundary-evolution',
'SCN-cli-lifecycle-surface',
'SCN-durable-message', 'SCN-ownership-handoff', 'SCN-worktree-purge-safety',
'SCN-backlog-lifecycle', 'SCN-native-integration', 'SCN-resource-fifo',
'SCN-fleet-fresh-resume', 'SCN-install-doctor-purge',
@@ -100,6 +101,60 @@ test('SCN-cli-domain-review: a generated proposal must be approved before instal
assert.equal(existsSync(join(root, '.torch', 'domain-proposal.approved.json')), true);
});
test('SCN-cli-lifecycle-surface: runtime selection, selective startup, capture, brief, list, and detach are public', () => {
const root = repo();
const proposalPath = join(root, 'fleet-proposal.json');
assert.equal(run(root, ['domains', '--output', proposalPath, '--json']).status, 0);
const proposal = JSON.parse(readFileSync(proposalPath, 'utf8'));
proposal.review = {
status: 'approved', reviewedAt: '2026-09-27T00:00:00Z', reviewedBy: 'fixture-owner', notes: [],
};
writeFileSync(proposalPath, `${JSON.stringify(proposal, null, 2)}\n`);
const preview = run(root, [
'install', '--proposal', proposalPath, '--runtime', 'claude', '--dry-run', '--json',
]);
assert.equal(preview.status, 0, preview.stderr || preview.stdout);
assert.deepEqual(JSON.parse(preview.stdout).runtimes, ['claude']);
const installed = run(root, [
'install', '--proposal', proposalPath, '--runtime', 'claude', '--yes', '--json',
]);
assert.equal(installed.status, 0, installed.stderr || installed.stdout);
const config = JSON.parse(readFileSync(join(root, '.torch', 'torch.yaml'), 'utf8'));
assert.deepEqual(Object.keys(config.runtimes).sort(), ['claude', 'default']);
execFileSync('git', ['-C', root, 'add', '.torch', 'fleet-proposal.json']);
execFileSync('git', ['-C', root, 'commit', '-m', 'install torch']);
const worktreeParent = join(tmpdir(), `${basename(root)}-cli-worktrees`);
const worktrees = run(root, ['worktrees', '--parent', worktreeParent, '--yes', '--json']);
assert.equal(worktrees.status, 0, worktrees.stderr || worktrees.stdout);
const worker = proposal.domains[0].id;
const listed = run(root, ['list', '--json']);
assert.equal(listed.status, 0, listed.stderr || listed.stdout);
assert.equal(JSON.parse(listed.stdout).agents.some((agent) => agent.areaId === worker), true);
const brief = run(root, ['brief', '--area', worker, '--json']);
assert.equal(brief.status, 0, brief.stderr || brief.stdout);
assert.equal(JSON.parse(brief.stdout).areas[0].areaId, worker);
assert.match(JSON.parse(brief.stdout).areas[0].prompt, /Common fleet rules/);
const selective = run(root, ['up', '--only', worker, '--dry-run', '--json']);
assert.equal(selective.status, 0, selective.stderr || selective.stdout);
assert.deepEqual(JSON.parse(selective.stdout).actions.map((action) => action.areaId), [worker]);
const unknown = run(root, ['up', '--only', 'not-a-domain', '--dry-run', '--json']);
assert.equal(unknown.status, 1);
assert.equal(JSON.parse(unknown.stdout).blockers[0].code, 'FLEET_IDENTITY_MISSING');
const captured = run(root, ['capture', '--json']);
assert.equal(captured.status, 0, captured.stderr || captured.stdout);
assert.equal(existsSync(JSON.parse(captured.stdout).snapshotPath), true);
assert.equal(JSON.parse(run(root, ['detach', '--json']).stdout).error, 'APPROVAL_REQUIRED');
const detached = run(root, ['detach', '--yes', '--json']);
assert.equal(detached.status, 0, detached.stderr || detached.stdout);
assert.deepEqual(JSON.parse(detached.stdout).preserved, [
'tracked organization', 'worktrees', 'branches', 'local state',
]);
const doctor = run(root, ['doctor', '--json']);
assert.equal(doctor.status, 0, doctor.stderr || doctor.stdout);
assert.equal(JSON.parse(doctor.stdout).findings.some((finding) => finding.code === 'FLEET_DETACHED'), true);
});
test('SCN-cli-control-plane: CLI messages, acknowledgements, ownership, and status share durable state', () => {
const root = repo();
const proposalPath = join(root, 'fleet-proposal.json');
+22
View File
@@ -109,6 +109,28 @@ test('SCN-domain-approval: installation rejects stale or unapproved organization
assert.equal(existsSync(join(root, '.torch')), false);
});
test('SCN-install-runtime-selection: approved runtime choices are explicit and cover every identity', () => {
const { root, env } = fixture();
const repository = inspectRepository(root);
const proposal = approvedProposal(repository);
assert.deepEqual(planInstall({ repository, proposal, env, runtimes: ['claude'] }).runtimes, ['claude']);
proposal.domains[0].runtime = 'codex';
assert.throws(
() => planInstall({ repository, proposal, env, runtimes: ['claude'] }),
(error) => error.code === 'INSTALL_RUNTIME_MISSING' && error.details.missing.includes('codex'),
);
assert.throws(
() => planInstall({ repository, proposal, env, runtimes: ['unknown'] }),
(error) => error.code === 'INSTALL_RUNTIME_INVALID',
);
const installed = installProject({
repository, proposal, env, projectId: 'runtime-selection', runtimes: ['claude', 'codex'],
});
assert.equal(installed.mutationPerformed, true);
const config = JSON.parse(readFileSync(join(root, '.torch', 'torch.yaml'), 'utf8'));
assert.deepEqual(Object.keys(config.runtimes).sort(), ['claude', 'codex', 'default']);
});
test('SCN-config-schema: installed configuration is strict, versioned, and migration-aware', () => {
const { root, env } = fixture();
const repository = inspectRepository(root);
+16 -1
View File
@@ -12,7 +12,9 @@ import { proposeDomains } from '../../src/kernel/domains.mjs';
import { inspectRepository } from '../../src/kernel/git.mjs';
import { installProject } from '../../src/kernel/install.mjs';
import { createWorktrees } from '../../src/kernel/worktrees.mjs';
import { planFleetDown, planFleetUp, startFleet, stopFleet } from '../../src/runtime/lifecycle.mjs';
import {
detachFleet, planFleetDetach, planFleetDown, planFleetUp, startFleet, stopFleet,
} from '../../src/runtime/lifecycle.mjs';
function fleetFixture({ workerRuntime = 'claude' } = {}) {
const root = mkdtempSync(join(tmpdir(), 'torch-lifecycle-'));
@@ -70,12 +72,25 @@ test('SCN-fleet-fresh-resume: workers start before manager and stable identities
assert.equal(stoppedIds.length, fresh.actions.length);
assert.equal(existsSync(down.snapshotPath), true);
const detached = detachFleet({
plan: planFleetDetach({ repositoryRoot: fixture.root, controlPlane: control }),
controlPlane: control, stopRuntime: () => ({ stopped: true }),
now: () => new Date('2026-09-27T13:00:00Z'),
});
assert.equal(detached.detachedAt, '2026-09-27T13:00:00.000Z');
const metadataPath = join(planFleetDown({
repositoryRoot: fixture.root, controlPlane: control,
}).stateRoot, 'project.json');
assert.equal(JSON.parse(readFileSync(metadataPath, 'utf8')).detachedAt, detached.detachedAt);
const resumed = planFleetUp({ repositoryRoot: fixture.root, controlPlane: control, adapter, fresh: false });
assert.equal(resumed.actions.every((action) => action.mode === 'resume'), true);
assert.deepEqual(
resumed.actions.map((action) => action.runtimeSessionId),
fresh.actions.map((action) => action.runtimeSessionId),
);
startFleet({ plan: resumed, controlPlane: control, executor: () => ({ status: 0 }) });
assert.equal(JSON.parse(readFileSync(metadataPath, 'utf8')).detachedAt, null);
control.close();
});