@kaelio/ktx 0.1.0-rc.5 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/python/kaelio_ktx-0.1.0-py3-none-any.whl +0 -0
- package/assets/python/manifest.json +2 -2
- package/dist/clack.d.ts +6 -0
- package/dist/clack.js +23 -0
- package/dist/cli-program.js +5 -2
- package/dist/cli-program.test.js +7 -1
- package/dist/cli-runtime.d.ts +4 -0
- package/dist/cli-runtime.js +8 -1
- package/dist/command-schemas.d.ts +1 -1
- package/dist/commands/ingest-commands.js +1 -0
- package/dist/commands/knowledge-commands.js +5 -0
- package/dist/commands/mcp-commands.js +11 -3
- package/dist/commands/mcp-commands.test.js +30 -1
- package/dist/commands/sql-commands.d.ts +3 -0
- package/dist/commands/sql-commands.js +43 -0
- package/dist/commands/sql-commands.test.d.ts +1 -0
- package/dist/commands/sql-commands.test.js +68 -0
- package/dist/context-build-view.js +5 -1
- package/dist/dev.test.js +27 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.test.js +56 -21
- package/dist/ingest.js +123 -18
- package/dist/ingest.test.js +206 -0
- package/dist/io/print-list.d.ts +2 -1
- package/dist/io/print-list.js +7 -0
- package/dist/io/print-list.test.js +13 -11
- package/dist/io/symbols.d.ts +2 -2
- package/dist/knowledge.d.ts +1 -0
- package/dist/knowledge.js +34 -16
- package/dist/knowledge.test.js +27 -0
- package/dist/managed-python-command.d.ts +2 -0
- package/dist/managed-python-command.js +17 -9
- package/dist/managed-python-command.test.js +59 -4
- package/dist/next-steps.js +1 -1
- package/dist/next-steps.test.js +2 -0
- package/dist/print-command-tree.js +7 -1
- package/dist/public-ingest.d.ts +9 -1
- package/dist/public-ingest.js +50 -7
- package/dist/public-ingest.test.js +69 -2
- package/dist/release-version.d.ts +5 -0
- package/dist/release-version.js +44 -0
- package/dist/runtime-requirements.d.ts +23 -0
- package/dist/runtime-requirements.js +99 -0
- package/dist/runtime-requirements.test.d.ts +1 -0
- package/dist/runtime-requirements.test.js +63 -0
- package/dist/setup-agents.d.ts +11 -3
- package/dist/setup-agents.js +397 -134
- package/dist/setup-agents.test.js +359 -61
- package/dist/setup-embeddings.js +3 -6
- package/dist/setup-embeddings.test.js +18 -2
- package/dist/setup-models.js +2 -2
- package/dist/setup-models.test.js +5 -3
- package/dist/setup-ready-menu.d.ts +1 -1
- package/dist/setup-ready-menu.js +2 -0
- package/dist/setup-ready-menu.test.js +3 -0
- package/dist/setup-runtime.d.ts +45 -0
- package/dist/setup-runtime.js +47 -0
- package/dist/setup-runtime.test.d.ts +1 -0
- package/dist/setup-runtime.test.js +110 -0
- package/dist/setup-sources-notion.test.d.ts +1 -0
- package/dist/setup-sources-notion.test.js +107 -0
- package/dist/setup-sources.js +5 -2
- package/dist/setup.d.ts +19 -1
- package/dist/setup.js +104 -29
- package/dist/setup.test.js +221 -57
- package/dist/sl.js +2 -2
- package/dist/sl.test.js +10 -0
- package/dist/source-mapping.js +9 -1
- package/dist/source-mapping.test.d.ts +1 -0
- package/dist/source-mapping.test.js +65 -0
- package/dist/sql.d.ts +22 -0
- package/dist/sql.js +125 -0
- package/dist/sql.test.d.ts +1 -0
- package/dist/sql.test.js +226 -0
- package/node_modules/@ktx/connector-clickhouse/dist/package-exports.test.js +1 -1
- package/node_modules/@ktx/context/dist/connections/connection-type.d.ts +4 -4
- package/node_modules/@ktx/context/dist/core/git.service.d.ts +3 -0
- package/node_modules/@ktx/context/dist/core/git.service.js +47 -1
- package/node_modules/@ktx/context/dist/core/git.service.patch.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/core/git.service.patch.test.js +40 -0
- package/node_modules/@ktx/context/dist/ingest/adapters/historic-sql/types.d.ts +5 -5
- package/node_modules/@ktx/context/dist/ingest/adapters/looker/looker.adapter.d.ts +2 -2
- package/node_modules/@ktx/context/dist/ingest/adapters/looker/tools/looker-query-to-sl.tool.d.ts +2 -2
- package/node_modules/@ktx/context/dist/ingest/adapters/looker/types.d.ts +16 -16
- package/node_modules/@ktx/context/dist/ingest/adapters/lookml/pull-config.d.ts +1 -1
- package/node_modules/@ktx/context/dist/ingest/adapters/metabase/fetch.js +16 -0
- package/node_modules/@ktx/context/dist/ingest/adapters/metabase/fetch.test.js +41 -0
- package/node_modules/@ktx/context/dist/ingest/adapters/metricflow/metricflow.adapter.d.ts +2 -1
- package/node_modules/@ktx/context/dist/ingest/adapters/metricflow/metricflow.adapter.js +40 -0
- package/node_modules/@ktx/context/dist/ingest/adapters/metricflow/metricflow.adapter.test.js +116 -1
- package/node_modules/@ktx/context/dist/ingest/adapters/metricflow/projection-config.d.ts +29 -0
- package/node_modules/@ktx/context/dist/ingest/adapters/metricflow/projection-config.js +40 -0
- package/node_modules/@ktx/context/dist/ingest/adapters/metricflow/pull-config.d.ts +1 -1
- package/node_modules/@ktx/context/dist/ingest/artifact-gates.d.ts +25 -0
- package/node_modules/@ktx/context/dist/ingest/artifact-gates.js +149 -0
- package/node_modules/@ktx/context/dist/ingest/artifact-gates.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/ingest/artifact-gates.test.js +167 -0
- package/node_modules/@ktx/context/dist/ingest/final-gate-repair.d.ts +29 -0
- package/node_modules/@ktx/context/dist/ingest/final-gate-repair.js +178 -0
- package/node_modules/@ktx/context/dist/ingest/final-gate-repair.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/ingest/final-gate-repair.test.js +109 -0
- package/node_modules/@ktx/context/dist/ingest/index.d.ts +8 -1
- package/node_modules/@ktx/context/dist/ingest/index.js +7 -0
- package/node_modules/@ktx/context/dist/ingest/ingest-bundle.runner.d.ts +18 -2
- package/node_modules/@ktx/context/dist/ingest/ingest-bundle.runner.isolated-diff.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/ingest/ingest-bundle.runner.isolated-diff.test.js +1761 -0
- package/node_modules/@ktx/context/dist/ingest/ingest-bundle.runner.js +1695 -901
- package/node_modules/@ktx/context/dist/ingest/ingest-bundle.runner.test.js +135 -118
- package/node_modules/@ktx/context/dist/ingest/ingest-trace.d.ts +50 -0
- package/node_modules/@ktx/context/dist/ingest/ingest-trace.js +88 -0
- package/node_modules/@ktx/context/dist/ingest/ingest-trace.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/ingest/ingest-trace.test.js +76 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/git-patch.d.ts +16 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/git-patch.js +78 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/git-patch.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/git-patch.test.js +76 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/patch-integrator.d.ts +58 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/patch-integrator.js +223 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/patch-integrator.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/patch-integrator.test.js +369 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/textual-conflict-resolver.d.ts +23 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/textual-conflict-resolver.js +190 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/textual-conflict-resolver.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/textual-conflict-resolver.test.js +101 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/work-unit-executor.d.ts +15 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/work-unit-executor.js +61 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/work-unit-executor.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/ingest/isolated-diff/work-unit-executor.test.js +137 -0
- package/node_modules/@ktx/context/dist/ingest/local-bundle-ingest.test.js +7 -0
- package/node_modules/@ktx/context/dist/ingest/local-bundle-runtime.js +54 -10
- package/node_modules/@ktx/context/dist/ingest/local-bundle-runtime.test.js +65 -0
- package/node_modules/@ktx/context/dist/ingest/memory-flow/schema.d.ts +23 -5
- package/node_modules/@ktx/context/dist/ingest/memory-flow/schema.js +17 -0
- package/node_modules/@ktx/context/dist/ingest/memory-flow/schema.test.js +1 -0
- package/node_modules/@ktx/context/dist/ingest/memory-flow/types.d.ts +6 -0
- package/node_modules/@ktx/context/dist/ingest/parsed-target-table.d.ts +1 -1
- package/node_modules/@ktx/context/dist/ingest/ports.d.ts +3 -0
- package/node_modules/@ktx/context/dist/ingest/report-snapshot.d.ts +32 -7
- package/node_modules/@ktx/context/dist/ingest/report-snapshot.js +25 -0
- package/node_modules/@ktx/context/dist/ingest/report-snapshot.test.js +124 -0
- package/node_modules/@ktx/context/dist/ingest/reports.d.ts +23 -0
- package/node_modules/@ktx/context/dist/ingest/semantic-layer-target-policy.d.ts +11 -0
- package/node_modules/@ktx/context/dist/ingest/semantic-layer-target-policy.js +26 -0
- package/node_modules/@ktx/context/dist/ingest/semantic-layer-target-policy.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/ingest/semantic-layer-target-policy.test.js +25 -0
- package/node_modules/@ktx/context/dist/ingest/stages/stage-3-work-units.d.ts +4 -0
- package/node_modules/@ktx/context/dist/ingest/stages/stage-3-work-units.js +4 -0
- package/node_modules/@ktx/context/dist/ingest/stages/stage-3-work-units.test.js +29 -0
- package/node_modules/@ktx/context/dist/ingest/tools/emit-unmapped-fallback.tool.d.ts +1 -1
- package/node_modules/@ktx/context/dist/ingest/types.d.ts +24 -0
- package/node_modules/@ktx/context/dist/ingest/wiki-body-refs.d.ts +24 -0
- package/node_modules/@ktx/context/dist/ingest/wiki-body-refs.js +111 -0
- package/node_modules/@ktx/context/dist/ingest/wiki-body-refs.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/ingest/wiki-body-refs.test.js +138 -0
- package/node_modules/@ktx/context/dist/llm/claude-code-runtime.js +19 -2
- package/node_modules/@ktx/context/dist/llm/claude-code-runtime.test.js +33 -0
- package/node_modules/@ktx/context/dist/project/setup-config.d.ts +1 -1
- package/node_modules/@ktx/context/dist/project/setup-config.js +10 -1
- package/node_modules/@ktx/context/dist/project/setup-config.test.js +3 -2
- package/node_modules/@ktx/context/dist/sl/tools/sl-edit-source.tool.js +5 -1
- package/node_modules/@ktx/context/dist/sl/tools/sl-edit-source.tool.test.js +15 -0
- package/node_modules/@ktx/context/dist/sl/tools/sl-write-source.tool.js +5 -1
- package/node_modules/@ktx/context/dist/sl/tools/sl-write-source.tool.test.js +22 -0
- package/node_modules/@ktx/context/dist/tools/action-target-connection.d.ts +9 -0
- package/node_modules/@ktx/context/dist/tools/action-target-connection.js +14 -0
- package/node_modules/@ktx/context/dist/tools/context-candidate-write.tool.d.ts +4 -4
- package/node_modules/@ktx/context/dist/tools/index.d.ts +1 -0
- package/node_modules/@ktx/context/dist/tools/index.js +1 -0
- package/node_modules/@ktx/context/dist/wiki/local-knowledge.js +4 -1
- package/node_modules/@ktx/context/dist/wiki/local-knowledge.test.js +44 -0
- package/node_modules/@ktx/context/dist/wiki/tools/wiki-write.tool.js +3 -48
- package/node_modules/@ktx/context/dist/wiki/tools/wiki-write.tool.test.js +28 -0
- package/node_modules/@ktx/context/dist/wiki/wiki-ref-validation.d.ts +17 -0
- package/node_modules/@ktx/context/dist/wiki/wiki-ref-validation.js +79 -0
- package/node_modules/@ktx/context/dist/wiki/wiki-ref-validation.test.d.ts +1 -0
- package/node_modules/@ktx/context/dist/wiki/wiki-ref-validation.test.js +64 -0
- package/node_modules/@ktx/context/prompts/memory_agent_bundle_ingest_work_unit.md +23 -4
- package/node_modules/@ktx/context/skills/ingest_triage/SKILL.md +7 -3
- package/package.json +4 -4
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { mkdir, mkdtemp, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
5
|
+
import { FileIngestTraceWriter } from '../ingest-trace.js';
|
|
6
|
+
import { resolveTextualConflict } from './textual-conflict-resolver.js';
|
|
7
|
+
async function makeHarness() {
|
|
8
|
+
const root = await mkdtemp(join(tmpdir(), 'ktx-textual-resolver-'));
|
|
9
|
+
const workdir = join(root, 'workdir');
|
|
10
|
+
const patchPath = join(root, 'failed.patch');
|
|
11
|
+
const trace = new FileIngestTraceWriter({
|
|
12
|
+
tracePath: join(root, 'trace.jsonl'),
|
|
13
|
+
jobId: 'job-1',
|
|
14
|
+
connectionId: 'warehouse',
|
|
15
|
+
sourceKey: 'metabase',
|
|
16
|
+
runId: 'run-1',
|
|
17
|
+
syncId: 'sync-1',
|
|
18
|
+
level: 'trace',
|
|
19
|
+
});
|
|
20
|
+
await mkdir(join(workdir, 'wiki/global'), { recursive: true });
|
|
21
|
+
await writeFile(join(workdir, 'wiki/global/account.md'), 'accepted line\n', 'utf-8');
|
|
22
|
+
await writeFile(patchPath, [
|
|
23
|
+
'diff --git a/wiki/global/account.md b/wiki/global/account.md',
|
|
24
|
+
'index 8877391..6f63f4d 100644',
|
|
25
|
+
'--- a/wiki/global/account.md',
|
|
26
|
+
'+++ b/wiki/global/account.md',
|
|
27
|
+
'@@ -1 +1 @@',
|
|
28
|
+
'-base line',
|
|
29
|
+
'+proposal line',
|
|
30
|
+
'',
|
|
31
|
+
].join('\n'), 'utf-8');
|
|
32
|
+
return { root, workdir, patchPath, trace };
|
|
33
|
+
}
|
|
34
|
+
describe('resolveTextualConflict', () => {
|
|
35
|
+
it('lets the repair agent read the failed patch and write only touched paths', async () => {
|
|
36
|
+
const { workdir, patchPath, trace } = await makeHarness();
|
|
37
|
+
const agentRunner = {
|
|
38
|
+
runLoop: vi.fn(async (params) => {
|
|
39
|
+
const current = await params.toolSet.read_integration_file.execute({ path: 'wiki/global/account.md' });
|
|
40
|
+
expect(current.structured).toEqual({ path: 'wiki/global/account.md', exists: true });
|
|
41
|
+
expect(current.markdown).toContain('accepted line');
|
|
42
|
+
const patch = await params.toolSet.read_failed_patch.execute({});
|
|
43
|
+
expect(patch.markdown).toContain('proposal line');
|
|
44
|
+
await expect(params.toolSet.write_integration_file.execute({
|
|
45
|
+
path: 'wiki/global/not-allowed.md',
|
|
46
|
+
content: 'bad\n',
|
|
47
|
+
})).rejects.toThrow(/resolver path not allowed/);
|
|
48
|
+
await params.toolSet.write_integration_file.execute({
|
|
49
|
+
path: 'wiki/global/account.md',
|
|
50
|
+
content: 'accepted line\nproposal line\n',
|
|
51
|
+
});
|
|
52
|
+
return { stopReason: 'natural' };
|
|
53
|
+
}),
|
|
54
|
+
};
|
|
55
|
+
const result = await resolveTextualConflict({
|
|
56
|
+
agentRunner,
|
|
57
|
+
workdir,
|
|
58
|
+
unitKey: 'wu-a',
|
|
59
|
+
patchPath,
|
|
60
|
+
touchedPaths: ['wiki/global/account.md'],
|
|
61
|
+
trace,
|
|
62
|
+
reason: 'patch failed: wiki/global/account.md',
|
|
63
|
+
maxAttempts: 1,
|
|
64
|
+
stepBudget: 8,
|
|
65
|
+
});
|
|
66
|
+
expect(result).toEqual({
|
|
67
|
+
status: 'repaired',
|
|
68
|
+
attempts: 1,
|
|
69
|
+
changedPaths: ['wiki/global/account.md'],
|
|
70
|
+
});
|
|
71
|
+
await expect(readFile(join(workdir, 'wiki/global/account.md'), 'utf-8')).resolves.toBe('accepted line\nproposal line\n');
|
|
72
|
+
expect(agentRunner.runLoop).toHaveBeenCalledWith(expect.objectContaining({
|
|
73
|
+
modelRole: 'repair',
|
|
74
|
+
stepBudget: 8,
|
|
75
|
+
telemetryTags: expect.objectContaining({
|
|
76
|
+
operationName: 'ingest-isolated-diff-textual-resolver',
|
|
77
|
+
jobId: 'job-1',
|
|
78
|
+
unitKey: 'wu-a',
|
|
79
|
+
}),
|
|
80
|
+
}));
|
|
81
|
+
});
|
|
82
|
+
it('fails when the repair agent completes without editing any touched path', async () => {
|
|
83
|
+
const { workdir, patchPath, trace } = await makeHarness();
|
|
84
|
+
const result = await resolveTextualConflict({
|
|
85
|
+
agentRunner: { runLoop: vi.fn(async () => ({ stopReason: 'natural' })) },
|
|
86
|
+
workdir,
|
|
87
|
+
unitKey: 'wu-a',
|
|
88
|
+
patchPath,
|
|
89
|
+
touchedPaths: ['wiki/global/account.md'],
|
|
90
|
+
trace,
|
|
91
|
+
reason: 'patch failed: wiki/global/account.md',
|
|
92
|
+
maxAttempts: 1,
|
|
93
|
+
stepBudget: 8,
|
|
94
|
+
});
|
|
95
|
+
expect(result).toEqual({
|
|
96
|
+
status: 'failed',
|
|
97
|
+
attempts: 1,
|
|
98
|
+
reason: 'resolver completed without editing an allowed path',
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { IngestSessionWorktree, IngestSessionWorktreePort } from '../ports.js';
|
|
2
|
+
import type { WorkUnit } from '../types.js';
|
|
3
|
+
import type { IngestTraceWriter } from '../ingest-trace.js';
|
|
4
|
+
import type { WorkUnitOutcome } from '../stages/stage-3-work-units.js';
|
|
5
|
+
export interface RunIsolatedWorkUnitInput {
|
|
6
|
+
unitIndex: number;
|
|
7
|
+
ingestionBaseSha: string;
|
|
8
|
+
sessionWorktreeService: IngestSessionWorktreePort;
|
|
9
|
+
patchDir: string;
|
|
10
|
+
trace: IngestTraceWriter;
|
|
11
|
+
workUnit: WorkUnit;
|
|
12
|
+
run(child: IngestSessionWorktree): Promise<WorkUnitOutcome>;
|
|
13
|
+
afterSuccess?(child: IngestSessionWorktree): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
export declare function runIsolatedWorkUnit(input: RunIsolatedWorkUnitInput): Promise<WorkUnitOutcome>;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { mkdir, readFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { parsePatchTouchedPaths } from './git-patch.js';
|
|
4
|
+
function patchFileName(unitIndex, unitKey) {
|
|
5
|
+
const safeKey = unitKey.replace(/[^a-zA-Z0-9_.-]+/g, '-');
|
|
6
|
+
return `${String(unitIndex).padStart(4, '0')}-${safeKey}.patch`;
|
|
7
|
+
}
|
|
8
|
+
export async function runIsolatedWorkUnit(input) {
|
|
9
|
+
const sessionKey = `${input.trace.context.jobId}-${input.workUnit.unitKey}`;
|
|
10
|
+
let cleanupOutcome = 'crash';
|
|
11
|
+
const child = await input.sessionWorktreeService.create(sessionKey, input.ingestionBaseSha);
|
|
12
|
+
await input.trace.event('debug', 'work_unit', 'work_unit_child_created', {
|
|
13
|
+
unitKey: input.workUnit.unitKey,
|
|
14
|
+
unitIndex: input.unitIndex,
|
|
15
|
+
worktreePath: child.workdir,
|
|
16
|
+
baseSha: input.ingestionBaseSha,
|
|
17
|
+
});
|
|
18
|
+
try {
|
|
19
|
+
const outcome = await input.run(child);
|
|
20
|
+
if (outcome.status !== 'success') {
|
|
21
|
+
cleanupOutcome = 'success';
|
|
22
|
+
await input.trace.event('error', 'work_unit', 'work_unit_failed_before_patch', {
|
|
23
|
+
unitKey: input.workUnit.unitKey,
|
|
24
|
+
reason: outcome.reason ?? 'unknown failure',
|
|
25
|
+
});
|
|
26
|
+
return { ...outcome, childWorktreePath: child.workdir };
|
|
27
|
+
}
|
|
28
|
+
await input.afterSuccess?.(child);
|
|
29
|
+
await mkdir(input.patchDir, { recursive: true });
|
|
30
|
+
const patchPath = join(input.patchDir, patchFileName(input.unitIndex, input.workUnit.unitKey));
|
|
31
|
+
await child.git.writeBinaryNoRenamePatch(input.ingestionBaseSha, 'HEAD', patchPath);
|
|
32
|
+
const patch = await readFile(patchPath, 'utf-8');
|
|
33
|
+
const touched = parsePatchTouchedPaths(patch);
|
|
34
|
+
cleanupOutcome = 'success';
|
|
35
|
+
await input.trace.event('debug', 'work_unit', 'work_unit_patch_collected', {
|
|
36
|
+
unitKey: input.workUnit.unitKey,
|
|
37
|
+
patchPath,
|
|
38
|
+
touchedPaths: touched.map((entry) => entry.path),
|
|
39
|
+
patchBytes: Buffer.byteLength(patch),
|
|
40
|
+
});
|
|
41
|
+
return {
|
|
42
|
+
...outcome,
|
|
43
|
+
patchPath,
|
|
44
|
+
patchTouchedPaths: touched.map((entry) => entry.path),
|
|
45
|
+
childWorktreePath: child.workdir,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
await input.trace.event('error', 'work_unit', 'work_unit_child_failed', { unitKey: input.workUnit.unitKey, worktreePath: child.workdir }, error);
|
|
50
|
+
cleanupOutcome = 'success';
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
await input.sessionWorktreeService.cleanup(child, cleanupOutcome);
|
|
55
|
+
await input.trace.event('trace', 'work_unit', 'work_unit_child_cleanup', {
|
|
56
|
+
unitKey: input.workUnit.unitKey,
|
|
57
|
+
outcome: cleanupOutcome,
|
|
58
|
+
worktreePath: child.workdir,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { mkdir, mkdtemp, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
5
|
+
import { GitService } from '../../core/index.js';
|
|
6
|
+
import { FileIngestTraceWriter } from '../ingest-trace.js';
|
|
7
|
+
import { runIsolatedWorkUnit } from './work-unit-executor.js';
|
|
8
|
+
async function makeGit() {
|
|
9
|
+
const homeDir = await mkdtemp(join(tmpdir(), 'ktx-isolated-wu-'));
|
|
10
|
+
const configDir = join(homeDir, 'config');
|
|
11
|
+
const git = new GitService({
|
|
12
|
+
storage: { configDir, homeDir },
|
|
13
|
+
git: {
|
|
14
|
+
userName: 'System User',
|
|
15
|
+
userEmail: 'system@example.com',
|
|
16
|
+
bootstrapMessage: 'init',
|
|
17
|
+
bootstrapAuthor: 'system',
|
|
18
|
+
bootstrapAuthorEmail: 'system@example.com',
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
await git.onModuleInit();
|
|
22
|
+
await mkdir(join(configDir, 'raw-sources/c1/fake/s'), { recursive: true });
|
|
23
|
+
await writeFile(join(configDir, 'raw-sources/c1/fake/s/a.json'), '{}\n');
|
|
24
|
+
await git.commitFiles(['raw-sources/c1/fake/s/a.json'], 'raw snapshot', 'System User', 'system@example.com');
|
|
25
|
+
return { homeDir, configDir, git, baseSha: await git.revParseHead() };
|
|
26
|
+
}
|
|
27
|
+
describe('runIsolatedWorkUnit', () => {
|
|
28
|
+
it('creates a child worktree at the ingestion base and persists a patch proposal', async () => {
|
|
29
|
+
const { homeDir, git, baseSha } = await makeGit();
|
|
30
|
+
const childDir = join(homeDir, '.worktrees/session-job-1-wu-1');
|
|
31
|
+
const sessionWorktreeService = {
|
|
32
|
+
create: vi.fn(async (_key, startSha) => {
|
|
33
|
+
await mkdir(join(homeDir, '.worktrees'), { recursive: true });
|
|
34
|
+
await git.addWorktree(childDir, 'session/job-1-wu-1', startSha);
|
|
35
|
+
const childGit = git.forWorktree(childDir);
|
|
36
|
+
return {
|
|
37
|
+
chatId: 'job-1-wu-1',
|
|
38
|
+
workdir: childDir,
|
|
39
|
+
branch: 'session/job-1-wu-1',
|
|
40
|
+
baseSha: startSha,
|
|
41
|
+
createdAt: new Date(),
|
|
42
|
+
git: childGit,
|
|
43
|
+
config: {},
|
|
44
|
+
};
|
|
45
|
+
}),
|
|
46
|
+
cleanup: vi.fn(async () => undefined),
|
|
47
|
+
};
|
|
48
|
+
const tracePath = join(homeDir, '.ktx/ingest-traces/job-1/trace.jsonl');
|
|
49
|
+
const trace = new FileIngestTraceWriter({
|
|
50
|
+
tracePath,
|
|
51
|
+
jobId: 'job-1',
|
|
52
|
+
connectionId: 'c1',
|
|
53
|
+
sourceKey: 'fake',
|
|
54
|
+
level: 'trace',
|
|
55
|
+
});
|
|
56
|
+
const result = await runIsolatedWorkUnit({
|
|
57
|
+
unitIndex: 0,
|
|
58
|
+
ingestionBaseSha: baseSha,
|
|
59
|
+
sessionWorktreeService: sessionWorktreeService,
|
|
60
|
+
patchDir: join(homeDir, '.ktx/ingest-patches/job-1'),
|
|
61
|
+
trace,
|
|
62
|
+
run: async (child) => {
|
|
63
|
+
await mkdir(join(child.workdir, 'wiki/global'), { recursive: true });
|
|
64
|
+
await writeFile(join(child.workdir, 'wiki/global/a.md'), '---\nsummary: A\nusage_mode: auto\n---\n\nBody\n');
|
|
65
|
+
await child.git.commitFiles(['wiki/global/a.md'], 'test: write wiki', 'KTX Test', 'system@ktx.local');
|
|
66
|
+
return {
|
|
67
|
+
unitKey: 'wu-1',
|
|
68
|
+
status: 'success',
|
|
69
|
+
preSha: baseSha,
|
|
70
|
+
postSha: await child.git.revParseHead(),
|
|
71
|
+
actions: [{ target: 'wiki', type: 'created', key: 'a', detail: 'A' }],
|
|
72
|
+
touchedSlSources: [],
|
|
73
|
+
};
|
|
74
|
+
},
|
|
75
|
+
workUnit: { unitKey: 'wu-1', rawFiles: ['a.json'], peerFileIndex: [], dependencyPaths: [] },
|
|
76
|
+
});
|
|
77
|
+
expect(sessionWorktreeService.create).toHaveBeenCalledWith('job-1-wu-1', baseSha);
|
|
78
|
+
expect(sessionWorktreeService.cleanup).toHaveBeenCalledWith(expect.any(Object), 'success');
|
|
79
|
+
expect(result.status).toBe('success');
|
|
80
|
+
if (result.status !== 'success') {
|
|
81
|
+
throw new Error('expected successful work unit');
|
|
82
|
+
}
|
|
83
|
+
const patchPath = result.patchPath;
|
|
84
|
+
if (!patchPath) {
|
|
85
|
+
throw new Error('expected patch path');
|
|
86
|
+
}
|
|
87
|
+
expect(patchPath).toContain('0000-wu-1.patch');
|
|
88
|
+
await expect(readFile(patchPath, 'utf-8')).resolves.toContain('wiki/global/a.md');
|
|
89
|
+
await expect(readFile(tracePath, 'utf-8')).resolves.toContain('work_unit_child_created');
|
|
90
|
+
});
|
|
91
|
+
it('removes child worktrees after failed WorkUnit outcomes are traced', async () => {
|
|
92
|
+
const { homeDir, git, baseSha } = await makeGit();
|
|
93
|
+
const childDir = join(homeDir, '.worktrees/session-job-1-wu-fail');
|
|
94
|
+
const sessionWorktreeService = {
|
|
95
|
+
create: vi.fn(async (_key, startSha) => {
|
|
96
|
+
await mkdir(join(homeDir, '.worktrees'), { recursive: true });
|
|
97
|
+
await git.addWorktree(childDir, 'session/job-1-wu-fail', startSha);
|
|
98
|
+
return {
|
|
99
|
+
chatId: 'job-1-wu-fail',
|
|
100
|
+
workdir: childDir,
|
|
101
|
+
branch: 'session/job-1-wu-fail',
|
|
102
|
+
baseSha: startSha,
|
|
103
|
+
createdAt: new Date(),
|
|
104
|
+
git: git.forWorktree(childDir),
|
|
105
|
+
config: {},
|
|
106
|
+
};
|
|
107
|
+
}),
|
|
108
|
+
cleanup: vi.fn(async () => undefined),
|
|
109
|
+
};
|
|
110
|
+
const trace = new FileIngestTraceWriter({
|
|
111
|
+
tracePath: join(homeDir, '.ktx/ingest-traces/job-1/trace.jsonl'),
|
|
112
|
+
jobId: 'job-1',
|
|
113
|
+
connectionId: 'c1',
|
|
114
|
+
sourceKey: 'fake',
|
|
115
|
+
level: 'trace',
|
|
116
|
+
});
|
|
117
|
+
const result = await runIsolatedWorkUnit({
|
|
118
|
+
unitIndex: 0,
|
|
119
|
+
ingestionBaseSha: baseSha,
|
|
120
|
+
sessionWorktreeService: sessionWorktreeService,
|
|
121
|
+
patchDir: join(homeDir, '.ktx/ingest-patches/job-1'),
|
|
122
|
+
trace,
|
|
123
|
+
run: async () => ({
|
|
124
|
+
unitKey: 'wu-fail',
|
|
125
|
+
status: 'failed',
|
|
126
|
+
reason: 'agent loop errored',
|
|
127
|
+
preSha: baseSha,
|
|
128
|
+
postSha: baseSha,
|
|
129
|
+
actions: [],
|
|
130
|
+
touchedSlSources: [],
|
|
131
|
+
}),
|
|
132
|
+
workUnit: { unitKey: 'wu-fail', rawFiles: ['a.json'], peerFileIndex: [], dependencyPaths: [] },
|
|
133
|
+
});
|
|
134
|
+
expect(result.status).toBe('failed');
|
|
135
|
+
expect(sessionWorktreeService.cleanup).toHaveBeenCalledWith(expect.any(Object), 'success');
|
|
136
|
+
});
|
|
137
|
+
});
|
|
@@ -596,6 +596,13 @@ describe('canonical local ingest', () => {
|
|
|
596
596
|
],
|
|
597
597
|
},
|
|
598
598
|
});
|
|
599
|
+
expect(result.report.body.isolatedDiff).toMatchObject({
|
|
600
|
+
enabled: true,
|
|
601
|
+
acceptedPatches: 0,
|
|
602
|
+
projectionSha: expect.any(String),
|
|
603
|
+
});
|
|
604
|
+
const projectedSourcePath = join(metricflowProject.projectDir, 'semantic-layer/warehouse/orders.yaml');
|
|
605
|
+
await expect(readFile(projectedSourcePath, 'utf-8')).resolves.toContain('name: orders');
|
|
599
606
|
const stagedRawPath = join(metricflowProject.projectDir, 'raw-sources', 'warehouse', 'metricflow', result.report.body.syncId, 'models', 'orders.yml');
|
|
600
607
|
await expect(readFile(stagedRawPath, 'utf-8')).resolves.toContain('semantic_models:');
|
|
601
608
|
});
|
|
@@ -16,6 +16,7 @@ import { createEmitHistoricSqlEvidenceTool } from './adapters/historic-sql/evide
|
|
|
16
16
|
import { HistoricSqlProjectionPostProcessor } from './adapters/historic-sql/post-processor.js';
|
|
17
17
|
import { ContextEvidenceIndexService, SqliteContextEvidenceStore } from './context-evidence/index.js';
|
|
18
18
|
import { DiffSetService } from './diff-set.service.js';
|
|
19
|
+
import { ingestTracePathForJob } from './ingest-trace.js';
|
|
19
20
|
import { IngestBundleRunner } from './ingest-bundle.runner.js';
|
|
20
21
|
import { PageTriageService } from './page-triage/index.js';
|
|
21
22
|
import { createWarehouseVerificationTools } from './tools/warehouse-verification/index.js';
|
|
@@ -25,6 +26,11 @@ const promptsDir = fileURLToPath(new URL('../../prompts', import.meta.url));
|
|
|
25
26
|
const skillsDir = fileURLToPath(new URL('../../skills', import.meta.url));
|
|
26
27
|
const LOCAL_AUTHOR = { name: 'KTX Local', email: 'local@ktx.local' };
|
|
27
28
|
const LOCAL_SHAPE_WARNING = 'Local ingest validates semantic-layer YAML shape only.';
|
|
29
|
+
const INGEST_TRACE_LEVELS = new Set(['error', 'info', 'debug', 'trace']);
|
|
30
|
+
function ingestTraceLevelFromEnv(env = process.env) {
|
|
31
|
+
const raw = env.KTX_INGEST_TRACE_LEVEL;
|
|
32
|
+
return raw && INGEST_TRACE_LEVELS.has(raw) ? raw : 'debug';
|
|
33
|
+
}
|
|
28
34
|
class NoopEmbeddingPort {
|
|
29
35
|
maxBatchSize = 64;
|
|
30
36
|
async computeEmbedding() {
|
|
@@ -51,6 +57,9 @@ class LocalIngestStorage {
|
|
|
51
57
|
resolveTranscriptDir(jobId) {
|
|
52
58
|
return join(this.project.projectDir, '.ktx/ingest-transcripts', jobId);
|
|
53
59
|
}
|
|
60
|
+
resolveTracePath(jobId) {
|
|
61
|
+
return ingestTracePathForJob(this.homeDir, jobId);
|
|
62
|
+
}
|
|
54
63
|
}
|
|
55
64
|
class LocalIngestLock {
|
|
56
65
|
async withLock(_key, fn) {
|
|
@@ -131,21 +140,55 @@ class LocalSlPythonPort {
|
|
|
131
140
|
}
|
|
132
141
|
}
|
|
133
142
|
class LocalShapeOnlySlValidator {
|
|
143
|
+
validateParsedSource(sourceName, parsed) {
|
|
144
|
+
const isOverlay = parsed.table == null && parsed.sql == null;
|
|
145
|
+
const result = (isOverlay ? sourceOverlaySchema : sourceDefinitionSchema).safeParse(parsed);
|
|
146
|
+
return result.success
|
|
147
|
+
? { errors: [], warnings: [LOCAL_SHAPE_WARNING] }
|
|
148
|
+
: {
|
|
149
|
+
errors: result.error.issues.map((issue) => `${sourceName}: ${issue.path.join('.') || 'source'} ${issue.message}`),
|
|
150
|
+
warnings: [],
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
async validateComposedSource(deps, connectionId, sourceName, readError) {
|
|
154
|
+
try {
|
|
155
|
+
const { sources, loadErrors } = await deps.semanticLayerService.loadAllSources(connectionId);
|
|
156
|
+
const source = sources.find((candidate) => candidate.name === sourceName);
|
|
157
|
+
if (source) {
|
|
158
|
+
return this.validateParsedSource(sourceName, source);
|
|
159
|
+
}
|
|
160
|
+
const detail = loadErrors.length > 0
|
|
161
|
+
? loadErrors.join('; ')
|
|
162
|
+
: readError instanceof Error
|
|
163
|
+
? readError.message
|
|
164
|
+
: String(readError);
|
|
165
|
+
return { errors: [`${sourceName}: ${detail}`], warnings: [] };
|
|
166
|
+
}
|
|
167
|
+
catch (fallbackError) {
|
|
168
|
+
return {
|
|
169
|
+
errors: [`${sourceName}: ${fallbackError instanceof Error ? fallbackError.message : String(fallbackError)}`],
|
|
170
|
+
warnings: [],
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
}
|
|
134
174
|
async validateSingleSource(deps, connectionId, sourceName) {
|
|
175
|
+
let content;
|
|
135
176
|
try {
|
|
136
177
|
const file = await deps.semanticLayerService.readSourceFile(connectionId, sourceName);
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
return
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
};
|
|
178
|
+
content = file.content;
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
return this.validateComposedSource(deps, connectionId, sourceName, error);
|
|
182
|
+
}
|
|
183
|
+
try {
|
|
184
|
+
const parsed = YAML.parse(content);
|
|
185
|
+
return this.validateParsedSource(sourceName, parsed);
|
|
146
186
|
}
|
|
147
187
|
catch (error) {
|
|
148
|
-
return {
|
|
188
|
+
return {
|
|
189
|
+
errors: [`${sourceName}: invalid YAML — ${error instanceof Error ? error.message : String(error)}`],
|
|
190
|
+
warnings: [],
|
|
191
|
+
};
|
|
149
192
|
}
|
|
150
193
|
}
|
|
151
194
|
}
|
|
@@ -494,6 +537,7 @@ export function createLocalBundleIngestRuntime(options) {
|
|
|
494
537
|
workUnitMaxConcurrency: options.project.config.ingest.workUnits.maxConcurrency,
|
|
495
538
|
workUnitStepBudget: options.project.config.ingest.workUnits.stepBudget,
|
|
496
539
|
workUnitFailureMode: options.project.config.ingest.workUnits.failureMode,
|
|
540
|
+
ingestTraceLevel: ingestTraceLevelFromEnv(),
|
|
497
541
|
},
|
|
498
542
|
skillsRegistry: new SkillsRegistryService({ skillsDir, logger }),
|
|
499
543
|
promptService,
|
|
@@ -102,6 +102,53 @@ describe('createLocalBundleIngestRuntime', () => {
|
|
|
102
102
|
{ id: 'bq', name: 'bq', connectionType: 'BIGQUERY' },
|
|
103
103
|
]);
|
|
104
104
|
});
|
|
105
|
+
it('validates manifest-backed scan sources during local ingest gates', async () => {
|
|
106
|
+
await project.fileStore.writeFile('semantic-layer/warehouse/_schema/public.yaml', [
|
|
107
|
+
'tables:',
|
|
108
|
+
' payments:',
|
|
109
|
+
' table: public.payments',
|
|
110
|
+
' columns:',
|
|
111
|
+
' - name: payment_id',
|
|
112
|
+
' type: string',
|
|
113
|
+
' - name: amount',
|
|
114
|
+
' type: number',
|
|
115
|
+
'',
|
|
116
|
+
].join('\n'), 'ktx', 'ktx@example.com', 'Add warehouse manifest');
|
|
117
|
+
const agentRunner = testAgentRunner();
|
|
118
|
+
const runtime = createLocalBundleIngestRuntime({
|
|
119
|
+
project,
|
|
120
|
+
adapters: [new FakeSourceAdapter()],
|
|
121
|
+
agentRunner,
|
|
122
|
+
});
|
|
123
|
+
const deps = runtime.runner.deps;
|
|
124
|
+
await expect(deps.slValidator.validateSingleSource(deps, 'warehouse', 'payments')).resolves.toEqual({
|
|
125
|
+
errors: [],
|
|
126
|
+
warnings: expect.any(Array),
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
it('does not mask malformed direct overlays with manifest-backed fallback validation', async () => {
|
|
130
|
+
await project.fileStore.writeFile('semantic-layer/warehouse/_schema/public.yaml', [
|
|
131
|
+
'tables:',
|
|
132
|
+
' payments:',
|
|
133
|
+
' table: public.payments',
|
|
134
|
+
' columns:',
|
|
135
|
+
' - name: payment_id',
|
|
136
|
+
' type: string',
|
|
137
|
+
'',
|
|
138
|
+
].join('\n'), 'ktx', 'ktx@example.com', 'Add warehouse manifest');
|
|
139
|
+
await project.fileStore.writeFile('semantic-layer/warehouse/payments.yaml', ['name: payments', 'columns:', ' - [', ''].join('\n'), 'ktx', 'ktx@example.com', 'Add malformed overlay');
|
|
140
|
+
const agentRunner = testAgentRunner();
|
|
141
|
+
const runtime = createLocalBundleIngestRuntime({
|
|
142
|
+
project,
|
|
143
|
+
adapters: [new FakeSourceAdapter()],
|
|
144
|
+
agentRunner,
|
|
145
|
+
});
|
|
146
|
+
const deps = runtime.runner.deps;
|
|
147
|
+
await expect(deps.slValidator.validateSingleSource(deps, 'warehouse', 'payments')).resolves.toEqual({
|
|
148
|
+
errors: [expect.stringContaining('invalid YAML')],
|
|
149
|
+
warnings: [],
|
|
150
|
+
});
|
|
151
|
+
});
|
|
105
152
|
it('passes project connection config to local ingest query executors', async () => {
|
|
106
153
|
const agentRunner = testAgentRunner();
|
|
107
154
|
const queryExecutor = {
|
|
@@ -130,6 +177,24 @@ describe('createLocalBundleIngestRuntime', () => {
|
|
|
130
177
|
sql: 'select 1',
|
|
131
178
|
});
|
|
132
179
|
});
|
|
180
|
+
it('defaults local bundle ingest to isolated diffs without a shared-worktree fallback setting', () => {
|
|
181
|
+
const runtime = createLocalBundleIngestRuntime({
|
|
182
|
+
project,
|
|
183
|
+
adapters: [new FakeSourceAdapter()],
|
|
184
|
+
agentRunner: testAgentRunner(),
|
|
185
|
+
});
|
|
186
|
+
const settings = runtime.runner.deps.settings;
|
|
187
|
+
const fallbackSettingKey = ['sharedWorktree', 'SourceKeys'].join('');
|
|
188
|
+
expect(settings).not.toHaveProperty(fallbackSettingKey);
|
|
189
|
+
expect(Object.keys(settings).sort()).toEqual([
|
|
190
|
+
'ingestTraceLevel',
|
|
191
|
+
'memoryIngestionModel',
|
|
192
|
+
'probeRowCount',
|
|
193
|
+
'workUnitFailureMode',
|
|
194
|
+
'workUnitMaxConcurrency',
|
|
195
|
+
'workUnitStepBudget',
|
|
196
|
+
]);
|
|
197
|
+
});
|
|
133
198
|
it('accepts a debug LLM request file when constructing the default agent runner', async () => {
|
|
134
199
|
await writeFile(join(project.projectDir, 'ktx.yaml'), [
|
|
135
200
|
'connections:',
|
|
@@ -2,8 +2,8 @@ import * as z from 'zod';
|
|
|
2
2
|
import type { MemoryFlowReplayInput } from './types.js';
|
|
3
3
|
export declare const memoryFlowRunStatusSchema: z.ZodEnum<{
|
|
4
4
|
error: "error";
|
|
5
|
-
running: "running";
|
|
6
5
|
done: "done";
|
|
6
|
+
running: "running";
|
|
7
7
|
}>;
|
|
8
8
|
export declare const memoryFlowEventSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
9
9
|
type: z.ZodLiteral<"source_acquired">;
|
|
@@ -42,15 +42,33 @@ export declare const memoryFlowEventSchema: z.ZodDiscriminatedUnion<[z.ZodObject
|
|
|
42
42
|
type: z.ZodLiteral<"stage_skipped">;
|
|
43
43
|
stage: z.ZodEnum<{
|
|
44
44
|
source: "source";
|
|
45
|
-
chunks: "chunks";
|
|
46
45
|
workUnits: "workUnits";
|
|
47
46
|
actions: "actions";
|
|
47
|
+
chunks: "chunks";
|
|
48
48
|
gates: "gates";
|
|
49
49
|
saved: "saved";
|
|
50
50
|
}>;
|
|
51
51
|
reason: z.ZodString;
|
|
52
52
|
} & {
|
|
53
53
|
emittedAt: z.ZodOptional<z.ZodString>;
|
|
54
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
55
|
+
type: z.ZodLiteral<"stage_progress">;
|
|
56
|
+
stage: z.ZodEnum<{
|
|
57
|
+
source: "source";
|
|
58
|
+
report: "report";
|
|
59
|
+
integration: "integration";
|
|
60
|
+
reconciliation: "reconciliation";
|
|
61
|
+
post_processor: "post_processor";
|
|
62
|
+
wiki_sl_ref_repair: "wiki_sl_ref_repair";
|
|
63
|
+
final_gates: "final_gates";
|
|
64
|
+
save: "save";
|
|
65
|
+
provenance: "provenance";
|
|
66
|
+
}>;
|
|
67
|
+
percent: z.ZodNumber;
|
|
68
|
+
message: z.ZodString;
|
|
69
|
+
transient: z.ZodOptional<z.ZodBoolean>;
|
|
70
|
+
} & {
|
|
71
|
+
emittedAt: z.ZodOptional<z.ZodString>;
|
|
54
72
|
}, z.core.$strip>, z.ZodObject<{
|
|
55
73
|
type: z.ZodLiteral<"work_unit_started">;
|
|
56
74
|
unitKey: z.ZodString;
|
|
@@ -125,8 +143,8 @@ export declare const memoryFlowActionDetailSchema: z.ZodObject<{
|
|
|
125
143
|
}>;
|
|
126
144
|
action: z.ZodEnum<{
|
|
127
145
|
created: "created";
|
|
128
|
-
updated: "updated";
|
|
129
146
|
removed: "removed";
|
|
147
|
+
updated: "updated";
|
|
130
148
|
}>;
|
|
131
149
|
key: z.ZodString;
|
|
132
150
|
summary: z.ZodString;
|
|
@@ -145,8 +163,8 @@ export declare const memoryFlowDetailSectionsSchema: z.ZodObject<{
|
|
|
145
163
|
}>;
|
|
146
164
|
action: z.ZodEnum<{
|
|
147
165
|
created: "created";
|
|
148
|
-
updated: "updated";
|
|
149
166
|
removed: "removed";
|
|
167
|
+
updated: "updated";
|
|
150
168
|
}>;
|
|
151
169
|
key: z.ZodString;
|
|
152
170
|
summary: z.ZodString;
|
|
@@ -181,8 +199,8 @@ export declare const memoryFlowStreamEventSchema: z.ZodDiscriminatedUnion<[z.Zod
|
|
|
181
199
|
type: z.ZodLiteral<"closed">;
|
|
182
200
|
status: z.ZodEnum<{
|
|
183
201
|
error: "error";
|
|
184
|
-
running: "running";
|
|
185
202
|
done: "done";
|
|
203
|
+
running: "running";
|
|
186
204
|
}>;
|
|
187
205
|
errors: z.ZodArray<z.ZodString>;
|
|
188
206
|
}, z.core.$strip>], "type">;
|
|
@@ -47,6 +47,23 @@ export const memoryFlowEventSchema = z.discriminatedUnion('type', [
|
|
|
47
47
|
stage: z.enum(['source', 'chunks', 'workUnits', 'actions', 'gates', 'saved']),
|
|
48
48
|
reason: z.string().min(1),
|
|
49
49
|
}),
|
|
50
|
+
eventSchema({
|
|
51
|
+
type: z.literal('stage_progress'),
|
|
52
|
+
stage: z.enum([
|
|
53
|
+
'source',
|
|
54
|
+
'integration',
|
|
55
|
+
'reconciliation',
|
|
56
|
+
'post_processor',
|
|
57
|
+
'wiki_sl_ref_repair',
|
|
58
|
+
'final_gates',
|
|
59
|
+
'save',
|
|
60
|
+
'provenance',
|
|
61
|
+
'report',
|
|
62
|
+
]),
|
|
63
|
+
percent: z.number().min(0).max(100),
|
|
64
|
+
message: z.string().min(1),
|
|
65
|
+
transient: z.boolean().optional(),
|
|
66
|
+
}),
|
|
50
67
|
eventSchema({
|
|
51
68
|
type: z.literal('work_unit_started'),
|
|
52
69
|
unitKey: z.string().min(1),
|
|
@@ -15,6 +15,7 @@ function snapshot(overrides = {}) {
|
|
|
15
15
|
{ type: 'raw_snapshot_written', syncId: 'sync-1', rawFileCount: 2 },
|
|
16
16
|
{ type: 'diff_computed', added: 1, modified: 1, deleted: 0, unchanged: 0 },
|
|
17
17
|
{ type: 'chunks_planned', chunkCount: 1, workUnitCount: 1, evictionCount: 0 },
|
|
18
|
+
{ type: 'stage_progress', stage: 'integration', percent: 80, message: 'Integrating 1/1 patches: orders' },
|
|
18
19
|
{ type: 'work_unit_started', unitKey: 'orders', skills: ['wiki_capture'], stepBudget: 40 },
|
|
19
20
|
{ type: 'work_unit_step', unitKey: 'orders', stepIndex: 1, stepBudget: 40 },
|
|
20
21
|
{ type: 'candidate_action', unitKey: 'orders', target: 'wiki', action: 'created', key: 'wiki/orders.md' },
|
|
@@ -38,6 +38,12 @@ type MemoryFlowEventPayload = {
|
|
|
38
38
|
type: 'stage_skipped';
|
|
39
39
|
stage: MemoryFlowColumnId;
|
|
40
40
|
reason: string;
|
|
41
|
+
} | {
|
|
42
|
+
type: 'stage_progress';
|
|
43
|
+
stage: 'source' | 'integration' | 'reconciliation' | 'post_processor' | 'wiki_sl_ref_repair' | 'final_gates' | 'save' | 'provenance' | 'report';
|
|
44
|
+
percent: number;
|
|
45
|
+
message: string;
|
|
46
|
+
transient?: boolean;
|
|
41
47
|
} | {
|
|
42
48
|
type: 'work_unit_started';
|
|
43
49
|
unitKey: string;
|
|
@@ -8,9 +8,9 @@ export declare const parsedTargetTableSchema: z.ZodDiscriminatedUnion<[z.ZodObje
|
|
|
8
8
|
}, z.core.$strip>, z.ZodObject<{
|
|
9
9
|
ok: z.ZodLiteral<false>;
|
|
10
10
|
reason: z.ZodEnum<{
|
|
11
|
+
no_connection_mapping: "no_connection_mapping";
|
|
11
12
|
looker_template_unresolved: "looker_template_unresolved";
|
|
12
13
|
derived_table_not_supported: "derived_table_not_supported";
|
|
13
|
-
no_connection_mapping: "no_connection_mapping";
|
|
14
14
|
no_physical_table: "no_physical_table";
|
|
15
15
|
multiple_table_references: "multiple_table_references";
|
|
16
16
|
unsupported_dialect: "unsupported_dialect";
|