Harden specialist backlog loop

This commit is contained in:
thePR0M3TH3AN committed 2026-09-27 22:49:19 -04:00
1 parent a900459d87
commit 770cdde4a7
10 files changed
+406 -28

No files matched your search

+25 -8
View File
@@ -9,6 +9,7 @@ import { forgeStatus } from '../forge/service.mjs';
import { classifyRecoverability } from '../canonical/local.mjs';
import { createClaudeAdapter } from '../adapters/claude.mjs';
import { createCodexAdapter } from '../adapters/codex.mjs';
import { assessBacklogHealth, backlogObservedCommitDistance } from '../backlog/service.mjs';
function parseJsonYaml(path) {
return JSON.parse(readFileSync(path, 'utf8'));
@@ -19,6 +20,7 @@ export function diagnoseProject({ repository, env = process.env, now = () => new
const configPath = join(trackedRoot, 'torch.yaml');
const manifestPath = join(trackedRoot, 'install-manifest.json');
const findings = [];
let identityRows = [];
if (!existsSync(configPath)) {
return { status: 'not-installed', healthy: false, findings: [{ severity: 'error', code: 'CONFIG_MISSING', path: configPath }] };
@@ -78,10 +80,11 @@ export function diagnoseProject({ repository, env = process.env, now = () => new
LEFT JOIN message_acks a ON a.message_id = m.id AND a.area_id = i.area_id
WHERE a.message_id IS NULL
`).get().count;
const present = database.prepare(`
identityRows = database.prepare(`
SELECT area_id, state, runtime, runtime_session_id, heartbeat_at
FROM identities WHERE state != 'offline' ORDER BY area_id
FROM identities ORDER BY area_id
`).all();
const present = identityRows.filter((identity) => identity.state !== 'offline');
if (unacknowledged > 0) {
findings.push({ severity: 'warning', code: 'MESSAGE_BACKLOG', unacknowledged });
}
@@ -167,19 +170,33 @@ export function diagnoseProject({ repository, env = process.env, now = () => new
try {
const task = parseJsonYaml(join(backlogRoot, name));
if (task.schema !== 'torch.dev/backlog-item/v1alpha1' || !task.id || !task.state) throw new Error('invalid task schema');
if (!['completed', 'cancelled'].includes(task.state)) {
tasks.push({ id: task.id, state: task.state, owner: task.owner, revision: task.revision });
}
tasks.push(task);
} catch (error) {
findings.push({
severity: 'error', code: 'BACKLOG_TASK_INVALID', path: join(backlogRoot, name), message: error.message,
});
}
}
if (tasks.length) findings.push({
severity: tasks.some((task) => task.state === 'blocked') ? 'warning' : 'info',
code: 'BACKLOG_ACTIVITY', tasks,
const activeTasks = tasks.filter((task) => !['completed', 'cancelled'].includes(task.state));
if (activeTasks.length) findings.push({
severity: activeTasks.some((task) => task.state === 'blocked') ? 'warning' : 'info',
code: 'BACKLOG_ACTIVITY', tasks: activeTasks.map((task) => ({
id: task.id, state: task.state, owner: task.owner, revision: task.revision,
})),
});
try {
const roster = parseJsonYaml(join(trackedRoot, 'roster.yaml'));
const presenceByArea = new Map(identityRows.map((identity) => [identity.area_id, identity]));
const agents = (roster.areas ?? []).map((area) => ({
...area, areaId: area.id, ...(presenceByArea.get(area.id) ?? { state: 'offline' }),
}));
findings.push(...assessBacklogHealth({
tasks, agents, now: now(),
commitDistance: (observedAt) => backlogObservedCommitDistance(repository.root, observedAt),
}).findings);
} catch (error) {
findings.push({ severity: 'error', code: 'BACKLOG_HEALTH_INVALID', message: error.message });
}
}
}
}
+1 -1
View File
@@ -133,7 +133,7 @@ function initialFiles({ repository, projectId, createdAt, proposal, runtimes })
const managerPrompt = `${promptContent(
proposal.session_manager?.prompt_source,
'# TORCH Session Manager\n\nRoute owner requests, establish ownership and priority, and keep routine coordination inside the fleet.\n',
).trimEnd()}\n\n## Fleet evolution\n\nAt startup, after backlog intake, and when repeated handoffs or cross-domain work appear, call \`torch_assess_fleet_evolution\`. Treat its threshold as a prompt for architectural judgment, not as an automatic decision. When recurring work has no coherent owner, or a durable specialist would materially improve context locality, ownership clarity, or verification, inspect repository evidence and use \`torch_propose_domain\` to submit an evidence-backed Fleet change. When a specialist no longer earns its coordination cost, use \`torch_propose_domain_retirement\`; retirement preserves its branch and refuses active or unrecoverable work. Use \`torch_list_fleet_changes\` to follow change state. Never create, retire, approve, activate, or start a persistent identity yourself; owner approval, CLI activation, and quota-consuming runtime start are separate. Recommend a merge or split for owner review when boundaries should change but do not silently rewrite ownership.\n`;
).trimEnd()}\n\n## Backlog loop\n\nKeep one durable Fleet backlog. Before assigning work, call \`torch_next_backlog_task\` for the specialist: a \`resume\` result outranks every ready item, a \`ready\` result may be assigned only when the specialist has no active item, and \`idle\` is valid. After completion, repeat the same lookup. Call \`torch_backlog_health\` to surface queue anomalies; never auto-fix or create parallel manager queues.\n\n## Fleet evolution\n\nAt startup, after backlog intake, and when repeated handoffs or cross-domain work appear, call \`torch_assess_fleet_evolution\`. Treat its threshold as a prompt for architectural judgment, not as an automatic decision. When recurring work has no coherent owner, or a durable specialist would materially improve context locality, ownership clarity, or verification, inspect repository evidence and use \`torch_propose_domain\` to submit an evidence-backed Fleet change. When a specialist no longer earns its coordination cost, use \`torch_propose_domain_retirement\`; retirement preserves its branch and refuses active or unrecoverable work. Use \`torch_list_fleet_changes\` to follow change state. Never create, retire, approve, activate, or start a persistent identity yourself; owner approval, CLI activation, and quota-consuming runtime start are separate. Recommend a merge or split for owner review when boundaries should change but do not silently rewrite ownership.\n`;
const files = new Map([
['torch.yaml', jsonYaml(config)],
['roster.yaml', jsonYaml(roster)],
+5 -2
View File
@@ -52,11 +52,14 @@ export function renderDomainPrompt(domain) {
'',
'## Initial backlog',
'',
'- No task is implied by session creation. Read the tracked backlog and accept only an explicit assignment.',
'- No task is implied by session creation. Call `torch_next_backlog_task` for this area before choosing work.',
'- A `resume` result is the only current assignment. Continue it before considering any `ready` item.',
'- A `ready` result is a candidate, not an assignment; request Session Manager assignment before implementation.',
'- An `idle` result is valid. Do not invent filler work.',
'',
'## First move',
'',
'Query live identity and ownership, read the assigned backlog item and inbox, then report current repository evidence to the Session Manager.',
'Query live identity and ownership, call `torch_next_backlog_task`, read the inbox, then report current repository evidence to the Session Manager.',
'',
].join('\n');
}