ktx/packages/context/src/ingest/isolated-diff/work-unit-executor.test.ts
Andrey Avtomonov e64da5a85d
feat(ingest): default local ingest to isolated diffs (#128)
* docs: add isolated-diff ingestion design

* Refine isolated-diff ingestion design after adversarial review iteration 1

* Refine isolated-diff ingestion design after adversarial review iteration 2

* Refine isolated-diff ingestion design after adversarial review iteration 3

* feat: persist ingest trace events

* feat: add isolated ingest patch helpers

* feat: validate wiki body semantic references

* feat: add final ingest artifact gates

* feat: execute ingest work units in child worktrees

* feat: integrate isolated work unit patches

* feat: route selected ingest sources through isolated diffs

* test: cover isolated diff ingestion regressions

* feat: add isolated diff ingestion v1 core

* docs: document ingest trace inspection

* docs: add isolated diff ingestion v1 core plan

* fix(ingest): tighten final artifact gates

* fix(ingest): gate isolated final integration tree

* fix(ingest): persist postmortem failure traces

* fix(ingest): trace policy conflicts and cleanup child worktrees

* test(ingest): verify isolated diff postmortem coverage

* docs: add isolated diff ingestion gates and trace closure plan

* fix(ingest): gate provenance before isolated diff squash

* docs: add isolated diff ingestion provenance gate closure plan

* fix(ingest): gate final wiki references

* fix(ingest): enforce SL target connection scope

* fix(ingest): trace isolated SL target policy gates

* test(ingest): cover isolated diff reference and target gates

* chore(ingest): verify isolated diff gate closure

* docs: add isolated diff ingestion reference and target gate closure plan

* fix(ingest): gate global wiki references

* docs: add isolated diff ingestion global wiki reference gate closure plan

* fix(ingest): validate scan sources and wiki refs

* test(ingest): cover isolated diff textual conflict resolver

* test(ingest): cover isolated diff resolver integration

* feat(ingest): repair isolated diff textual conflicts

* feat(ingest): report isolated diff resolver outcomes

* test(ingest): verify isolated diff textual conflict repair

* test(ingest): align textual conflict failure coverage

* docs: add isolated diff textual conflict resolver plan

* test(ingest): cover isolated diff gate repair

* feat(ingest): add isolated diff gate repair agent

* feat(ingest): repair isolated diff semantic gate failures

* feat(ingest): wire isolated diff gate repair

* test(ingest): verify isolated diff final gate repair

* chore(ingest): verify isolated diff gate repair

* docs: add isolated diff gate repair plan

* Improve ingest progress updates

* feat(ingest): route direct-write connectors through isolated diffs

* test(ingest): cover non-metabase isolated diff routing

* feat(ingest): project metricflow semantic models before work units

* test(ingest): verify metricflow isolated projection path

* chore(ingest): verify isolated diff connector migration

* docs: add isolated diff connector migration plan

* feat(ingest): make isolated diff routing the private default

* feat(ingest): promote isolated diff to default runner path

* feat(ingest): default local ingest to isolated diffs

* chore(ingest): remove isolated diff allowlist references

* fix(ingest): preserve transient evidence for isolated work units

* docs: add isolated diff default promotion plan

* refactor(ingest): remove shared worktree WorkUnit path

* docs(ingest): align WorkUnit prompts with isolated diffs

* test(ingest): drop unused runner import

* docs: add isolated diff shared worktree removal plan

* docs: add isolated diff gate repair classification plan

* fix: restrict claude-code mcp servers

* docs: align ingest trace guidance with public CLI

---------

Co-authored-by: Andrey Avtomonov <7889985+andreybavt@users.noreply.github.com>
2026-05-18 13:38:06 +02:00

144 lines
5.5 KiB
TypeScript

import { mkdir, mkdtemp, readFile, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { describe, expect, it, vi } from 'vitest';
import { GitService } from '../../core/index.js';
import { FileIngestTraceWriter } from '../ingest-trace.js';
import { runIsolatedWorkUnit } from './work-unit-executor.js';
async function makeGit() {
const homeDir = await mkdtemp(join(tmpdir(), 'ktx-isolated-wu-'));
const configDir = join(homeDir, 'config');
const git = new GitService({
storage: { configDir, homeDir },
git: {
userName: 'System User',
userEmail: 'system@example.com',
bootstrapMessage: 'init',
bootstrapAuthor: 'system',
bootstrapAuthorEmail: 'system@example.com',
},
});
await git.onModuleInit();
await mkdir(join(configDir, 'raw-sources/c1/fake/s'), { recursive: true });
await writeFile(join(configDir, 'raw-sources/c1/fake/s/a.json'), '{}\n');
await git.commitFiles(['raw-sources/c1/fake/s/a.json'], 'raw snapshot', 'System User', 'system@example.com');
return { homeDir, configDir, git, baseSha: await git.revParseHead() };
}
describe('runIsolatedWorkUnit', () => {
it('creates a child worktree at the ingestion base and persists a patch proposal', async () => {
const { homeDir, git, baseSha } = await makeGit();
const childDir = join(homeDir, '.worktrees/session-job-1-wu-1');
const sessionWorktreeService = {
create: vi.fn(async (_key: string, startSha: string) => {
await mkdir(join(homeDir, '.worktrees'), { recursive: true });
await git.addWorktree(childDir, 'session/job-1-wu-1', startSha);
const childGit = git.forWorktree(childDir);
return {
chatId: 'job-1-wu-1',
workdir: childDir,
branch: 'session/job-1-wu-1',
baseSha: startSha,
createdAt: new Date(),
git: childGit,
config: {},
};
}),
cleanup: vi.fn(async () => undefined),
};
const tracePath = join(homeDir, '.ktx/ingest-traces/job-1/trace.jsonl');
const trace = new FileIngestTraceWriter({
tracePath,
jobId: 'job-1',
connectionId: 'c1',
sourceKey: 'fake',
level: 'trace',
});
const result = await runIsolatedWorkUnit({
unitIndex: 0,
ingestionBaseSha: baseSha,
sessionWorktreeService: sessionWorktreeService as never,
patchDir: join(homeDir, '.ktx/ingest-patches/job-1'),
trace,
run: async (child) => {
await mkdir(join(child.workdir, 'wiki/global'), { recursive: true });
await writeFile(join(child.workdir, 'wiki/global/a.md'), '---\nsummary: A\nusage_mode: auto\n---\n\nBody\n');
await child.git.commitFiles(['wiki/global/a.md'], 'test: write wiki', 'KTX Test', 'system@ktx.local');
return {
unitKey: 'wu-1',
status: 'success',
preSha: baseSha,
postSha: await child.git.revParseHead(),
actions: [{ target: 'wiki', type: 'created', key: 'a', detail: 'A' }],
touchedSlSources: [],
};
},
workUnit: { unitKey: 'wu-1', rawFiles: ['a.json'], peerFileIndex: [], dependencyPaths: [] },
});
expect(sessionWorktreeService.create).toHaveBeenCalledWith('job-1-wu-1', baseSha);
expect(sessionWorktreeService.cleanup).toHaveBeenCalledWith(expect.any(Object), 'success');
expect(result.status).toBe('success');
if (result.status !== 'success') {
throw new Error('expected successful work unit');
}
const patchPath = result.patchPath;
if (!patchPath) {
throw new Error('expected patch path');
}
expect(patchPath).toContain('0000-wu-1.patch');
await expect(readFile(patchPath, 'utf-8')).resolves.toContain('wiki/global/a.md');
await expect(readFile(tracePath, 'utf-8')).resolves.toContain('work_unit_child_created');
});
it('removes child worktrees after failed WorkUnit outcomes are traced', async () => {
const { homeDir, git, baseSha } = await makeGit();
const childDir = join(homeDir, '.worktrees/session-job-1-wu-fail');
const sessionWorktreeService = {
create: vi.fn(async (_key: string, startSha: string) => {
await mkdir(join(homeDir, '.worktrees'), { recursive: true });
await git.addWorktree(childDir, 'session/job-1-wu-fail', startSha);
return {
chatId: 'job-1-wu-fail',
workdir: childDir,
branch: 'session/job-1-wu-fail',
baseSha: startSha,
createdAt: new Date(),
git: git.forWorktree(childDir),
config: {},
};
}),
cleanup: vi.fn(async () => undefined),
};
const trace = new FileIngestTraceWriter({
tracePath: join(homeDir, '.ktx/ingest-traces/job-1/trace.jsonl'),
jobId: 'job-1',
connectionId: 'c1',
sourceKey: 'fake',
level: 'trace',
});
const result = await runIsolatedWorkUnit({
unitIndex: 0,
ingestionBaseSha: baseSha,
sessionWorktreeService: sessionWorktreeService as never,
patchDir: join(homeDir, '.ktx/ingest-patches/job-1'),
trace,
run: async () => ({
unitKey: 'wu-fail',
status: 'failed',
reason: 'agent loop errored',
preSha: baseSha,
postSha: baseSha,
actions: [],
touchedSlSources: [],
}),
workUnit: { unitKey: 'wu-fail', rawFiles: ['a.json'], peerFileIndex: [], dependencyPaths: [] },
});
expect(result.status).toBe('failed');
expect(sessionWorktreeService.cleanup).toHaveBeenCalledWith(expect.any(Object), 'success');
});
});