Merge pull request #317 from pikasTech/fix/310-artificer-workspace-cleanliness
Pipelines as Code CI / agentrun-nc01-v02-ci-c38a90d6cd102f5ee4ceeaf257778166f6d1f1bf Success
Pipelines as Code CI / agentrun-nc01-v02-ci-c38a90d6cd102f5ee4ceeaf257778166f6d1f1bf Success
修复 Artificer 受管能力覆盖污染源码视图
This commit is contained in:
@@ -13,6 +13,7 @@ const maxPromptRefBytes = 16 * 1024;
|
||||
const maxInitialPromptBytes = 64 * 1024;
|
||||
const skillSummaryChars = 600;
|
||||
const runtimeBundledToolNames = Object.freeze(bundledWorkReadyTools.map((tool) => tool.name));
|
||||
const managedOverlayDirectory = "agentrun-managed-overlay";
|
||||
|
||||
export interface MaterializedResourceBundle {
|
||||
workspacePath: string;
|
||||
@@ -173,8 +174,10 @@ export async function materializeResourceBundle(resourceBundleRef: ResourceBundl
|
||||
checkoutPath = defaultCheckout.checkoutPath;
|
||||
const stagePath = await stagePrimaryWorkspace(defaultCheckout.checkoutPath, workspacePath, runScope);
|
||||
try {
|
||||
const stagedBundles = await materializeGitBundles(stagePath, resourceBundleRef, defaultSource, defaultCheckout, checkoutFor);
|
||||
const stageOverlayRoot = await prepareManagedOverlay(stagePath);
|
||||
const stagedBundles = await materializeGitBundles(stageOverlayRoot, resourceBundleRef, defaultSource, defaultCheckout, checkoutFor);
|
||||
const stagedProvenance = createPrimaryWorkspaceProvenance(resourceBundleRef, workspaceRef, defaultCheckout, stagedBundles);
|
||||
await assertCleanPrimarySourceView(stagePath);
|
||||
await verifyPrimaryWorkspace(stagePath, workspaceRef, stagedProvenance, "first-materialization", true);
|
||||
await writePrimaryWorkspaceProvenance(stagePath, stagedProvenance);
|
||||
const installed = await installPrimaryWorkspace(stagePath, workspacePath);
|
||||
@@ -192,8 +195,9 @@ export async function materializeResourceBundle(resourceBundleRef: ResourceBundl
|
||||
}
|
||||
|
||||
const primaryWorkspace = await verifyPrimaryWorkspace(workspacePath, workspaceRef, provenance, workspaceLifecycle, false);
|
||||
const tools = await prepareGitBundleTools(workspacePath, env);
|
||||
const skills = await discoverGitBundleSkills(workspacePath, materializedBundles);
|
||||
const managedOverlayRoot = await verifyManagedOverlay(workspacePath, materializedBundles);
|
||||
const tools = await prepareGitBundleTools(managedOverlayRoot, env);
|
||||
const skills = await discoverGitBundleSkills(managedOverlayRoot, materializedBundles);
|
||||
const requiredSkills = materializeRequiredSkills(resourceBundleRef.requiredSkills ?? [], skills.items);
|
||||
const prompts = await materializePromptRefs(checkoutPath ?? workspacePath, resourceBundleRef.promptRefs ?? []);
|
||||
const initialPrompt = assembleInitialPrompt(prompts.items, skills.items);
|
||||
@@ -221,10 +225,18 @@ export async function materializeResourceBundle(resourceBundleRef: ResourceBundl
|
||||
workspaceLifecycle,
|
||||
resolutionAuthority: provenance.resolutionAuthority,
|
||||
primaryWorkspace,
|
||||
managedOverlay: {
|
||||
kind: "git-metadata",
|
||||
root: pathSummary(managedOverlayRoot),
|
||||
rootRelativeToWorkspace: `.git/${managedOverlayDirectory}`,
|
||||
sourceViewIsolated: true,
|
||||
targetPathAuthority: "resourceBundleRef.bundles[].targetPath",
|
||||
valuesPrinted: false,
|
||||
},
|
||||
gitTransport: gitTransportSummary({ scope: "resource-bundle" }),
|
||||
bundles: {
|
||||
count: materializedBundles.length,
|
||||
items: materializedBundles.map((item) => ({ ...item, valuesPrinted: false })),
|
||||
items: materializedBundles.map((item) => ({ ...item, logicalTargetPath: item.targetPath, storageKind: "git-metadata-overlay", valuesPrinted: false })),
|
||||
valuesPrinted: false,
|
||||
},
|
||||
tools: tools.event,
|
||||
@@ -608,6 +620,57 @@ async function installPrimaryWorkspace(stagePath: string, workspacePath: string)
|
||||
}
|
||||
}
|
||||
|
||||
async function prepareManagedOverlay(workspacePath: string): Promise<string> {
|
||||
const overlayRoot = managedOverlayRoot(workspacePath);
|
||||
await rm(overlayRoot, { recursive: true, force: true });
|
||||
await mkdir(overlayRoot, { recursive: true });
|
||||
return overlayRoot;
|
||||
}
|
||||
|
||||
async function verifyManagedOverlay(workspacePath: string, bundles: MaterializedGitBundle[]): Promise<string> {
|
||||
const overlayRoot = managedOverlayRoot(workspacePath);
|
||||
let root;
|
||||
try {
|
||||
root = await stat(overlayRoot);
|
||||
} catch (error) {
|
||||
throw new AgentRunError("schema-invalid", "primary workspace managed capability overlay is missing", { httpStatus: 400, details: { overlayRoot: pathSummary(overlayRoot), error: fileErrorSummary(error), valuesPrinted: false } });
|
||||
}
|
||||
if (!root.isDirectory()) {
|
||||
throw new AgentRunError("schema-invalid", "primary workspace managed capability overlay is not a directory", { httpStatus: 400, details: { overlayRoot: pathSummary(overlayRoot), valuesPrinted: false } });
|
||||
}
|
||||
for (const [index, bundle] of bundles.entries()) {
|
||||
const target = resolveManagedOverlayTargetPath(overlayRoot, bundle.targetPath, `bundleProvenance[${index}].targetPath`);
|
||||
let targetStat;
|
||||
try {
|
||||
targetStat = await stat(target);
|
||||
} catch (error) {
|
||||
throw new AgentRunError("schema-invalid", "primary workspace managed capability target is missing", { httpStatus: 400, details: { index, targetPath: bundle.targetPath, overlayRoot: pathSummary(overlayRoot), error: fileErrorSummary(error), valuesPrinted: false } });
|
||||
}
|
||||
const actualKind = targetStat.isDirectory() ? "directory" : targetStat.isFile() ? "file" : "unsupported";
|
||||
if (actualKind !== bundle.sourceKind) {
|
||||
throw new AgentRunError("schema-invalid", "primary workspace managed capability target kind conflicts with frozen provenance", { httpStatus: 400, details: { index, targetPath: bundle.targetPath, expectedKind: bundle.sourceKind, actualKind, valuesPrinted: false } });
|
||||
}
|
||||
}
|
||||
return overlayRoot;
|
||||
}
|
||||
|
||||
function managedOverlayRoot(workspacePath: string): string {
|
||||
return path.join(workspacePath, ".git", managedOverlayDirectory);
|
||||
}
|
||||
|
||||
async function assertCleanPrimarySourceView(workspacePath: string): Promise<void> {
|
||||
const status = await git(["status", "--porcelain=v1", "--untracked-files=all"], workspacePath);
|
||||
if (status.stdout.trim().length > 0) {
|
||||
throw new AgentRunError("infra-failed", "managed capability assembly polluted the staged primary Git source view", {
|
||||
httpStatus: 500,
|
||||
details: {
|
||||
changedEntries: status.stdout.split(/\r?\n/u).filter((line) => line.trim().length > 0).length,
|
||||
valuesPrinted: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function verifyPrimaryWorkspace(workspacePath: string, workspaceRef: WorkspaceRef, provenance: PrimaryWorkspaceProvenance, workspaceLifecycle: "first-materialization" | "continuation-reuse", requireExactSource: boolean): Promise<JsonRecord> {
|
||||
let rootStat;
|
||||
let gitStat;
|
||||
@@ -714,13 +777,13 @@ function cleanGithubPath(owner: string, repo: string): string | null {
|
||||
return `${cleanOwner}/${cleanRepo}`;
|
||||
}
|
||||
|
||||
async function materializeGitBundles(workspacePath: string, resourceBundleRef: ResourceBundleRef, defaultSource: GitBundleSource, defaultCheckout: GitCheckout, checkoutFor: (source: GitBundleSource, sourceContext: GitBundleSourceContext) => Promise<GitCheckout>): Promise<MaterializedGitBundle[]> {
|
||||
async function materializeGitBundles(overlayRoot: string, resourceBundleRef: ResourceBundleRef, defaultSource: GitBundleSource, defaultCheckout: GitCheckout, checkoutFor: (source: GitBundleSource, sourceContext: GitBundleSourceContext) => Promise<GitCheckout>): Promise<MaterializedGitBundle[]> {
|
||||
const items: MaterializedGitBundle[] = [];
|
||||
for (const [index, bundle] of resourceBundleRef.bundles.entries()) {
|
||||
const gitSource = bundleGitSource(bundle, resourceBundleRef, defaultSource);
|
||||
const checkout = gitSource === defaultSource ? defaultCheckout : await checkoutFor(gitSource, { kind: "bundle", index, name: bundle.name ?? null });
|
||||
const source = resolveBundlePath(checkout.checkoutPath, bundle.subpath, `bundles[${index}].subpath`);
|
||||
const target = resolveWorkspaceTargetPath(workspacePath, bundle.targetPath, `bundles[${index}].target_path`);
|
||||
const target = resolveManagedOverlayTargetPath(overlayRoot, bundle.targetPath, `bundles[${index}].target_path`);
|
||||
let sourceStat;
|
||||
try {
|
||||
sourceStat = await stat(source);
|
||||
@@ -747,8 +810,8 @@ function installedToolShim(sourcePath: string): string {
|
||||
return `#!/usr/bin/env sh\nexec ${shellSingleQuote(sourcePath)} "$@"\n`;
|
||||
}
|
||||
|
||||
async function prepareGitBundleTools(workspacePath: string, env: NodeJS.ProcessEnv): Promise<{ binPath?: string; event: JsonRecord }> {
|
||||
const sourceBinPath = path.join(workspacePath, "tools");
|
||||
async function prepareGitBundleTools(overlayRoot: string, env: NodeJS.ProcessEnv): Promise<{ binPath?: string; event: JsonRecord }> {
|
||||
const sourceBinPath = path.join(overlayRoot, "tools");
|
||||
const installedBinPath = optionalNonEmpty(env.AGENTRUN_RESOURCE_BIN_PATH);
|
||||
const runtimeBinPath = installedBinPath ?? sourceBinPath;
|
||||
const runtimeToolTargetBinPath = runtimeBinPath;
|
||||
@@ -862,8 +925,8 @@ async function materializePromptRefs(checkoutPath: string, refs: NonNullable<Res
|
||||
};
|
||||
}
|
||||
|
||||
async function discoverGitBundleSkills(workspacePath: string, bundles: MaterializedGitBundle[]): Promise<{ items: MaterializedSkillRef[]; skillsDir?: string; event: JsonRecord }> {
|
||||
const skillsDir = path.join(workspacePath, ".agents", "skills");
|
||||
async function discoverGitBundleSkills(overlayRoot: string, bundles: MaterializedGitBundle[]): Promise<{ items: MaterializedSkillRef[]; skillsDir?: string; event: JsonRecord }> {
|
||||
const skillsDir = path.join(overlayRoot, ".agents", "skills");
|
||||
let entries;
|
||||
try {
|
||||
entries = await readdir(skillsDir, { withFileTypes: true });
|
||||
@@ -984,8 +1047,8 @@ function assembleInitialPrompt(promptRefs: MaterializedPromptRef[], skills: Mate
|
||||
if (skills.length > 0) {
|
||||
const lines = [
|
||||
"## Resource Skills",
|
||||
"The following required runtime skills are mounted in the current workspace. Use these bundle skills instead of default model skill guesses.",
|
||||
...skills.map((skill) => `- ${skill.name}: ${skill.summary || "No summary provided."} manifest=.agents/skills/${skill.aggregateAs}/SKILL.md source=${skill.path} required=${skill.required}`),
|
||||
"The following required runtime skills are published through the runtime-managed AGENTRUN_SKILLS_DIRS directory outside the Git source view. Use these bundle skills instead of default model skill guesses.",
|
||||
...skills.map((skill) => `- ${skill.name}: ${skill.summary || "No summary provided."} manifest=${skill.registryPath} skillsDirEnv=AGENTRUN_SKILLS_DIRS source=${skill.path} required=${skill.required}`),
|
||||
];
|
||||
sections.push(lines.join("\n"));
|
||||
}
|
||||
@@ -1212,10 +1275,10 @@ function resolveBundlePath(checkoutPath: string, relativePath: string, fieldName
|
||||
return resolved;
|
||||
}
|
||||
|
||||
function resolveWorkspaceTargetPath(workspacePath: string, relativePath: string, fieldName: string): string {
|
||||
const resolved = path.resolve(workspacePath, relativePath);
|
||||
const root = path.resolve(workspacePath);
|
||||
if (resolved === root || !resolved.startsWith(`${root}${path.sep}`)) throw new AgentRunError("schema-invalid", `${fieldName} escaped workspace`, { httpStatus: 400 });
|
||||
function resolveManagedOverlayTargetPath(overlayRoot: string, relativePath: string, fieldName: string): string {
|
||||
const resolved = path.resolve(overlayRoot, relativePath);
|
||||
const root = path.resolve(overlayRoot);
|
||||
if (resolved === root || !resolved.startsWith(`${root}${path.sep}`)) throw new AgentRunError("schema-invalid", `${fieldName} escaped managed overlay`, { httpStatus: 400 });
|
||||
return resolved;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,9 +19,16 @@ const selfTest: SelfTestCase = async (context) => {
|
||||
const primary = await createRepo(context, "primary-source", {
|
||||
"AGENTS.md": "# Primary workspace\n",
|
||||
"package.json": "{\"name\":\"primary-workspace-selftest\"}\n",
|
||||
"tools/source-tool": "#!/usr/bin/env sh\necho primary-source-tool\n",
|
||||
"tools/probe": "#!/usr/bin/env sh\necho primary-source-probe\n",
|
||||
".agents/skills/source-skill/SKILL.md": "# Source-owned skill\n",
|
||||
".agents/skills/dad-dev/SKILL.md": "# Source-owned dad-dev skill\n",
|
||||
});
|
||||
const addon = await createRepo(context, "addon-source", {
|
||||
"capability/probe": "#!/usr/bin/env sh\necho capability\n",
|
||||
"dad-dev/SKILL.md": "---\ndescription: Managed dad-dev capability\n---\n",
|
||||
"git-spec/SKILL.md": "---\ndescription: Managed git-spec capability\n---\n",
|
||||
"unidesk-gh/SKILL.md": "---\ndescription: Managed unidesk-gh capability\n---\n",
|
||||
});
|
||||
const workspaceRef: WorkspaceRef = { kind: "opaque", path: "." };
|
||||
const resourceBundleRef: ResourceBundleRef = {
|
||||
@@ -29,7 +36,14 @@ const selfTest: SelfTestCase = async (context) => {
|
||||
repoUrl: primary.path,
|
||||
ref: primary.branch,
|
||||
commitId: primary.commitId,
|
||||
bundles: [{ name: "addon", repoUrl: addon.path, commitId: addon.commitId, subpath: "capability", targetPath: "tools" }],
|
||||
bundles: [
|
||||
{ name: "primary-skills", subpath: ".agents/skills", targetPath: ".agents/skills" },
|
||||
{ name: "addon-tools", repoUrl: addon.path, commitId: addon.commitId, subpath: "capability", targetPath: "tools" },
|
||||
{ name: "dad-dev-skill", repoUrl: addon.path, commitId: addon.commitId, subpath: "dad-dev", targetPath: ".agents/skills/dad-dev" },
|
||||
{ name: "git-spec-skill", repoUrl: addon.path, commitId: addon.commitId, subpath: "git-spec", targetPath: ".agents/skills/git-spec" },
|
||||
{ name: "unidesk-gh-skill", repoUrl: addon.path, commitId: addon.commitId, subpath: "unidesk-gh", targetPath: ".agents/skills/unidesk-gh" },
|
||||
],
|
||||
requiredSkills: [{ name: "source-skill" }, { name: "dad-dev" }, { name: "git-spec" }, { name: "unidesk-gh" }],
|
||||
};
|
||||
const workspacePath = path.join(context.tmp, "primary-workspace-materialized");
|
||||
const env = { AGENTRUN_WORKSPACE_ROOT: path.join(context.tmp, "primary-workspace-assembly"), AGENTRUN_WORKSPACE_PATH: workspacePath, AGENTRUN_RUN_ID: "run_primary_workspace_selftest" };
|
||||
@@ -40,7 +54,25 @@ const selfTest: SelfTestCase = async (context) => {
|
||||
await access(path.join(workspacePath, ".git"));
|
||||
await access(path.join(workspacePath, "AGENTS.md"));
|
||||
await access(path.join(workspacePath, "package.json"));
|
||||
await access(path.join(workspacePath, "tools", "probe"));
|
||||
await access(path.join(workspacePath, "tools", "source-tool"));
|
||||
await access(path.join(workspacePath, ".agents", "skills", "source-skill", "SKILL.md"));
|
||||
assert.equal(await readFile(path.join(workspacePath, "tools", "probe"), "utf8"), "#!/usr/bin/env sh\necho primary-source-probe\n");
|
||||
assert.equal(await readFile(path.join(workspacePath, ".agents", "skills", "dad-dev", "SKILL.md"), "utf8"), "# Source-owned dad-dev skill\n");
|
||||
assert.ok(first.binPath);
|
||||
assert.ok(first.skillsDir);
|
||||
assert.match(first.binPath, /\.git\/agentrun-managed-overlay\/tools$/u);
|
||||
assert.match(first.skillsDir, /\.git\/agentrun-managed-overlay\/\.agents\/skills$/u);
|
||||
await access(path.join(first.binPath, "probe"));
|
||||
await access(path.join(first.skillsDir, "source-skill", "SKILL.md"));
|
||||
await access(path.join(first.skillsDir, "dad-dev", "SKILL.md"));
|
||||
await access(path.join(first.skillsDir, "git-spec", "SKILL.md"));
|
||||
await access(path.join(first.skillsDir, "unidesk-gh", "SKILL.md"));
|
||||
assert.equal(await readFile(path.join(first.binPath, "probe"), "utf8"), "#!/usr/bin/env sh\necho capability\n");
|
||||
assert.match(await readFile(path.join(first.skillsDir, "dad-dev", "SKILL.md"), "utf8"), /Managed dad-dev capability/u);
|
||||
assert.match(first.initialPrompt?.text ?? "", /manifest=.*\.git\/agentrun-managed-overlay\/\.agents\/skills\/dad-dev\/SKILL\.md skillsDirEnv=AGENTRUN_SKILLS_DIRS/u);
|
||||
assert.match(first.initialPrompt?.text ?? "", /manifest=.*\.git\/agentrun-managed-overlay\/\.agents\/skills\/git-spec\/SKILL\.md skillsDirEnv=AGENTRUN_SKILLS_DIRS/u);
|
||||
assert.match(first.initialPrompt?.text ?? "", /manifest=.*\.git\/agentrun-managed-overlay\/\.agents\/skills\/unidesk-gh\/SKILL\.md skillsDirEnv=AGENTRUN_SKILLS_DIRS/u);
|
||||
assert.equal((await execFile("git", ["status", "--porcelain=v1", "--untracked-files=all"], { cwd: workspacePath })).stdout, "");
|
||||
assert.equal((await execFile("git", ["rev-parse", "HEAD"], { cwd: workspacePath })).stdout.trim(), primary.commitId);
|
||||
assert.equal((await execFile("git", ["branch", "--show-current"], { cwd: workspacePath })).stdout.trim(), primary.branch);
|
||||
assert.equal((await execFile("git", ["remote", "get-url", "origin"], { cwd: workspacePath })).stdout.trim(), primary.path);
|
||||
@@ -51,17 +83,37 @@ const selfTest: SelfTestCase = async (context) => {
|
||||
assert.equal(firstPrimary.workspaceLifecycle, "first-materialization");
|
||||
assert.equal(firstPrimary.resolutionAuthority, "declared-commit");
|
||||
assert.equal(firstPrimary.writable, true);
|
||||
const managedOverlay = first.event.managedOverlay as JsonRecord;
|
||||
assert.equal(managedOverlay.kind, "git-metadata");
|
||||
assert.equal(managedOverlay.rootRelativeToWorkspace, ".git/agentrun-managed-overlay");
|
||||
assert.equal(managedOverlay.sourceViewIsolated, true);
|
||||
const bundleItems = ((first.event.bundles as JsonRecord).items as JsonRecord[]);
|
||||
assert.deepEqual(bundleItems.map((item) => item.logicalTargetPath), [".agents/skills", "tools", ".agents/skills/dad-dev", ".agents/skills/git-spec", ".agents/skills/unidesk-gh"]);
|
||||
assert.equal(bundleItems.every((item) => item.storageKind === "git-metadata-overlay"), true);
|
||||
assert.deepEqual(((first.event.requiredSkills as JsonRecord).names), ["source-skill", "dad-dev", "git-spec", "unidesk-gh"]);
|
||||
const provenancePath = path.join(workspacePath, ".git", "agentrun-primary-workspace.json");
|
||||
const persistedProvenance = JSON.parse(await readFile(provenancePath, "utf8")) as JsonRecord;
|
||||
assert.equal(persistedProvenance.resolutionAuthority, "declared-commit");
|
||||
assert.equal(((persistedProvenance.resolvedSource as JsonRecord).commitId), primary.commitId);
|
||||
|
||||
await writeFile(path.join(workspacePath, "dirty-from-previous-attempt.txt"), "preserve me\n", "utf8");
|
||||
await writeFile(path.join(workspacePath, "tools", "source-tool"), "#!/usr/bin/env sh\necho source-modified\n", "utf8");
|
||||
const dirtyStatus = (await execFile("git", ["status", "--porcelain=v1", "--untracked-files=all"], { cwd: workspacePath })).stdout;
|
||||
assert.match(dirtyStatus, / M tools\/source-tool/u);
|
||||
assert.match(dirtyStatus, /\?\? dirty-from-previous-attempt\.txt/u);
|
||||
const replay = await materializeResourceBundle(resourceBundleRef, workspaceRef, env);
|
||||
assert.ok(replay);
|
||||
assert.equal(await readFile(path.join(workspacePath, "dirty-from-previous-attempt.txt"), "utf8"), "preserve me\n");
|
||||
assert.equal(await readFile(path.join(workspacePath, "tools", "source-tool"), "utf8"), "#!/usr/bin/env sh\necho source-modified\n");
|
||||
assert.equal(replay.binPath, first.binPath);
|
||||
assert.equal(replay.skillsDir, first.skillsDir);
|
||||
await access(path.join(replay.binPath!, "probe"));
|
||||
await access(path.join(replay.skillsDir!, "dad-dev", "SKILL.md"));
|
||||
assert.equal((replay.event.primaryWorkspace as JsonRecord).workspaceLifecycle, "continuation-reuse");
|
||||
assert.equal((replay.event.primaryWorkspace as JsonRecord).provenanceHash, firstPrimary.provenanceHash);
|
||||
const replayStatus = (await execFile("git", ["status", "--porcelain=v1", "--untracked-files=all"], { cwd: workspacePath })).stdout;
|
||||
assert.match(replayStatus, / M tools\/source-tool/u);
|
||||
assert.match(replayStatus, /\?\? dirty-from-previous-attempt\.txt/u);
|
||||
|
||||
await assert.rejects(
|
||||
() => materializeResourceBundle({ ...resourceBundleRef, ref: "changed-source-ref" }, workspaceRef, { ...env, AGENTRUN_RUN_ID: "run_changed_source_contract" }),
|
||||
@@ -103,6 +155,7 @@ const selfTest: SelfTestCase = async (context) => {
|
||||
const mutablePrimary = await createRepo(context, "mutable-primary-source", {
|
||||
"AGENTS.md": "# Frozen primary workspace\n",
|
||||
"package.json": "{\"name\":\"mutable-primary-workspace-selftest\"}\n",
|
||||
"tools/source-tool": "#!/usr/bin/env sh\necho mutable-source-tool\n",
|
||||
});
|
||||
const mutableBundle: ResourceBundleRef = {
|
||||
kind: "gitbundle",
|
||||
@@ -117,6 +170,8 @@ const selfTest: SelfTestCase = async (context) => {
|
||||
const mutableFirstPrimary = mutableFirst.event.primaryWorkspace as JsonRecord;
|
||||
assert.equal(mutableFirstPrimary.sourceCommit, mutablePrimary.commitId);
|
||||
assert.equal(mutableFirstPrimary.resolutionAuthority, "runner-first-materialization");
|
||||
assert.ok(mutableFirst.binPath);
|
||||
await access(path.join(mutableFirst.binPath, "probe"));
|
||||
|
||||
const workspaceSuccessorCommit = await commitFile(mutableWorkspacePath, "workspace-successor.txt", "workspace successor\n", "workspace successor");
|
||||
await writeFile(path.join(mutableWorkspacePath, "AGENTS.md"), "# Dirty continuation state\n", "utf8");
|
||||
@@ -125,6 +180,7 @@ const selfTest: SelfTestCase = async (context) => {
|
||||
assert.notEqual(advancedUpstreamCommit, mutablePrimary.commitId);
|
||||
assert.notEqual(workspaceSuccessorCommit, advancedUpstreamCommit);
|
||||
await rename(mutablePrimary.path, `${mutablePrimary.path}-unavailable-after-advance`);
|
||||
await rename(addon.path, `${addon.path}-unavailable-after-first-materialization`);
|
||||
|
||||
const mutableReplay = await materializeResourceBundle(mutableBundle, workspaceRef, { ...mutableEnv, AGENTRUN_RUN_ID: "run_mutable_primary_continuation" });
|
||||
assert.ok(mutableReplay);
|
||||
@@ -205,6 +261,10 @@ const selfTest: SelfTestCase = async (context) => {
|
||||
"primary-workspace-exact-source-materialization",
|
||||
"primary-workspace-provenance-and-declared-origin",
|
||||
"primary-workspace-empty-root-atomic-installation",
|
||||
"managed-capability-overlay-keeps-primary-source-view-clean",
|
||||
"managed-capability-overlay-preserves-nested-targets",
|
||||
"managed-capability-overlay-publishes-tools-and-skills",
|
||||
"real-primary-source-modifications-remain-git-visible",
|
||||
"primary-workspace-continuation-preserves-dirty-tree",
|
||||
"primary-workspace-continuation-freezes-mutable-ref",
|
||||
"primary-workspace-continuation-does-not-fetch",
|
||||
|
||||
Reference in New Issue
Block a user