feat: add orchestration workflows and harness skills

This commit is contained in:
Affaan Mustafa
2026-03-12 08:50:24 -07:00
parent bfc73866c9
commit 2349e21731
46 changed files with 5618 additions and 80 deletions

View File

@@ -360,22 +360,30 @@ async function runTests() {
if (
await asyncTest('creates or updates session file', async () => {
// Run the script
await runScript(path.join(scriptsDir, 'session-end.js'));
const isoHome = path.join(os.tmpdir(), `ecc-session-create-${Date.now()}`);
// Check if session file was created
// Note: Without CLAUDE_SESSION_ID, falls back to project name (not 'default')
// Use local time to match the script's getDateString() function
const sessionsDir = path.join(os.homedir(), '.claude', 'sessions');
const now = new Date();
const today = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
try {
await runScript(path.join(scriptsDir, 'session-end.js'), '', {
HOME: isoHome,
USERPROFILE: isoHome
});
// Get the expected session ID (project name fallback)
const utils = require('../../scripts/lib/utils');
const expectedId = utils.getSessionIdShort();
const sessionFile = path.join(sessionsDir, `${today}-${expectedId}-session.tmp`);
// Check if session file was created
// Note: Without CLAUDE_SESSION_ID, falls back to project/worktree name (not 'default')
// Use local time to match the script's getDateString() function
const sessionsDir = path.join(isoHome, '.claude', 'sessions');
const now = new Date();
const today = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
assert.ok(fs.existsSync(sessionFile), `Session file should exist: ${sessionFile}`);
// Get the expected session ID (project name fallback)
const utils = require('../../scripts/lib/utils');
const expectedId = utils.getSessionIdShort();
const sessionFile = path.join(sessionsDir, `${today}-${expectedId}-session.tmp`);
assert.ok(fs.existsSync(sessionFile), `Session file should exist: ${sessionFile}`);
} finally {
fs.rmSync(isoHome, { recursive: true, force: true });
}
})
)
passed++;
@@ -404,6 +412,39 @@ async function runTests() {
passed++;
else failed++;
if (
await asyncTest('writes project, branch, and worktree metadata into new session files', async () => {
const isoHome = path.join(os.tmpdir(), `ecc-session-metadata-${Date.now()}`);
const testSessionId = 'test-session-meta1234';
const expectedShortId = testSessionId.slice(-8);
const topLevel = spawnSync('git', ['rev-parse', '--show-toplevel'], { encoding: 'utf8' }).stdout.trim();
const branch = spawnSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { encoding: 'utf8' }).stdout.trim();
const project = path.basename(topLevel);
try {
const result = await runScript(path.join(scriptsDir, 'session-end.js'), '', {
HOME: isoHome,
USERPROFILE: isoHome,
CLAUDE_SESSION_ID: testSessionId
});
assert.strictEqual(result.code, 0, 'Hook should exit 0');
const now = new Date();
const today = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
const sessionFile = path.join(isoHome, '.claude', 'sessions', `${today}-${expectedShortId}-session.tmp`);
const content = fs.readFileSync(sessionFile, 'utf8');
assert.ok(content.includes(`**Project:** ${project}`), 'Should persist project metadata');
assert.ok(content.includes(`**Branch:** ${branch}`), 'Should persist branch metadata');
assert.ok(content.includes(`**Worktree:** ${process.cwd()}`), 'Should persist worktree metadata');
} finally {
fs.rmSync(isoHome, { recursive: true, force: true });
}
})
)
passed++;
else failed++;
// pre-compact.js tests
console.log('\npre-compact.js:');
@@ -1218,7 +1259,10 @@ async function runTests() {
fs.writeFileSync(transcriptPath, lines.join('\n'));
const stdinJson = JSON.stringify({ transcript_path: transcriptPath });
const result = await runScript(path.join(scriptsDir, 'session-end.js'), stdinJson);
const result = await runScript(path.join(scriptsDir, 'session-end.js'), stdinJson, {
HOME: testDir,
USERPROFILE: testDir
});
assert.strictEqual(result.code, 0);
// Session file should contain summary with tools used
assert.ok(result.stderr.includes('Created session file') || result.stderr.includes('Updated session file'), 'Should create/update session file');
@@ -2448,6 +2492,42 @@ async function runTests() {
passed++;
else failed++;
if (
await asyncTest('normalizes existing session headers with project, branch, and worktree metadata', async () => {
const testDir = createTestDir();
const sessionsDir = path.join(testDir, '.claude', 'sessions');
fs.mkdirSync(sessionsDir, { recursive: true });
const utils = require('../../scripts/lib/utils');
const today = utils.getDateString();
const shortId = 'update04';
const sessionFile = path.join(sessionsDir, `${today}-${shortId}-session.tmp`);
const branch = spawnSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { encoding: 'utf8' }).stdout.trim();
const project = path.basename(spawnSync('git', ['rev-parse', '--show-toplevel'], { encoding: 'utf8' }).stdout.trim());
fs.writeFileSync(
sessionFile,
`# Session: ${today}\n**Date:** ${today}\n**Started:** 09:00\n**Last Updated:** 09:00\n\n---\n\n## Current State\n\n[Session context goes here]\n`
);
const result = await runScript(path.join(scriptsDir, 'session-end.js'), '', {
HOME: testDir,
USERPROFILE: testDir,
CLAUDE_SESSION_ID: `session-${shortId}`
});
assert.strictEqual(result.code, 0);
const updated = fs.readFileSync(sessionFile, 'utf8');
assert.ok(updated.includes(`**Project:** ${project}`), 'Should inject project metadata into existing headers');
assert.ok(updated.includes(`**Branch:** ${branch}`), 'Should inject branch metadata into existing headers');
assert.ok(updated.includes(`**Worktree:** ${process.cwd()}`), 'Should inject worktree metadata into existing headers');
cleanupTestDir(testDir);
})
)
passed++;
else failed++;
if (
await asyncTest('replaces blank template with summary when updating existing file', async () => {
const testDir = createTestDir();
@@ -3815,6 +3895,8 @@ async function runTests() {
try {
const result = await runScript(path.join(scriptsDir, 'session-end.js'), oversizedPayload, {
HOME: testDir,
USERPROFILE: testDir,
CLAUDE_TRANSCRIPT_PATH: transcriptPath
});
assert.strictEqual(result.code, 0, 'Should exit 0 even with oversized stdin');

View File

@@ -0,0 +1,214 @@
'use strict';
const assert = require('assert');
const fs = require('fs');
const os = require('os');
const path = require('path');
const {
buildSessionSnapshot,
loadWorkerSnapshots,
parseWorkerHandoff,
parseWorkerStatus,
parseWorkerTask,
resolveSnapshotTarget
} = require('../../scripts/lib/orchestration-session');
console.log('=== Testing orchestration-session.js ===\n');
let passed = 0;
let failed = 0;
function test(desc, fn) {
try {
fn();
console.log(`${desc}`);
passed++;
} catch (error) {
console.log(`${desc}: ${error.message}`);
failed++;
}
}
test('parseWorkerStatus extracts structured status fields', () => {
const status = parseWorkerStatus([
'# Status',
'',
'- State: completed',
'- Updated: 2026-03-12T14:09:15Z',
'- Branch: feature-branch',
'- Worktree: `/tmp/worktree`',
'',
'- Handoff file: `/tmp/handoff.md`'
].join('\n'));
assert.deepStrictEqual(status, {
state: 'completed',
updated: '2026-03-12T14:09:15Z',
branch: 'feature-branch',
worktree: '/tmp/worktree',
taskFile: null,
handoffFile: '/tmp/handoff.md'
});
});
test('parseWorkerTask extracts objective and seeded overlays', () => {
const task = parseWorkerTask([
'# Worker Task',
'',
'## Seeded Local Overlays',
'- `scripts/orchestrate-worktrees.js`',
'- `commands/orchestrate.md`',
'',
'## Objective',
'Verify seeded files and summarize status.'
].join('\n'));
assert.deepStrictEqual(task.seedPaths, [
'scripts/orchestrate-worktrees.js',
'commands/orchestrate.md'
]);
assert.strictEqual(task.objective, 'Verify seeded files and summarize status.');
});
test('parseWorkerHandoff extracts summary, validation, and risks', () => {
const handoff = parseWorkerHandoff([
'# Handoff',
'',
'## Summary',
'- Worker completed successfully',
'',
'## Validation',
'- Ran tests',
'',
'## Remaining Risks',
'- No runtime screenshot'
].join('\n'));
assert.deepStrictEqual(handoff.summary, ['Worker completed successfully']);
assert.deepStrictEqual(handoff.validation, ['Ran tests']);
assert.deepStrictEqual(handoff.remainingRisks, ['No runtime screenshot']);
});
test('parseWorkerHandoff also supports bold section headers', () => {
const handoff = parseWorkerHandoff([
'# Handoff',
'',
'**Summary**',
'- Worker completed successfully',
'',
'**Validation**',
'- Ran tests',
'',
'**Remaining Risks**',
'- No runtime screenshot'
].join('\n'));
assert.deepStrictEqual(handoff.summary, ['Worker completed successfully']);
assert.deepStrictEqual(handoff.validation, ['Ran tests']);
assert.deepStrictEqual(handoff.remainingRisks, ['No runtime screenshot']);
});
test('loadWorkerSnapshots reads coordination worker directories', () => {
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'ecc-orch-session-'));
const coordinationDir = path.join(tempRoot, 'coordination');
const workerDir = path.join(coordinationDir, 'seed-check');
const proofDir = path.join(coordinationDir, 'proof');
fs.mkdirSync(workerDir, { recursive: true });
fs.mkdirSync(proofDir, { recursive: true });
try {
fs.writeFileSync(path.join(workerDir, 'status.md'), [
'# Status',
'',
'- State: running',
'- Branch: seed-branch',
'- Worktree: `/tmp/seed-worktree`'
].join('\n'));
fs.writeFileSync(path.join(workerDir, 'task.md'), [
'# Worker Task',
'',
'## Objective',
'Inspect seed paths.'
].join('\n'));
fs.writeFileSync(path.join(workerDir, 'handoff.md'), [
'# Handoff',
'',
'## Summary',
'- Pending'
].join('\n'));
const workers = loadWorkerSnapshots(coordinationDir);
assert.strictEqual(workers.length, 1);
assert.strictEqual(workers[0].workerSlug, 'seed-check');
assert.strictEqual(workers[0].status.branch, 'seed-branch');
assert.strictEqual(workers[0].task.objective, 'Inspect seed paths.');
} finally {
fs.rmSync(tempRoot, { recursive: true, force: true });
}
});
test('buildSessionSnapshot merges tmux panes with worker metadata', () => {
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'ecc-orch-snapshot-'));
const coordinationDir = path.join(tempRoot, 'coordination');
const workerDir = path.join(coordinationDir, 'seed-check');
fs.mkdirSync(workerDir, { recursive: true });
try {
fs.writeFileSync(path.join(workerDir, 'status.md'), '- State: completed\n- Branch: seed-branch\n');
fs.writeFileSync(path.join(workerDir, 'task.md'), '## Objective\nInspect seed paths.\n');
fs.writeFileSync(path.join(workerDir, 'handoff.md'), '## Summary\n- ok\n');
const snapshot = buildSessionSnapshot({
sessionName: 'workflow-visual-proof',
coordinationDir,
panes: [
{
paneId: '%95',
windowIndex: 1,
paneIndex: 2,
title: 'seed-check',
currentCommand: 'codex',
currentPath: '/tmp/worktree',
active: false,
dead: false,
pid: 1234
}
]
});
assert.strictEqual(snapshot.sessionActive, true);
assert.strictEqual(snapshot.workerCount, 1);
assert.strictEqual(snapshot.workerStates.completed, 1);
assert.strictEqual(snapshot.workers[0].pane.paneId, '%95');
} finally {
fs.rmSync(tempRoot, { recursive: true, force: true });
}
});
test('resolveSnapshotTarget handles plan files and direct session names', () => {
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'ecc-orch-target-'));
const repoRoot = path.join(tempRoot, 'repo');
fs.mkdirSync(repoRoot, { recursive: true });
const planPath = path.join(repoRoot, 'plan.json');
fs.writeFileSync(planPath, JSON.stringify({
sessionName: 'workflow-visual-proof',
repoRoot,
coordinationRoot: path.join(repoRoot, '.claude', 'orchestration')
}));
try {
const fromPlan = resolveSnapshotTarget(planPath, repoRoot);
assert.strictEqual(fromPlan.targetType, 'plan');
assert.strictEqual(fromPlan.sessionName, 'workflow-visual-proof');
const fromSession = resolveSnapshotTarget('workflow-visual-proof', repoRoot);
assert.strictEqual(fromSession.targetType, 'session');
assert.ok(fromSession.coordinationDir.endsWith(path.join('.claude', 'orchestration', 'workflow-visual-proof')));
} finally {
fs.rmSync(tempRoot, { recursive: true, force: true });
}
});
console.log(`\n=== Results: ${passed} passed, ${failed} failed ===`);
if (failed > 0) process.exit(1);

View File

@@ -94,6 +94,9 @@ function runTests() {
**Date:** 2026-02-01
**Started:** 10:30
**Last Updated:** 14:45
**Project:** everything-claude-code
**Branch:** feature/session-metadata
**Worktree:** /tmp/ecc-worktree
### Completed
- [x] Set up project
@@ -114,6 +117,9 @@ src/main.ts
assert.strictEqual(meta.date, '2026-02-01');
assert.strictEqual(meta.started, '10:30');
assert.strictEqual(meta.lastUpdated, '14:45');
assert.strictEqual(meta.project, 'everything-claude-code');
assert.strictEqual(meta.branch, 'feature/session-metadata');
assert.strictEqual(meta.worktree, '/tmp/ecc-worktree');
assert.strictEqual(meta.completed.length, 2);
assert.strictEqual(meta.completed[0], 'Set up project');
assert.strictEqual(meta.inProgress.length, 1);

View File

@@ -0,0 +1,233 @@
'use strict';
const assert = require('assert');
const fs = require('fs');
const os = require('os');
const path = require('path');
const {
slugify,
renderTemplate,
buildOrchestrationPlan,
materializePlan,
normalizeSeedPaths,
overlaySeedPaths
} = require('../../scripts/lib/tmux-worktree-orchestrator');
console.log('=== Testing tmux-worktree-orchestrator.js ===\n');
let passed = 0;
let failed = 0;
function test(desc, fn) {
try {
fn();
console.log(`${desc}`);
passed++;
} catch (error) {
console.log(`${desc}: ${error.message}`);
failed++;
}
}
console.log('Helpers:');
test('slugify normalizes mixed punctuation and casing', () => {
assert.strictEqual(slugify('Feature Audit: Docs + Tmux'), 'feature-audit-docs-tmux');
});
test('renderTemplate replaces supported placeholders', () => {
const rendered = renderTemplate('run {worker_name} in {worktree_path}', {
worker_name: 'Docs Fixer',
worktree_path: '/tmp/repo-worker'
});
assert.strictEqual(rendered, 'run Docs Fixer in /tmp/repo-worker');
});
test('renderTemplate rejects unknown placeholders', () => {
assert.throws(
() => renderTemplate('missing {unknown}', { worker_name: 'docs' }),
/Unknown template variable/
);
});
console.log('\nPlan generation:');
test('buildOrchestrationPlan creates worktrees, branches, and tmux commands', () => {
const repoRoot = path.join('/tmp', 'ecc');
const plan = buildOrchestrationPlan({
repoRoot,
sessionName: 'Skill Audit',
baseRef: 'main',
launcherCommand: 'codex exec --cwd {worktree_path} --task-file {task_file}',
workers: [
{ name: 'Docs A', task: 'Fix skills 1-4' },
{ name: 'Docs B', task: 'Fix skills 5-8' }
]
});
assert.strictEqual(plan.sessionName, 'skill-audit');
assert.strictEqual(plan.workerPlans.length, 2);
assert.strictEqual(plan.workerPlans[0].branchName, 'orchestrator-skill-audit-docs-a');
assert.strictEqual(plan.workerPlans[1].branchName, 'orchestrator-skill-audit-docs-b');
assert.deepStrictEqual(
plan.workerPlans[0].gitArgs.slice(0, 4),
['worktree', 'add', '-b', 'orchestrator-skill-audit-docs-a'],
'Should create branch-backed worktrees'
);
assert.ok(
plan.workerPlans[0].worktreePath.endsWith(path.join('ecc-skill-audit-docs-a')),
'Should create sibling worktree path'
);
assert.ok(
plan.workerPlans[0].taskFilePath.endsWith(path.join('.orchestration', 'skill-audit', 'docs-a', 'task.md')),
'Should create per-worker task file'
);
assert.ok(
plan.workerPlans[0].handoffFilePath.endsWith(path.join('.orchestration', 'skill-audit', 'docs-a', 'handoff.md')),
'Should create per-worker handoff file'
);
assert.ok(
plan.workerPlans[0].launchCommand.includes(plan.workerPlans[0].taskFilePath),
'Launch command should interpolate task file'
);
assert.ok(
plan.workerPlans[0].launchCommand.includes(plan.workerPlans[0].worktreePath),
'Launch command should interpolate worktree path'
);
assert.ok(
plan.tmuxCommands.some(command => command.args.includes('split-window')),
'Should include tmux split commands'
);
assert.ok(
plan.tmuxCommands.some(command => command.args.includes('select-layout')),
'Should include tiled layout command'
);
});
test('buildOrchestrationPlan requires at least one worker', () => {
assert.throws(
() => buildOrchestrationPlan({
repoRoot: '/tmp/ecc',
sessionName: 'empty',
launcherCommand: 'codex exec --task-file {task_file}',
workers: []
}),
/at least one worker/
);
});
test('buildOrchestrationPlan normalizes global and worker seed paths', () => {
const plan = buildOrchestrationPlan({
repoRoot: '/tmp/ecc',
sessionName: 'seeded',
launcherCommand: 'echo run',
seedPaths: ['scripts/orchestrate-worktrees.js', './.claude/plan/workflow-e2e-test.json'],
workers: [
{
name: 'Docs',
task: 'Update docs',
seedPaths: ['commands/multi-workflow.md']
}
]
});
assert.deepStrictEqual(plan.workerPlans[0].seedPaths, [
'scripts/orchestrate-worktrees.js',
'.claude/plan/workflow-e2e-test.json',
'commands/multi-workflow.md'
]);
});
test('normalizeSeedPaths rejects paths outside the repo root', () => {
assert.throws(
() => normalizeSeedPaths(['../outside.txt'], '/tmp/ecc'),
/inside repoRoot/
);
});
test('materializePlan keeps worker instructions inside the worktree boundary', () => {
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'ecc-orchestrator-test-'));
try {
const plan = buildOrchestrationPlan({
repoRoot: tempRoot,
coordinationRoot: path.join(tempRoot, '.claude', 'orchestration'),
sessionName: 'Workflow E2E',
launcherCommand: 'bash {repo_root}/scripts/orchestrate-codex-worker.sh {task_file} {handoff_file} {status_file}',
workers: [{ name: 'Docs', task: 'Update the workflow docs.' }]
});
materializePlan(plan);
const taskFile = fs.readFileSync(plan.workerPlans[0].taskFilePath, 'utf8');
assert.ok(
taskFile.includes('Report results in your final response.'),
'Task file should tell the worker to report in stdout'
);
assert.ok(
taskFile.includes('Do not spawn subagents or external agents for this task.'),
'Task file should keep nested workers single-session'
);
assert.ok(
!taskFile.includes('Write results and handoff notes to'),
'Task file should not require writing handoff files outside the worktree'
);
assert.ok(
!taskFile.includes('Update `'),
'Task file should not instruct the nested worker to update orchestration status files'
);
} finally {
fs.rmSync(tempRoot, { recursive: true, force: true });
}
});
test('overlaySeedPaths copies local overlays into the worker worktree', () => {
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'ecc-orchestrator-overlay-'));
const repoRoot = path.join(tempRoot, 'repo');
const worktreePath = path.join(tempRoot, 'worktree');
try {
fs.mkdirSync(path.join(repoRoot, 'scripts'), { recursive: true });
fs.mkdirSync(path.join(repoRoot, '.claude', 'plan'), { recursive: true });
fs.mkdirSync(path.join(worktreePath, 'scripts'), { recursive: true });
fs.writeFileSync(
path.join(repoRoot, 'scripts', 'orchestrate-worktrees.js'),
'local-version\n',
'utf8'
);
fs.writeFileSync(
path.join(repoRoot, '.claude', 'plan', 'workflow-e2e-test.json'),
'{"seeded":true}\n',
'utf8'
);
fs.writeFileSync(
path.join(worktreePath, 'scripts', 'orchestrate-worktrees.js'),
'head-version\n',
'utf8'
);
overlaySeedPaths({
repoRoot,
seedPaths: [
'scripts/orchestrate-worktrees.js',
'.claude/plan/workflow-e2e-test.json'
],
worktreePath
});
assert.strictEqual(
fs.readFileSync(path.join(worktreePath, 'scripts', 'orchestrate-worktrees.js'), 'utf8'),
'local-version\n'
);
assert.strictEqual(
fs.readFileSync(path.join(worktreePath, '.claude', 'plan', 'workflow-e2e-test.json'), 'utf8'),
'{"seeded":true}\n'
);
} finally {
fs.rmSync(tempRoot, { recursive: true, force: true });
}
});
console.log(`\n=== Results: ${passed} passed, ${failed} failed ===`);
if (failed > 0) process.exit(1);