From d6f548337eea3fb4731f627b3ccd47ed321d3865 Mon Sep 17 00:00:00 2001 From: Petr Plenkov <6381507+ThePlenkov@users.noreply.github.com> Date: Wed, 23 Sep 2026 22:09:30 +0200 Subject: [PATCH] fix(adt-flow): expose diverged ownership path Keep the existing hash-verification boolean contract for fast paths, while including the first divergent owned-file path in working_tree_diverged diagnostics. --- packages/adt-flow/src/repository.ts | 17 +++++++++++++---- packages/adt-flow/src/service.ts | 6 ++++-- packages/adt-flow/tests/service.test.ts | 8 +++++++- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/packages/adt-flow/src/repository.ts b/packages/adt-flow/src/repository.ts index 4bb4edeef..65630c1b5 100644 --- a/packages/adt-flow/src/repository.ts +++ b/packages/adt-flow/src/repository.ts @@ -299,15 +299,24 @@ export function validateDesiredFiles(files: readonly DesiredFile[]): void { } } -export async function verifyOwnedHashes( +export async function findDivergedOwnedPath( root: string, files: readonly { path: string; hash: string }[], -): Promise { +): Promise { for (const file of files) { const content = await readText(root, file.path); - if (content === undefined || sha256(content) !== file.hash) return false; + if (content === undefined || sha256(content) !== file.hash) { + return file.path; + } } - return true; + return undefined; +} + +export async function verifyOwnedHashes( + root: string, + files: readonly { path: string; hash: string }[], +): Promise { + return (await findDivergedOwnedPath(root, files)) === undefined; } export async function planRepositoryChanges( diff --git a/packages/adt-flow/src/service.ts b/packages/adt-flow/src/service.ts index 7d70b1673..482ae4ad0 100644 --- a/packages/adt-flow/src/service.ts +++ b/packages/adt-flow/src/service.ts @@ -14,6 +14,7 @@ import { import { repositoryType } from './adt-client-adapter'; import { applyRepositoryPlan, + findDivergedOwnedPath, planRepositoryChanges, readText, verifyOwnedHashes, @@ -1204,11 +1205,12 @@ async function validateIndexedOwnership( identity, ctx.dependencies.format, ); - if (!(await verifyOwnedHashes(ctx.root, descriptor.ownedFiles))) { + const path = await findDivergedOwnedPath(ctx.root, descriptor.ownedFiles); + if (path !== undefined) { throw new AdtFlowError( 'working_tree_diverged', 'An indexed file differs from its recorded content hash.', - { object: identity.canonical }, + { object: identity.canonical, path }, ); } } diff --git a/packages/adt-flow/tests/service.test.ts b/packages/adt-flow/tests/service.test.ts index cede7c6ce..fc7b28d8c 100644 --- a/packages/adt-flow/tests/service.test.ts +++ b/packages/adt-flow/tests/service.test.ts @@ -669,7 +669,13 @@ describe('transport checkout', () => { await expect( flow.checkout({ root: workspace, transports: ['DEVK900001'], config }), - ).rejects.toMatchObject({ code: 'working_tree_diverged' }); + ).rejects.toMatchObject({ + code: 'working_tree_diverged', + details: { + object: expect.any(String), + path: 'src/feature/zcl_sample.clas.abap', + }, + }); expect(ports.readSource).not.toHaveBeenCalled(); expect( await readFile(