Skip to content

Commit 045b04f

Browse files
fix(project-context): canonicalize Skill IR paths in model digest (#191)
Fixes dev-services modelDigest regression from #185 by canonicalizing skillIr/hostDocuments paths in canonicalizeNormalizedModel.
1 parent 9df37f8 commit 045b04f

2 files changed

Lines changed: 69 additions & 0 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"agent-bundle": patch
3+
---
4+
5+
Fix root-independent model digest canonicalization for Skill IR fields added in #185.
6+
7+
`skillIr` and `hostDocuments` carried absolute filesystem paths into `modelDigest`, breaking cross-checkout identity for equivalent projects.

‎packages/agent-bundle/src/core/project-context.ts‎

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { readFileSync, realpathSync } from 'node:fs';
22
import { isAbsolute, join, relative, resolve } from 'node:path';
33

4+
import type { SkillHostDocument, SkillIr, SkillSidecarRef } from '../skills/ir.ts';
5+
import type { Diagnostic } from './diagnostics.ts';
46
import { digest } from './digest.ts';
57
import { deepFreeze } from './freeze.ts';
68
import { isInsideOrEqual } from './paths.ts';
@@ -209,6 +211,46 @@ const canonicalProvenance = (root: string, provenance: SourceProvenance): Source
209211
sourcePath: canonicalCompilerPath(root, provenance.sourcePath, 'Model provenance path'),
210212
});
211213

214+
const canonicalDiagnostic = (root: string, diagnostic: Diagnostic): Diagnostic => ({
215+
...diagnostic,
216+
...(diagnostic.generatedPath === undefined
217+
? {}
218+
: { generatedPath: canonicalCompilerPath(root, diagnostic.generatedPath, 'Diagnostic generated path') }),
219+
...(diagnostic.sourcePath === undefined
220+
? {}
221+
: { sourcePath: canonicalCompilerPath(root, diagnostic.sourcePath, 'Diagnostic source path') }),
222+
});
223+
224+
const canonicalSkillSidecar = (root: string, sidecar: SkillSidecarRef): SkillSidecarRef => ({
225+
...sidecar,
226+
...(sidecar.source === undefined
227+
? {}
228+
: { source: canonicalCompilerPath(root, sidecar.source, 'Skill sidecar source path') }),
229+
});
230+
231+
const canonicalSkillIr = (root: string, skillIr: SkillIr): SkillIr => ({
232+
...skillIr,
233+
diagnostics: skillIr.diagnostics.map((diagnostic) => canonicalDiagnostic(root, diagnostic)),
234+
resources: skillIr.resources.map((resource) => ({
235+
...resource,
236+
source: canonicalCompilerPath(root, resource.source, 'Skill IR resource path'),
237+
})),
238+
sidecars: skillIr.sidecars.map((sidecar) => canonicalSkillSidecar(root, sidecar)),
239+
source: canonicalCompilerPath(root, skillIr.source, 'Skill IR source path'),
240+
});
241+
242+
const canonicalHostDocuments = (
243+
root: string,
244+
hostDocuments: Readonly<Record<string, SkillHostDocument>>,
245+
): Readonly<Record<string, SkillHostDocument>> =>
246+
Object.fromEntries(Object.entries(hostDocuments)
247+
.sort(([left], [right]) => left.localeCompare(right))
248+
.map(([host, document]) => [host, {
249+
...document,
250+
diagnostics: document.diagnostics.map((diagnostic) => canonicalDiagnostic(root, diagnostic)),
251+
sidecars: document.sidecars.map((sidecar) => canonicalSkillSidecar(root, sidecar)),
252+
}]));
253+
212254
const modelPathReferences = (model: NormalizedPlugin): readonly string[] => [
213255
model.metadata.provenance.sourcePath,
214256
...(model.assets ?? []).flatMap((asset) => [asset.provenance.sourcePath, asset.source]),
@@ -226,6 +268,22 @@ const modelPathReferences = (model: NormalizedPlugin): readonly string[] => [
226268
skill.provenance.sourcePath,
227269
skill.source,
228270
...skill.resources.map((resource) => resource.source),
271+
...(skill.skillIr === undefined
272+
? []
273+
: [
274+
skill.skillIr.source,
275+
...skill.skillIr.resources.map((resource) => resource.source),
276+
...skill.skillIr.sidecars.flatMap((sidecar) => sidecar.source === undefined ? [] : [sidecar.source]),
277+
...skill.skillIr.diagnostics.flatMap((diagnostic) =>
278+
diagnostic.sourcePath === undefined ? [] : [diagnostic.sourcePath]),
279+
]),
280+
...(skill.hostDocuments === undefined
281+
? []
282+
: Object.values(skill.hostDocuments).flatMap((document) => [
283+
...document.diagnostics.flatMap((diagnostic) =>
284+
diagnostic.sourcePath === undefined ? [] : [diagnostic.sourcePath]),
285+
...document.sidecars.flatMap((sidecar) => sidecar.source === undefined ? [] : [sidecar.source]),
286+
])),
229287
]),
230288
...model.scripts.flatMap((script) => [script.provenance.sourcePath, script.source]),
231289
...model.mcpServers.flatMap((server) => [
@@ -367,11 +425,15 @@ export const canonicalizeNormalizedModel = (
367425
skills: detached.skills.map((skill) => ({
368426
...skill,
369427
dir: canonicalCompilerPath(root, skill.dir, 'Skill directory path'),
428+
...(skill.hostDocuments === undefined
429+
? {}
430+
: { hostDocuments: canonicalHostDocuments(root, skill.hostDocuments) }),
370431
provenance: canonicalProvenance(root, skill.provenance),
371432
resources: skill.resources.map((resource) => ({
372433
...resource,
373434
source: canonicalCompilerPath(root, resource.source, 'Skill resource path'),
374435
})),
436+
...(skill.skillIr === undefined ? {} : { skillIr: canonicalSkillIr(root, skill.skillIr) }),
375437
source: canonicalCompilerPath(root, skill.source, 'Skill source path'),
376438
})),
377439
targets: detached.targets.map((target) => ({

0 commit comments

Comments
 (0)