Compare commits

...

2 Commits

Author SHA1 Message Date
Dotta
6b6ecf2377 Hide project execution workspace config for now
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-16 18:12:48 -05:00
Dotta
084bfd3d99 Treat Codex bootstrap logs as stdout
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-16 18:09:43 -05:00
5 changed files with 39 additions and 11 deletions

View File

@@ -94,7 +94,7 @@ export async function prepareWorktreeCodexHome(
}
await onLog(
"stderr",
"stdout",
`[paperclip] Using worktree-isolated Codex home "${targetHome}" (seeded from "${sourceHome}").\n`,
);
return targetHome;

View File

@@ -110,7 +110,7 @@ export async function ensureCodexSkillsInjected(
);
for (const skillName of removedSkills) {
await onLog(
"stderr",
"stdout",
`[paperclip] Removed maintainer-only Codex skill "${skillName}" from ${skillsHome}\n`,
);
}
@@ -137,7 +137,7 @@ export async function ensureCodexSkillsInjected(
await fs.symlink(entry.source, target);
}
await onLog(
"stderr",
"stdout",
`[paperclip] Repaired Codex skill "${entry.name}" into ${skillsHome}\n`,
);
continue;
@@ -148,7 +148,7 @@ export async function ensureCodexSkillsInjected(
if (result === "skipped") continue;
await onLog(
"stderr",
"stdout",
`[paperclip] ${result === "repaired" ? "Repaired" : "Injected"} Codex skill "${entry.name}" into ${skillsHome}\n`,
);
} catch (err) {
@@ -353,7 +353,7 @@ export async function execute(ctx: AdapterExecutionContext): Promise<AdapterExec
`Resolve any relative file references from ${instructionsDir}.\n\n`;
instructionsChars = instructionsPrefix.length;
await onLog(
"stderr",
"stdout",
`[paperclip] Loaded agent instructions file: ${instructionsFilePath}\n`,
);
} catch (err) {

View File

@@ -35,6 +35,11 @@ type CapturePayload = {
paperclipEnvKeys: string[];
};
type LogEntry = {
stream: "stdout" | "stderr";
chunk: string;
};
describe("codex execute", () => {
it("uses a worktree-isolated CODEX_HOME while preserving shared auth and config", async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-codex-execute-"));
@@ -62,6 +67,7 @@ describe("codex execute", () => {
process.env.CODEX_HOME = sharedCodexHome;
try {
const logs: LogEntry[] = [];
const result = await execute({
runId: "run-1",
agent: {
@@ -87,7 +93,9 @@ describe("codex execute", () => {
},
context: {},
authToken: "run-jwt-token",
onLog: async () => {},
onLog: async (stream, chunk) => {
logs.push({ stream, chunk });
},
});
expect(result.exitCode).toBe(0);
@@ -116,6 +124,18 @@ describe("codex execute", () => {
expect((await fs.lstat(isolatedConfig)).isFile()).toBe(true);
expect(await fs.readFile(isolatedConfig, "utf8")).toBe('model = "codex-mini-latest"\n');
expect((await fs.lstat(isolatedSkill)).isSymbolicLink()).toBe(true);
expect(logs).toContainEqual(
expect.objectContaining({
stream: "stdout",
chunk: expect.stringContaining("Using worktree-isolated Codex home"),
}),
);
expect(logs).toContainEqual(
expect.objectContaining({
stream: "stdout",
chunk: expect.stringContaining('Injected Codex skill "paperclip"'),
}),
);
} finally {
if (previousHome === undefined) delete process.env.HOME;
else process.env.HOME = previousHome;

View File

@@ -50,10 +50,10 @@ describe("codex local adapter skill injection", () => {
await createPaperclipRepoSkill(oldRepo, "paperclip");
await fs.symlink(path.join(oldRepo, "skills", "paperclip"), path.join(skillsHome, "paperclip"));
const logs: string[] = [];
const logs: Array<{ stream: "stdout" | "stderr"; chunk: string }> = [];
await ensureCodexSkillsInjected(
async (_stream, chunk) => {
logs.push(chunk);
async (stream, chunk) => {
logs.push({ stream, chunk });
},
{
skillsHome,
@@ -64,7 +64,12 @@ describe("codex local adapter skill injection", () => {
expect(await fs.realpath(path.join(skillsHome, "paperclip"))).toBe(
await fs.realpath(path.join(currentRepo, "skills", "paperclip")),
);
expect(logs.some((line) => line.includes('Repaired Codex skill "paperclip"'))).toBe(true);
expect(logs).toContainEqual(
expect.objectContaining({
stream: "stdout",
chunk: expect.stringContaining('Repaired Codex skill "paperclip"'),
}),
);
});
it("preserves a custom Codex skill symlink outside Paperclip repo checkouts", async () => {

View File

@@ -26,7 +26,9 @@ const PROJECT_STATUSES = [
{ value: "cancelled", label: "Cancelled" },
];
// TODO(issue-worktree-support): re-enable this UI once the workflow is ready to ship.
// PAP-525 / TODO(issue-worktree-support): keep the project-level execution
// workspace controls implemented, but hide them from project configuration
// until the workflow is ready to ship again.
const SHOW_EXPERIMENTAL_ISSUE_WORKTREE_UI = false;
interface ProjectPropertiesProps {
@@ -712,6 +714,7 @@ export function ProjectProperties({ project, onUpdate, onFieldUpdate, getFieldSa
)}
</div>
{/* Hidden for now per PAP-525. Keep this section intact so it can be restored later. */}
{SHOW_EXPERIMENTAL_ISSUE_WORKTREE_UI && (
<>
<Separator className="my-4" />