From aa8d59c14aa5e935a23077d5f4479376031affa4 Mon Sep 17 00:00:00 2001 From: Andrey Avtomonov Date: Mon, 18 May 2026 00:24:12 +0200 Subject: [PATCH] feat(ingest): report isolated diff resolver outcomes --- .../src/ingest/ingest-bundle.runner.ts | 28 ++++++++++++ .../src/ingest/report-snapshot.test.ts | 45 +++++++++++++++++++ .../context/src/ingest/report-snapshot.ts | 3 ++ packages/context/src/ingest/reports.ts | 3 ++ 4 files changed, 79 insertions(+) diff --git a/packages/context/src/ingest/ingest-bundle.runner.ts b/packages/context/src/ingest/ingest-bundle.runner.ts index 68ddd83b..3788efb3 100644 --- a/packages/context/src/ingest/ingest-bundle.runner.ts +++ b/packages/context/src/ingest/ingest-bundle.runner.ts @@ -15,6 +15,7 @@ import { validateFinalIngestArtifacts, validateProvenanceRawPaths } from './arti import { selectRelevantCanonicalPins } from './canonical-pins.js'; import { FileIngestTraceWriter, ingestTracePathForJob, type IngestTraceWriter, traceTimed } from './ingest-trace.js'; import { integrateWorkUnitPatch } from './isolated-diff/patch-integrator.js'; +import { resolveTextualConflict } from './isolated-diff/textual-conflict-resolver.js'; import { runIsolatedWorkUnit } from './isolated-diff/work-unit-executor.js'; import { sanitizeMemoryFlowError } from './memory-flow/live-buffer.js'; import type { CanonicalPin } from './canonical-pins.js'; @@ -965,6 +966,9 @@ export class IngestBundleRunner { acceptedPatches: number; textualConflicts: number; semanticConflicts: number; + resolverAttempts: number; + resolverRepairs: number; + resolverFailures: number; } | undefined; await trace.event('info', 'run', 'ingest_started', { @@ -1286,6 +1290,9 @@ export class IngestBundleRunner { acceptedPatches: 0, textualConflicts: 0, semanticConflicts: 0, + resolverAttempts: 0, + resolverRepairs: 0, + resolverFailures: 0, }; latestIsolatedDiffSummary = isolatedDiffSummary; @@ -1521,7 +1528,28 @@ export class IngestBundleRunner { ), }); }, + resolveTextualConflict: (context) => + resolveTextualConflict({ + agentRunner: this.deps.agentRunner, + workdir: sessionWorktree.workdir, + unitKey: context.unitKey, + patchPath: context.patchPath, + touchedPaths: context.touchedPaths, + trace: runTrace, + reason: context.reason, + maxAttempts: 1, + stepBudget: 12, + }), }); + if (integration.textualResolution) { + isolatedDiffSummary.resolverAttempts += integration.textualResolution.attempts; + if (integration.textualResolution.status === 'repaired') { + isolatedDiffSummary.textualConflicts += 1; + isolatedDiffSummary.resolverRepairs += 1; + } else { + isolatedDiffSummary.resolverFailures += 1; + } + } if (integration.status === 'textual_conflict') { isolatedDiffSummary.textualConflicts += 1; await this.deps.runs.markFailed(runRow.id); diff --git a/packages/context/src/ingest/report-snapshot.test.ts b/packages/context/src/ingest/report-snapshot.test.ts index 2e125386..01b77b87 100644 --- a/packages/context/src/ingest/report-snapshot.test.ts +++ b/packages/context/src/ingest/report-snapshot.test.ts @@ -256,4 +256,49 @@ describe('parseIngestReportSnapshot', () => { expect(() => parseIngestReportSnapshot(report)).toThrow('Invalid ingest report snapshot'); }); + + it('parses isolated-diff textual resolver counters', () => { + const snapshot = parseIngestReportSnapshot({ + id: 'report-1', + runId: 'run-1', + jobId: 'job-1', + connectionId: 'warehouse', + sourceKey: 'metabase', + createdAt: '2026-05-18T00:00:00.000Z', + body: { + status: 'completed', + syncId: 'sync-1', + diffSummary: { added: 0, modified: 1, deleted: 0, unchanged: 0 }, + commitSha: 'abc123', + isolatedDiff: { + enabled: true, + acceptedPatches: 2, + textualConflicts: 1, + semanticConflicts: 0, + resolverAttempts: 1, + resolverRepairs: 1, + resolverFailures: 0, + }, + workUnits: [], + failedWorkUnits: [], + reconciliationSkipped: true, + conflictsResolved: [], + evictionsApplied: [], + unmappedFallbacks: [], + artifactResolutions: [], + evictionInputs: [], + unresolvedCards: [], + supersededBy: null, + overrideOf: null, + provenanceRows: [], + toolTranscripts: [], + }, + }); + + expect(snapshot.body.isolatedDiff).toMatchObject({ + resolverAttempts: 1, + resolverRepairs: 1, + resolverFailures: 0, + }); + }); }); diff --git a/packages/context/src/ingest/report-snapshot.ts b/packages/context/src/ingest/report-snapshot.ts index b26cf611..4d48a9c9 100644 --- a/packages/context/src/ingest/report-snapshot.ts +++ b/packages/context/src/ingest/report-snapshot.ts @@ -155,6 +155,9 @@ export const ingestReportSnapshotSchema = z acceptedPatches: z.number().int().min(0), textualConflicts: z.number().int().min(0), semanticConflicts: z.number().int().min(0), + resolverAttempts: z.number().int().min(0).default(0), + resolverRepairs: z.number().int().min(0).default(0), + resolverFailures: z.number().int().min(0).default(0), }) .optional(), workUnits: z.array( diff --git a/packages/context/src/ingest/reports.ts b/packages/context/src/ingest/reports.ts index 14d1193e..32c5983a 100644 --- a/packages/context/src/ingest/reports.ts +++ b/packages/context/src/ingest/reports.ts @@ -70,6 +70,9 @@ export interface IngestReportBody { acceptedPatches: number; textualConflicts: number; semanticConflicts: number; + resolverAttempts?: number; + resolverRepairs?: number; + resolverFailures?: number; }; workUnits: IngestReportWorkUnit[]; failedWorkUnits: string[];