mirror of
https://github.com/PR0M3TH3AN/TORCH.git
synced 2026-10-29 08:51:53 +00:00
Complete public fleet lifecycle commands
This commit is contained in:
1 parent
c714e2602d
commit
17aec17afc
10 files changed
+365
-41
No files matched your search
@@ -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');
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user