import { execFileSync } from 'node:child_process'; import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { TorchError } from '../kernel/errors.mjs'; import { readInstallManifest } from '../kernel/install.mjs'; import { loadFleetDefinition } from '../kernel/worktrees.mjs'; import { organizationGraphFromConfig } from '../kernel/organization.mjs'; import { sha256 } from '../kernel/files.mjs'; import { costCeilingPlanIssues, profileCapabilityIssues, resolveLaunchPolicy, validateRuntimeAdapter, } from '../adapters/runtime.mjs'; import { hierarchyOrder } from './hierarchy-order.mjs'; const MCP_ENTRY = fileURLToPath(new URL('../mcp/server.mjs', import.meta.url)); function localStateRoot(manifest) { const record = (manifest.external ?? []).find((entry) => entry.type === 'local-state'); if (!record?.path) throw new TorchError('Installation manifest has no local state root', { code: 'LOCAL_STATE_MISSING' }); return record.path; } function worktreesByArea(manifest) { return new Map((manifest.external ?? []) .filter((entry) => entry.type === 'worktree') .map((entry) => [entry.area, entry])); } function selectedAreas(roster, only, blockers, ordered = roster.areas) { if (only === undefined) return ordered; const selected = Array.isArray(only) ? [...new Set(only.map((id) => typeof id === 'string' ? id.trim() : '').filter(Boolean))] : []; if (!selected.length) { blockers.push({ code: 'FLEET_SELECTION_EMPTY' }); return []; } const known = new Set(ordered.map((area) => area.id)); for (const areaId of selected.filter((id) => !known.has(id))) { blockers.push({ areaId, code: 'FLEET_IDENTITY_MISSING' }); } const wanted = new Set(selected); return ordered.filter((area) => wanted.has(area.id)); } function adapterMap({ adapter, adapters } = {}) { if (adapters instanceof Map) return adapters; if (adapters && typeof adapters === 'object') return new Map(Object.entries(adapters)); if (adapter) return new Map([[adapter.name, adapter]]); return new Map(); } function configuredRuntime(area, config) { if (area.id === 'session-manager') { return config.session_manager?.runtime ?? area.runtime ?? config.runtimes?.default; } return config.domains?.find((domain) => domain.id === area.id)?.runtime ?? area.runtime ?? config.runtimes?.default; } function configuredProfile(area, config, runtime) { const runtimeConfig = config.runtimes?.[runtime] ?? {}; const configured = area.id === 'session-manager' ? config.session_manager : config.domains?.find((domain) => domain.id === area.id); const launchPolicy = resolveLaunchPolicy({ runtime, adapterDefaults: {}, runtimeConfig, identity: configured, }); return { model: configured?.model ?? area.model ?? runtimeConfig.model ?? null, reasoning: configured?.reasoning ?? area.reasoning ?? runtimeConfig.reasoning ?? null, launchPolicy, }; } function organizationContext(config, areaId) { const graph = organizationGraphFromConfig(config); const rolesById = new Map(graph.roles.map((role) => [role.id, role])); const ownRoles = graph.roles.filter((role) => role.identity_id === areaId); const ownRoleIds = new Set(ownRoles.map((role) => role.id)); const summarizeRole = (role) => ({ roleId: role.id, title: role.title, kind: role.kind, responsibilities: role.responsibilities, authority: role.authority, reportsTo: role.reports_to.map((roleId) => rolesById.get(roleId)?.title ?? roleId), }); const directReports = new Map(); for (const role of graph.roles) { if (role.identity_id === areaId || !role.reports_to.some((parent) => ownRoleIds.has(parent))) continue; directReports.set(role.identity_id, summarizeRole(role)); } const related = (field) => [...new Map(ownRoles.flatMap((role) => role[field] .map((roleId) => rolesById.get(roleId)).filter(Boolean) .filter((role) => role.identity_id !== areaId) .map((role) => [role.identity_id, { identityId: role.identity_id, title: role.title }]))).values()] .sort((left, right) => left.identityId.localeCompare(right.identityId)); return { schema: graph.schema, revision: graph.revision, identityId: areaId, roles: ownRoles.map(summarizeRole), directReports: [...directReports.entries()].sort(([left], [right]) => left.localeCompare(right)) .map(([identityId, role]) => ({ identityId, ...role })), coordinates: related('coordinates'), consultsWith: related('consults_with'), implementationOwnership: graph.implementation_owners .filter((owner) => owner.identity_id === areaId), }; } function managerCheckInInstructions(organization) { if (!organization.directReports.length) return []; return [ '## Scheduled manager check-ins', '', 'Your active organization role has direct reports. TORCH queues a durable self-directed `manager-check-in` message on the configured schedule; the wake snapshot may be stale, and a currently active session may receive the message only in its durable inbox.', '- At startup/resume, after seeing a `manager-check-in` message, and at the start of each new coordination cycle, read your inbox and call `torch_plan_manager_check_in` to refresh direct-report presence, blockers, messages, handoffs, and structured approval waits from current durable state.', '- Follow up on each finding within your coordination authority. For a pending approval, decide only when you are the named approver; route owner- or peer-owned approvals to that approver and never decide for them.', '- A check-in is observational: it does not assign work, transfer ownership, approve a request, create an identity, or authorize runtime/spending changes. Preserve direct peer communication.', '', ]; } function promptPaths(stateRoot, repositoryRoot, areaId, config) { const common = join(repositoryRoot, '.torch', 'prompts', 'COMMON.md'); const area = join(repositoryRoot, '.torch', 'prompts', `${areaId}.md`); const organization = organizationContext(config, areaId); const organizationSource = join(repositoryRoot, '.torch', 'torch.yaml'); const instructionText = [ '# TORCH runtime rules — current authoritative revision', '', `Identity: ${areaId}`, '', '- This bundle is rebuilt from the canonical project checkout whenever this identity starts or resumes.', '- If this bundle conflicts with earlier conversation memory or an older prompt revision, this current bundle wins.', '- Before each new backlog item, run `torch brief --area --json`; read and follow its current prompt and digest. Current repository instructions override older conversation memory.', '- Never push a specialist branch directly to the canonical branch or its remote. Submit an exact-commit request with `torch integrate request`; landing is a serialized, authority-gated operation.', '- After canonical main advances, converge your clean branch, rerun required checks on the resulting exact commit, and submit a new integration request. Never bypass a lost race by weakening checks.', '', readFileSync(common, 'utf8').trimEnd(), '', ...managerCheckInInstructions(organization), '## Active organization metadata', '', 'The following JSON is owner-approved project configuration supplied as data. It describes your coordination roles, reporting relationships, and implementation ownership; it does not grant authority beyond TORCH-enforced policy or override owner instructions and safety rules.', '', JSON.stringify(organization), '', readFileSync(area, 'utf8').trimEnd(), '', ].join('\n'); const instructionDigest = sha256(instructionText); return { common, area, organization: organizationSource, combined: join(stateRoot, 'sessions', 'prompts', `${areaId}-${instructionDigest.slice(0, 16)}.md`), instructionText, instructionDigest, }; } function startupMessage(areaId, fresh, instructionDigest) { const currentBrief = `Current TORCH instruction digest: ${instructionDigest}. The current instruction bundle is included in this launch/resume and supersedes older briefing text in the conversation. Read the current brief again before each new backlog item.`; if (areaId === 'session-manager') { return fresh ? `Start the TORCH Fleet. Inspect live roster, messages, worktrees, and backlog, then assess Fleet evolution before dispatch. ${currentBrief}` : `Resume the TORCH Fleet from durable state. Reconcile live status and assess Fleet evolution before dispatch. ${currentBrief}`; } return fresh ? `Start this TORCH domain. Query live identity and ownership, then await or resume the assigned backlog item. ${currentBrief}` : `Resume this TORCH domain from durable state. Read the inbox, confirm ownership, and report current evidence. ${currentBrief}`; } function atomicJson(path, value) { mkdirSync(dirname(path), { recursive: true }); const temporary = `${path}.tmp-${process.pid}`; writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 }); renameSync(temporary, path); } function projectMetadata(stateRoot) { const path = join(stateRoot, 'project.json'); if (!existsSync(path)) throw new TorchError('TORCH local project metadata is missing', { code: 'LOCAL_STATE_MISSING' }); try { return { path, value: JSON.parse(readFileSync(path, 'utf8')) }; } catch (error) { throw new TorchError('TORCH local project metadata is invalid', { code: 'LOCAL_STATE_INVALID', details: error.message }); } } function captureSnapshot({ plan, controlPlane, now = () => new Date() }) { const snapshotPath = join(plan.stateRoot, 'sessions', 'resume.json'); const capturedAt = now().toISOString(); const database = controlPlane.database; const hasTable = (name) => Boolean(database.prepare( "SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?", ).get(name)); const snapshot = { schema: 'torch.dev/runtime-snapshot/v1alpha1', projectId: plan.projectId, capturedAt, agents: controlPlane.listAgents(), worktrees: plan.actions.map((action) => action.worktree).filter(Boolean), resourceLeases: hasTable('resource_leases') ? database.prepare(` SELECT resource_id AS resourceId, area_id AS areaId, acquired_at AS acquiredAt, expires_at AS expiresAt FROM resource_leases WHERE released_at IS NULL ORDER BY resource_id, acquired_at `).all() : [], worktreeGuards: hasTable('worktree_guards') ? database.prepare(` SELECT area_id AS areaId, guard_type AS type, reason, created_at AS createdAt FROM worktree_guards WHERE released_at IS NULL ORDER BY created_at, id `).all() : [], integration: hasTable('integration_requests') ? database.prepare(` SELECT id, source_area AS sourceArea, source_commit AS sourceCommit, state, reason FROM integration_requests WHERE state NOT IN ('landed', 'superseded') ORDER BY created_at, id `).all() : [], }; atomicJson(snapshotPath, snapshot); return { snapshotPath, snapshot }; } function writeResumeBrief(plan, snapshot) { if (!plan.resumeBriefPath) return null; const lines = [ '# TORCH resume brief', '', `Captured: ${snapshot.capturedAt}`, `Project: ${snapshot.projectId}`, '', '## Fleet identities', '', ...snapshot.agents.map((agent) => `- ${agent.areaId}: ${agent.state}; runtime=${agent.runtime ?? 'none'}; task=${agent.currentTask ?? agent.task ?? 'none'}; summary=${agent.summary ?? 'none'}`), '', '## Worktrees', '', ...(snapshot.worktrees.length ? snapshot.worktrees.map((worktree) => `- ${worktree.areaId}: branch=${worktree.branch}; commit=${worktree.commit}; dirty=${worktree.dirtyEntries.length ? worktree.dirtyEntries.join(', ') : 'no'}`) : ['- No managed worktrees recorded.']), '', '## Open integration', '', ...(snapshot.integration.length ? snapshot.integration.map((request) => `- ${request.id}: ${request.sourceArea} ${request.sourceCommit} is ${request.state}${request.reason ? ` (${request.reason})` : ''}`) : ['- None.']), '', '## Guards and leases', '', ...(snapshot.worktreeGuards.length ? snapshot.worktreeGuards.map((guard) => `- guard: ${guard.areaId} ${guard.type} — ${guard.reason}`) : ['- No active worktree guards.']), ...(snapshot.resourceLeases.length ? snapshot.resourceLeases.map((lease) => `- lease: ${lease.areaId} holds ${lease.resourceId} until ${lease.expiresAt}`) : ['- No active resource leases.']), '', 'Resume by checking live identity, inbox, backlog, worktree state, and exact check receipts before dispatch.', '', ]; const temporary = `${plan.resumeBriefPath}.tmp-${process.pid}`; writeFileSync(temporary, `${lines.join('\n')}\n`, { encoding: 'utf8', mode: 0o600, flag: 'wx' }); renameSync(temporary, plan.resumeBriefPath); return plan.resumeBriefPath; } function inspectWorktree(worktree) { const git = (args) => execFileSync('git', ['-C', worktree.path, ...args], { encoding: 'utf8' }).trim(); const operations = ['MERGE_HEAD', 'REBASE_HEAD', 'CHERRY_PICK_HEAD', 'REVERT_HEAD'] .filter((name) => existsSync(git(['rev-parse', '--git-path', name]))); return { areaId: worktree.area, path: worktree.path, branch: git(['branch', '--show-current']), commit: git(['rev-parse', 'HEAD']), dirtyEntries: git(['status', '--porcelain']).split('\n').filter(Boolean), operations, }; } export function planFleetUp({ repositoryRoot, controlPlane, adapter, adapters, fresh = false, only } = {}) { const { config, roster } = loadFleetDefinition(repositoryRoot); const hierarchy = hierarchyOrder(roster, config); const manifest = readInstallManifest(repositoryRoot); const worktrees = worktreesByArea(manifest); const stateRoot = localStateRoot(manifest); const runtimes = adapterMap({ adapter, adapters }); const blockers = [...hierarchy.blockers]; const actions = []; for (const area of selectedAreas(roster, only, blockers, hierarchy.areas)) { const worktree = worktrees.get(area.id); if (!worktree) { blockers.push({ areaId: area.id, code: 'WORKTREE_MISSING' }); continue; } const runtime = configuredRuntime(area, config); const runtimeAdapter = runtimes.get(runtime); if (!runtimeAdapter) { blockers.push({ areaId: area.id, code: 'RUNTIME_ADAPTER_MISSING', runtime }); continue; } if (runtimeAdapter.name !== runtime) { blockers.push({ areaId: area.id, code: 'RUNTIME_ADAPTER_MISMATCH', runtime, adapter: runtimeAdapter.name, }); continue; } validateRuntimeAdapter(runtimeAdapter); const identity = controlPlane.identity(area.id); const runtimeIntegration = runtimeAdapter.configure({ repositoryRoot, areaId: area.id, mcpEntry: MCP_ENTRY, projectId: manifest.projectId, stateRoot, }); if (runtimeIntegration?.mutationPerformed !== false || !(runtimeIntegration?.mcp || runtimeIntegration?.integration)) { blockers.push({ areaId: area.id, code: 'RUNTIME_CONFIGURATION_INVALID', runtime, }); continue; } const prompts = promptPaths(stateRoot, repositoryRoot, area.id, config); const profile = configuredProfile(area, config, runtime); const unsupported = profileCapabilityIssues(runtimeAdapter, profile); if (unsupported.length) { blockers.push({ areaId: area.id, code: 'RUNTIME_PROFILE_UNSUPPORTED', runtime, adapter: runtimeAdapter.name, profile, capabilities: unsupported, }); continue; } const runtimeConfig = config.runtimes?.[runtime] ?? {}; const { model, reasoning, launchPolicy } = profile; const background = runtimeConfig.background ?? true; const shouldResume = !fresh && identity.runtime === runtime && Boolean(identity.runtimeSessionId); const runtimePlan = shouldResume ? runtimeAdapter.resumeSession({ areaId: area.id, runtimeSessionId: identity.runtimeSessionId, worktree: worktree.path, model, reasoning, launchPolicy, background, message: startupMessage(area.id, false, prompts.instructionDigest), promptFile: prompts.combined, instructionText: prompts.instructionText, instructionDigest: prompts.instructionDigest, mcp: runtimeIntegration.mcp, integration: runtimeIntegration.integration, }) : runtimeAdapter.createSession({ areaId: area.id, title: area.title, worktree: worktree.path, promptFile: prompts.combined, firstMessage: startupMessage(area.id, true, prompts.instructionDigest), model, reasoning, launchPolicy, background, mcp: runtimeIntegration.mcp, integration: runtimeIntegration.integration, }); actions.push({ areaId: area.id, title: area.title, mode: shouldResume ? 'resume' : 'create', managerIds: [...(hierarchy.managersByIdentity.get(area.id) ?? [])], runtime: runtimeAdapter.name, runtimeSessionId: runtimePlan.runtimeSessionId, profile, requiresRuntimeIdCapture: runtimePlan.requiresRuntimeIdCapture ?? false, completionState: runtimePlan.completionState ?? 'starting', worktree: worktree.path, branch: worktree.branch, promptFile: prompts.combined, promptSources: [prompts.common, prompts.area, prompts.organization], instructionText: prompts.instructionText, instructionDigest: prompts.instructionDigest, mcp: runtimeIntegration.mcp ?? null, integration: runtimeIntegration.integration ?? null, launch: runtimePlan.launch, }); } return { action: 'fleet-up', projectId: manifest.projectId, stateRoot, fresh, only: only ?? null, startupOrder: actions.map((action) => action.areaId), actions, blockers, canProceed: blockers.length === 0, mutationPerformed: false, }; } export function planAreaUp({ repositoryRoot, controlPlane, areaId, adapter, adapters, fresh = false, maxCostUsd } = {}) { const { config, roster } = loadFleetDefinition(repositoryRoot); const manifest = readInstallManifest(repositoryRoot); const worktree = worktreesByArea(manifest).get(areaId); const area = roster.areas.find((candidate) => candidate.id === areaId); const blockers = []; if (maxCostUsd !== undefined && (!Number.isFinite(maxCostUsd) || maxCostUsd <= 0)) { blockers.push({ areaId, code: 'RUNTIME_COST_CEILING_INVALID', maxUsd: maxCostUsd }); } if (!area) blockers.push({ areaId, code: 'FLEET_IDENTITY_MISSING' }); if (!worktree) blockers.push({ areaId, code: 'WORKTREE_MISSING' }); if (blockers.length) { return { action: 'area-up', projectId: manifest.projectId, areaId, fresh, actions: [], blockers, canProceed: false, mutationPerformed: false, }; } const runtimes = adapterMap({ adapter, adapters }); const runtime = configuredRuntime(area, config); const runtimeAdapter = runtimes.get(runtime); if (!runtimeAdapter) blockers.push({ areaId, code: 'RUNTIME_ADAPTER_MISSING', runtime }); else if (runtimeAdapter.name !== runtime) { blockers.push({ areaId, code: 'RUNTIME_ADAPTER_MISMATCH', runtime, adapter: runtimeAdapter.name }); } if (blockers.length) { return { action: 'area-up', projectId: manifest.projectId, areaId, fresh, actions: [], blockers, canProceed: false, mutationPerformed: false, }; } validateRuntimeAdapter(runtimeAdapter); const identity = controlPlane.identity(area.id); const runtimeIntegration = runtimeAdapter.configure({ repositoryRoot, areaId: area.id, mcpEntry: MCP_ENTRY, projectId: manifest.projectId, stateRoot: localStateRoot(manifest), }); if (runtimeIntegration?.mutationPerformed !== false || !(runtimeIntegration?.mcp || runtimeIntegration?.integration)) { return { action: 'area-up', projectId: manifest.projectId, areaId, fresh, actions: [], blockers: [{ areaId, code: 'RUNTIME_CONFIGURATION_INVALID', runtime }], canProceed: false, mutationPerformed: false, }; } const prompts = promptPaths(localStateRoot(manifest), repositoryRoot, area.id, config); const profile = configuredProfile(area, config, runtime); const unsupported = profileCapabilityIssues(runtimeAdapter, profile); if (unsupported.length) { return { action: 'area-up', projectId: manifest.projectId, areaId, fresh, actions: [], blockers: [{ areaId: area.id, code: 'RUNTIME_PROFILE_UNSUPPORTED', runtime, adapter: runtimeAdapter.name, profile, capabilities: unsupported, }], canProceed: false, mutationPerformed: false, }; } const runtimeConfig = config.runtimes?.[runtime] ?? {}; const { model, reasoning, launchPolicy } = profile; const background = runtimeConfig.background ?? true; const shouldResume = !fresh && identity.runtime === runtime && Boolean(identity.runtimeSessionId); const operation = shouldResume ? 'resumeSession' : 'createSession'; const declaredCostModes = runtimeAdapter.capabilities.perInvocationCostCeiling[operation]; if (maxCostUsd !== undefined && declaredCostModes.length === 0) { return { action: 'area-up', projectId: manifest.projectId, areaId, fresh, actions: [], blockers: [{ areaId, code: 'RUNTIME_COST_CEILING_UNSUPPORTED', runtime: runtimeAdapter.name, operation, maxUsd: maxCostUsd, background, }], canProceed: false, mutationPerformed: false, }; } const runtimePlan = shouldResume ? runtimeAdapter.resumeSession({ areaId: area.id, runtimeSessionId: identity.runtimeSessionId, worktree: worktree.path, model, reasoning, launchPolicy, background, message: startupMessage(area.id, false, prompts.instructionDigest), promptFile: prompts.combined, instructionText: prompts.instructionText, instructionDigest: prompts.instructionDigest, mcp: runtimeIntegration.mcp, integration: runtimeIntegration.integration, ...(maxCostUsd === undefined ? {} : { maxCostUsd }), }) : runtimeAdapter.createSession({ areaId: area.id, title: area.title, worktree: worktree.path, promptFile: prompts.combined, firstMessage: startupMessage(area.id, true, prompts.instructionDigest), model, reasoning, launchPolicy, background, mcp: runtimeIntegration.mcp, integration: runtimeIntegration.integration, ...(maxCostUsd === undefined ? {} : { maxCostUsd }), }); if (maxCostUsd !== undefined) { const ceilingIssues = costCeilingPlanIssues(runtimeAdapter, operation, maxCostUsd, runtimePlan); if (ceilingIssues.length) { return { action: 'area-up', projectId: manifest.projectId, areaId, fresh, actions: [], blockers: ceilingIssues.map((issue) => ({ areaId, ...issue, background })), canProceed: false, mutationPerformed: false, }; } } const action = { areaId: area.id, title: area.title, mode: shouldResume ? 'resume' : 'create', runtime: runtimeAdapter.name, runtimeSessionId: runtimePlan.runtimeSessionId, profile, ...(maxCostUsd === undefined ? {} : { costCeiling: runtimePlan.costCeiling }), requiresRuntimeIdCapture: runtimePlan.requiresRuntimeIdCapture ?? false, completionState: runtimePlan.completionState ?? 'starting', worktree: worktree.path, branch: worktree.branch, promptFile: prompts.combined, promptSources: [prompts.common, prompts.area, prompts.organization], instructionText: prompts.instructionText, instructionDigest: prompts.instructionDigest, mcp: runtimeIntegration.mcp ?? null, integration: runtimeIntegration.integration ?? null, launch: runtimePlan.launch, }; return { action: 'area-up', projectId: manifest.projectId, areaId, fresh, actions: [action], blockers, canProceed: true, mutationPerformed: false, }; } function writeCombinedPrompt(action) { mkdirSync(dirname(action.promptFile), { recursive: true }); writeFileSync(action.promptFile, action.instructionText, { encoding: 'utf8', mode: 0o600 }); } export function startFleet({ plan, controlPlane, executor, adapters } = {}) { if (!plan?.canProceed) { throw new TorchError('Fleet startup plan has unresolved blockers', { code: 'FLEET_NOT_READY_TO_START', details: plan?.blockers ?? [], }); } if (typeof executor !== 'function') { throw new TorchError('Fleet startup requires an explicitly authorized runtime executor', { code: 'RUNTIME_EXECUTION_NOT_AUTHORIZED', }); } const started = []; const runtimes = adapterMap({ adapters }); for (const action of plan.actions) { controlPlane.assertIdentity(action.areaId); writeCombinedPrompt(action); const result = executor({ ...action.launch, areaId: action.areaId, runtimeSessionId: action.runtimeSessionId }); if (result?.status !== undefined && result.status !== 0) { controlPlane.reportStatus({ areaId: action.areaId, state: 'offline', runtime: action.runtime, runtimeSessionId: action.runtimeSessionId, summary: 'Runtime launch failed.', }); throw new TorchError(`Fleet startup failed for ${action.areaId}`, { code: 'FLEET_START_FAILED', details: { areaId: action.areaId, status: result.status, stderr: result.stderr ?? null, started }, }); } let runtimeSessionId = action.runtimeSessionId; if (action.requiresRuntimeIdCapture) { const runtimeAdapter = runtimes.get(action.runtime); if (!runtimeAdapter) { controlPlane.reportStatus({ areaId: action.areaId, state: 'offline', runtime: action.runtime, summary: 'Runtime launched but its durable session ID could not be captured.', }); throw new TorchError(`Fleet startup could not capture a runtime ID for ${action.areaId}`, { code: 'RUNTIME_ADAPTER_MISSING', details: { areaId: action.areaId, runtime: action.runtime, started }, }); } try { runtimeSessionId = runtimeAdapter.captureRuntimeId({ areaId: action.areaId, runtimeSessionId, stdout: result?.stdout, output: result?.output, }).runtimeSessionId; } catch (error) { controlPlane.reportStatus({ areaId: action.areaId, state: 'offline', runtime: action.runtime, summary: 'Runtime launch completed without a capturable durable session ID.', }); throw new TorchError(`Fleet startup could not capture a runtime ID for ${action.areaId}`, { code: 'FLEET_START_FAILED', details: { areaId: action.areaId, cause: error.code ?? error.message, started }, }); } } const identity = controlPlane.reportStatus({ areaId: action.areaId, state: action.completionState, runtime: action.runtime, runtimeSessionId, summary: action.completionState === 'idle' ? `${action.mode} turn completed` : `${action.mode} requested`, }); started.push({ areaId: action.areaId, runtimeSessionId: identity.runtimeSessionId, mode: action.mode }); } if (plan.stateRoot) { const metadata = projectMetadata(plan.stateRoot); if (metadata.value.detachedAt) { atomicJson(metadata.path, { ...metadata.value, detachedAt: null, attachedAt: new Date().toISOString() }); } } return { projectId: plan.projectId, started, mutationPerformed: true }; } export function planFleetDown({ repositoryRoot, controlPlane, adapters } = {}) { const { config, roster } = loadFleetDefinition(repositoryRoot); const hierarchy = hierarchyOrder(roster, config); const manifest = readInstallManifest(repositoryRoot); const worktrees = worktreesByArea(manifest); const runtimes = adapterMap({ adapters }); const adapterRegistryProvided = adapters !== undefined; const database = controlPlane.database; const hasTable = (name) => Boolean(database.prepare( "SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?", ).get(name)); const actions = [...hierarchy.areas].reverse().map((area) => { const identity = controlPlane.identity(area.id); const worktree = worktrees.get(area.id); return { areaId: area.id, state: identity.state, runtime: identity.runtime, managerIds: [...(hierarchy.managersByIdentity.get(area.id) ?? [])], runtimeSessionId: identity.runtimeSessionId, currentTask: identity.currentTask, requiresRuntimeStop: identity.runtimeSessionId ? runtimes.get(identity.runtime)?.capabilities.stopSession !== false : false, runtimeAdapterMissing: Boolean( adapterRegistryProvided && identity.runtimeSessionId && !runtimes.has(identity.runtime), ), worktree: worktree ? inspectWorktree(worktree) : null, }; }); const blockers = [...hierarchy.blockers, ...actions .filter((action) => ['starting', 'working', 'stopping'].includes(action.state)) .map((action) => ({ areaId: action.areaId, state: action.state, code: 'ACTIVE_SESSION' }))]; blockers.push(...actions .filter((action) => action.runtimeAdapterMissing) .map((action) => ({ areaId: action.areaId, runtime: action.runtime, code: 'RUNTIME_ADAPTER_MISSING' }))); blockers.push(...actions .filter((action) => action.worktree?.operations.length) .map((action) => ({ areaId: action.areaId, code: 'GIT_OPERATION_ACTIVE', operations: action.worktree.operations, }))); if (hasTable('resource_leases')) { blockers.push(...database.prepare(` SELECT resource_id AS resourceId, area_id AS areaId, expires_at AS expiresAt FROM resource_leases WHERE released_at IS NULL ORDER BY resource_id, acquired_at `).all().map((lease) => ({ ...lease, code: 'RESOURCE_LEASE_ACTIVE' }))); } if (hasTable('worktree_guards')) { blockers.push(...database.prepare(` SELECT area_id AS areaId, guard_type AS type, reason FROM worktree_guards WHERE released_at IS NULL AND guard_type IN ('check', 'measurement') ORDER BY created_at, id `).all().map((guard) => ({ ...guard, code: 'WORKTREE_GUARD_ACTIVE' }))); } if (hasTable('integration_requests')) { blockers.push(...database.prepare(` SELECT id, source_area AS areaId FROM integration_requests WHERE state = 'landing' ORDER BY created_at, id `).all().map((request) => ({ ...request, code: 'INTEGRATION_LANDING_ACTIVE' }))); } const managerWorktree = worktrees.get('session-manager'); return { action: 'fleet-down', projectId: manifest.projectId, stateRoot: localStateRoot(manifest), resumeBriefPath: managerWorktree ? join(managerWorktree.path, '.torch', 'RESUME-BRIEF.md') : null, shutdownOrder: actions.map((action) => action.areaId), actions, blockers, canProceed: blockers.length === 0, mutationPerformed: false, }; } export function stopFleet({ plan, controlPlane, stopRuntime, now = () => new Date() } = {}) { if (!plan?.canProceed) { throw new TorchError('Fleet wind-down refused while safety conditions remain unresolved', { code: 'FLEET_NOT_READY_TO_STOP', details: plan?.blockers ?? [], }); } if (typeof stopRuntime !== 'function') { throw new TorchError('Fleet wind-down requires an explicitly authorized runtime stopper', { code: 'RUNTIME_EXECUTION_NOT_AUTHORIZED', }); } const stopped = []; for (const action of plan.actions) { if (action.state !== 'offline') { for (const managerId of action.managerIds ?? []) { controlPlane.sendMessage({ sender: action.areaId, recipient: managerId, body: `Final status: ${action.state}. ${action.worktree ? `Branch ${action.worktree.branch} at ${action.worktree.commit}; ${action.worktree.dirtyEntries.length} dirty entries.` : 'No managed worktree.'}`, references: { commit: action.worktree?.commit }, }); } } if (!action.runtimeSessionId || action.state === 'offline' || action.requiresRuntimeStop === false) { if (action.state !== 'offline') { controlPlane.reportStatus({ areaId: action.areaId, state: 'offline', summary: 'No resident runtime process to stop.', task: action.currentTask, }); } stopped.push({ areaId: action.areaId, runtimeSessionId: action.runtimeSessionId, alreadyOffline: true, residentProcess: action.requiresRuntimeStop !== false, }); continue; } controlPlane.reportStatus({ areaId: action.areaId, state: 'stopping', summary: 'TORCH wind-down requested.', task: action.currentTask, }); const result = stopRuntime(action); if (result?.stopped === false || (result?.status !== undefined && result.status !== 0)) { controlPlane.reportStatus({ areaId: action.areaId, state: 'waiting', summary: 'Runtime stop failed.', task: action.currentTask, }); throw new TorchError(`Fleet wind-down failed for ${action.areaId}`, { code: 'FLEET_STOP_FAILED', details: { areaId: action.areaId, result }, }); } controlPlane.reportStatus({ areaId: action.areaId, state: 'offline', summary: 'Runtime stopped cleanly.', task: action.currentTask, }); stopped.push({ areaId: action.areaId, runtimeSessionId: action.runtimeSessionId, alreadyOffline: false }); } const captured = captureSnapshot({ plan, controlPlane, now }); const resumeBriefPath = writeResumeBrief(plan, captured.snapshot); return { projectId: plan.projectId, stopped, snapshotPath: captured.snapshotPath, resumeBriefPath, mutationPerformed: true, }; } export function captureFleet({ repositoryRoot, controlPlane, now = () => new Date() } = {}) { const plan = planFleetDown({ repositoryRoot, controlPlane }); const { snapshotPath } = captureSnapshot({ plan, controlPlane, now }); return { action: 'capture', projectId: plan.projectId, snapshotPath, agents: controlPlane.listAgents(), mutationPerformed: true, }; } export function createFleetBrief({ repositoryRoot, controlPlane, areaId } = {}) { const { config, roster } = loadFleetDefinition(repositoryRoot); const manifest = readInstallManifest(repositoryRoot); const selected = areaId ? roster.areas.filter((area) => area.id === areaId) : hierarchyOrder(roster, config).areas; if (!selected.length) { throw new TorchError(`Unknown Fleet identity: ${areaId}`, { code: 'UNKNOWN_FLEET_IDENTITY' }); } return { schema: 'torch.dev/fleet-brief/v1alpha1', mutationPerformed: false, areas: selected.map((area) => { const prompts = promptPaths(localStateRoot(manifest), repositoryRoot, area.id, config); return { areaId: area.id, title: area.title, identity: controlPlane.identity(area.id), promptSources: [prompts.common, prompts.area, prompts.organization], instructionDigest: prompts.instructionDigest, prompt: prompts.instructionText, }; }), }; } export function planFleetDetach({ repositoryRoot, controlPlane, adapters } = {}) { const plan = planFleetDown({ repositoryRoot, controlPlane, adapters }); return { ...plan, action: 'fleet-detach' }; } export function detachFleet({ plan, controlPlane, stopRuntime, now = () => new Date() } = {}) { if (plan?.action !== 'fleet-detach') { throw new TorchError('Fleet detach requires a reviewed detach plan', { code: 'FLEET_DETACH_PLAN_REQUIRED' }); } const stopped = stopFleet({ plan, controlPlane, stopRuntime, now }); const metadata = projectMetadata(plan.stateRoot); const detachedAt = now().toISOString(); atomicJson(metadata.path, { ...metadata.value, detachedAt }); return { action: 'fleet-detach', projectId: plan.projectId, detachedAt, stopped: stopped.stopped, snapshotPath: stopped.snapshotPath, preserved: ['tracked organization', 'worktrees', 'branches', 'local state'], mutationPerformed: true, }; }