Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/sort-issuer-identities.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'agent-bundle': patch
---

Keep AB6005 issuer ordering stable across checkout paths. (#768)
2 changes: 1 addition & 1 deletion packages/agent-bundle/src/build/external-policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export const viewSelfContainmentDiagnostics = (
evidence.externals.map((external) => artifactDiagnostic(
'AB6005',
`Compiled MCP App view ${JSON.stringify(asset)} `
+ keptExternalClause(external, external.issuers.map((issuer) => posixRelativeWhenInside(projectRoot, issuer)))
+ keptExternalClause(external, external.issuers.map((issuer) => posixRelativeWhenInside(projectRoot, issuer)).sort())
+ 'a view inlines every module it loads.',
asset,
));
Expand Down
2 changes: 1 addition & 1 deletion packages/agent-bundle/src/build/rslib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -781,7 +781,7 @@ export const compileResultOf = (
externals: Object.freeze(record.externals.map((external): ExternalIR => ({
asset,
externalType: external.externalType,
issuers: external.issuers.map((issuer) => posixRelativeWhenInside(options.cwd, issuer)),
issuers: external.issuers.map((issuer) => posixRelativeWhenInside(options.cwd, issuer)).sort(),
Comment thread
ScriptedAlchemy marked this conversation as resolved.
kind: classifyExternal(external, { asset, emittedAssets: options.emittedAssets }),
request: external.request,
userRequest: external.userRequest,
Expand Down
33 changes: 32 additions & 1 deletion packages/agent-bundle/tests/compile-stages.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';

import type { CompilationEvidence } from '../src/build/compile-result.ts';
import { compileRslibSurfaces, settledRslibSurface } from '../src/build/compiler.ts';
import { generatedMetaModulePath, metaModuleSpecifier } from '../src/build/meta.ts';
import { buildRslibSurfaces, entryLibId, type RslibEntry } from '../src/build/rslib.ts';
import { buildRslibSurfaces, compileResultOf, entryLibId, type RslibEntry } from '../src/build/rslib.ts';
import { planCompileStages } from '../src/build/compile-stages.ts';
import type { AgentBundleMeta } from '../src/meta.ts';

Expand Down Expand Up @@ -107,6 +108,36 @@ const surfaceEntry = (name: string, outputRelativePath: string, source: string):
sourceInputs: [source],
});

describe('compileResultOf', () => {
it('sorts issuer identities after making project paths relative', () => {
const project = join(tmpdir(), 'a-agent-bundle-project');
const externalIssuer = join(tmpdir(), 'z-agent-bundle-source', 'terminal-capability.ts');
const projectIssuer = join(project, 'src', 'scripts', 'pad.ts');
const record: CompilationEvidence = {
compiler: 'agent-bundle-scripts-pad',
externals: [{
externalType: 'global',
issuers: [projectIssuer, externalIssuer].sort(),
request: 'fs',
userRequest: 'node:fs',
}],
modules: [],
};

const result = compileResultOf(record, {
asset: { path: 'scripts/pad.mjs', sourceInputs: [projectIssuer] },
cwd: project,
dependencyRoots: new Map(),
emittedAssets: new Set(['scripts/pad.mjs']),
});

expect(result.externals[0]?.issuers).toEqual([
externalIssuer,
'src/scripts/pad.ts',
].sort());
});
});

describe('buildRslibSurfaces', () => {
it('lowers every surface of the root through one Rslib instance and hands each surface its own evidence', async () => {
const outputRoot = await mkdtemp(join(tmpdir(), 'agent-bundle-compile-stages-'));
Expand Down
27 changes: 26 additions & 1 deletion packages/agent-bundle/tests/external-policy.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { describe, expect, it } from '@rstest/core';
import { tmpdir } from 'node:os';
import { join } from 'node:path';

import type { CompileResult, ExternalIR } from '../src/build/compile-result.ts';
import type { CompilationEvidence, CompileResult, ExternalIR } from '../src/build/compile-result.ts';
import {
classifyExternal,
externalizedSpecifiers,
selfContainmentDiagnostics,
viewSelfContainmentDiagnostics,
} from '../src/build/external-policy.ts';

const resultWith = (externals: readonly ExternalIR[]): CompileResult => ({
Expand Down Expand Up @@ -139,6 +142,28 @@ describe('selfContainmentDiagnostics', () => {
});
});

describe('viewSelfContainmentDiagnostics', () => {
it('sorts issuer identities after making project paths relative', () => {
const project = join(tmpdir(), 'a-agent-bundle-project');
const externalIssuer = join(tmpdir(), 'z-agent-bundle-source', 'view-runtime.ts');
const projectIssuer = join(project, 'views', 'status.tsx');
const evidence: CompilationEvidence = {
compiler: 'agent-bundle-mcp-app-status',
externals: [{
externalType: 'module',
issuers: [projectIssuer, externalIssuer].sort(),
request: 'react',
userRequest: 'react',
}],
modules: [],
};

expect(viewSelfContainmentDiagnostics(evidence, 'mcp-apps/status.html', project)[0]?.message).toBe(
`Compiled MCP App view "mcp-apps/status.html" keeps "react" external (module) from ${externalIssuer}, views/status.tsx; a view inlines every module it loads.`,
);
});
});

describe('externalizedSpecifiers', () => {
it('accepts builtins, disabled object entries, regular expressions, and functions', () => {
expect(externalizedSpecifiers('fs')).toEqual([]);
Expand Down
Loading