Clarify Codex instruction sources

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
dotta 2026-03-23 16:55:10 -05:00
parent c0c1fd17cb
commit 19154d0fec
6 changed files with 76 additions and 6 deletions

View file

@ -40,6 +40,12 @@ pnpm paperclipai agent local-cli codexcoder --company-id <company-id>
This installs any missing skills, creates an agent API key, and prints shell exports to run as that agent. This installs any missing skills, creates an agent API key, and prints shell exports to run as that agent.
## Instructions Resolution
If `instructionsFilePath` is configured, Paperclip reads that file and prepends it to the stdin prompt sent to `codex exec` on every run.
This is separate from any workspace-level instruction discovery that Codex itself performs in the run `cwd`. Paperclip does not disable Codex-native repo instruction files, so a repo-local `AGENTS.md` may still be loaded by Codex in addition to the Paperclip-managed agent instructions.
## Environment Test ## Environment Test
The environment test checks: The environment test checks:

View file

@ -40,6 +40,8 @@ Operational fields:
Notes: Notes:
- Prompts are piped via stdin (Codex receives "-" prompt argument). - Prompts are piped via stdin (Codex receives "-" prompt argument).
- If instructionsFilePath is configured, Paperclip prepends that file's contents to the stdin prompt on every run.
- Codex exec automatically applies repo-scoped AGENTS.md instructions from the active workspace. Paperclip cannot suppress that discovery in exec mode, so repo AGENTS.md files may still apply even when you only configured an explicit instructionsFilePath.
- Paperclip injects desired local skills into the active workspace's ".agents/skills" directory at execution time so Codex can discover "$paperclip" and related skills without coupling them to the user's login home. - Paperclip injects desired local skills into the active workspace's ".agents/skills" directory at execution time so Codex can discover "$paperclip" and related skills without coupling them to the user's login home.
- Unless explicitly overridden in adapter config, Paperclip runs Codex with a per-company managed CODEX_HOME under the active Paperclip instance and seeds auth/config from the shared Codex home (the CODEX_HOME env var, when set, or ~/.codex). - Unless explicitly overridden in adapter config, Paperclip runs Codex with a per-company managed CODEX_HOME under the active Paperclip instance and seeds auth/config from the shared Codex home (the CODEX_HOME env var, when set, or ~/.codex).
- Some model/tool combinations reject certain effort levels (for example minimal with web search enabled). - Some model/tool combinations reject certain effort levels (for example minimal with web search enabled).

View file

@ -427,16 +427,22 @@ export async function execute(ctx: AdapterExecutionContext): Promise<AdapterExec
); );
} }
} }
const repoAgentsNote =
"Codex exec automatically applies repo-scoped AGENTS.md instructions from the current workspace; Paperclip does not currently suppress that discovery.";
const commandNotes = (() => { const commandNotes = (() => {
if (!instructionsFilePath) return [] as string[]; if (!instructionsFilePath) {
return [repoAgentsNote];
}
if (instructionsPrefix.length > 0) { if (instructionsPrefix.length > 0) {
return [ return [
`Loaded agent instructions from ${instructionsFilePath}`, `Loaded agent instructions from ${instructionsFilePath}`,
`Prepended instructions + path directive to stdin prompt (relative references from ${instructionsDir}).`, `Prepended instructions + path directive to stdin prompt (relative references from ${instructionsDir}).`,
repoAgentsNote,
]; ];
} }
return [ return [
`Configured instructionsFilePath ${instructionsFilePath}, but file could not be read; continuing without injected instructions.`, `Configured instructionsFilePath ${instructionsFilePath}, but file could not be read; continuing without injected instructions.`,
repoAgentsNote,
]; ];
})(); })();
const bootstrapPromptTemplate = asString(config.bootstrapPromptTemplate, ""); const bootstrapPromptTemplate = asString(config.bootstrapPromptTemplate, "");

View file

@ -139,6 +139,62 @@ describe("codex execute", () => {
} }
}); });
it("emits a command note that Codex auto-applies repo-scoped AGENTS.md files", async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-codex-execute-notes-"));
const workspace = path.join(root, "workspace");
const commandPath = path.join(root, "codex");
const capturePath = path.join(root, "capture.json");
await fs.mkdir(workspace, { recursive: true });
await writeFakeCodexCommand(commandPath);
const previousHome = process.env.HOME;
process.env.HOME = root;
let commandNotes: string[] = [];
try {
const result = await execute({
runId: "run-notes",
agent: {
id: "agent-1",
companyId: "company-1",
name: "Codex Coder",
adapterType: "codex_local",
adapterConfig: {},
},
runtime: {
sessionId: null,
sessionParams: null,
sessionDisplayId: null,
taskKey: null,
},
config: {
command: commandPath,
cwd: workspace,
env: {
PAPERCLIP_TEST_CAPTURE_PATH: capturePath,
},
promptTemplate: "Follow the paperclip heartbeat.",
},
context: {},
authToken: "run-jwt-token",
onLog: async () => {},
onMeta: async (meta) => {
commandNotes = Array.isArray(meta.commandNotes) ? meta.commandNotes : [];
},
});
expect(result.exitCode).toBe(0);
expect(result.errorMessage).toBeNull();
expect(commandNotes).toContain(
"Codex exec automatically applies repo-scoped AGENTS.md instructions from the current workspace; Paperclip does not currently suppress that discovery.",
);
} finally {
if (previousHome === undefined) delete process.env.HOME;
else process.env.HOME = previousHome;
await fs.rm(root, { recursive: true, force: true });
}
});
it("uses a worktree-isolated CODEX_HOME while preserving shared auth and config", async () => { 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-")); const root = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-codex-execute-"));
const workspace = path.join(root, "workspace"); const workspace = path.join(root, "workspace");

View file

@ -330,7 +330,7 @@ Use this when validating Paperclip itself (assignment flow, checkouts, run visib
1. Create a throwaway issue assigned to a known local agent (`claudecoder` or `codexcoder`): 1. Create a throwaway issue assigned to a known local agent (`claudecoder` or `codexcoder`):
```bash ```bash
pnpm paperclipai issue create \ npx paperclipai issue create \
--company-id "$PAPERCLIP_COMPANY_ID" \ --company-id "$PAPERCLIP_COMPANY_ID" \
--title "Self-test: assignment/watch flow" \ --title "Self-test: assignment/watch flow" \
--description "Temporary validation issue" \ --description "Temporary validation issue" \
@ -341,19 +341,19 @@ pnpm paperclipai issue create \
2. Trigger and watch a heartbeat for that assignee: 2. Trigger and watch a heartbeat for that assignee:
```bash ```bash
pnpm paperclipai heartbeat run --agent-id "$PAPERCLIP_AGENT_ID" npx paperclipai heartbeat run --agent-id "$PAPERCLIP_AGENT_ID"
``` ```
3. Verify the issue transitions (`todo -> in_progress -> done` or `blocked`) and that comments are posted: 3. Verify the issue transitions (`todo -> in_progress -> done` or `blocked`) and that comments are posted:
```bash ```bash
pnpm paperclipai issue get <issue-id-or-identifier> npx paperclipai issue get <issue-id-or-identifier>
``` ```
4. Reassignment test (optional): move the same issue between `claudecoder` and `codexcoder` and confirm wake/run behavior: 4. Reassignment test (optional): move the same issue between `claudecoder` and `codexcoder` and confirm wake/run behavior:
```bash ```bash
pnpm paperclipai issue update <issue-id> --assignee-agent-id <other-agent-id> --status todo npx paperclipai issue update <issue-id> --assignee-agent-id <other-agent-id> --status todo
``` ```
5. Cleanup: mark temporary issues done/cancelled with a clear note. 5. Cleanup: mark temporary issues done/cancelled with a clear note.

View file

@ -11,7 +11,7 @@ import { LocalWorkspaceRuntimeFields } from "../local-workspace-runtime-fields";
const inputClass = const inputClass =
"w-full rounded-md border border-border px-2.5 py-1.5 bg-transparent outline-none text-sm font-mono placeholder:text-muted-foreground/40"; "w-full rounded-md border border-border px-2.5 py-1.5 bg-transparent outline-none text-sm font-mono placeholder:text-muted-foreground/40";
const instructionsFileHint = const instructionsFileHint =
"Absolute path to a markdown file (e.g. AGENTS.md) that defines this agent's behavior. Injected into the system prompt at runtime."; "Absolute path to a markdown file (e.g. AGENTS.md) that defines this agent's behavior. Injected into the system prompt at runtime. Note: Codex may still auto-apply repo-scoped AGENTS.md files from the workspace.";
export function CodexLocalConfigFields({ export function CodexLocalConfigFields({
mode, mode,