Publish a draft
After independent verification, the factory may open a draft pull request. Policy still denies ready pull requests, and the factory has no merge capability.
if (input?.draft === true) return "not-applicable";
return { reason: "The factory may create draft pull requests only.", type: "denied" };A draft carries the real diff and decision evidence. Final authority remains with a person.
Ship Proof, Keep Merge Authority
Deploy the factory and allow it to publish independently verified draft pull requests only.
Hands-on Exercise 5.2
Create agent/lib/github/approval.ts:
import type { ApprovalContext, ApprovalStatus } from "eve/tools";
export function createPullRequestPolicy(ctx: ApprovalContext): ApprovalStatus {
const input = ctx.toolInput as { draft?: unknown } | undefined;
if (input?.draft === true) {
return "not-applicable";
}
return {
reason: "The factory may create draft pull requests only.",
type: "denied",
};
}Create agent/extensions/github.ts and include only the repository tools the orchestrator needs. Connect createPullRequest to the policy:
import githubExtension from "@github-tools/eve-extension";
import { factoryRepo } from "../lib/config.js";
import { createPullRequestPolicy } from "../lib/github/approval.js";
import { GITHUB_CONNECTOR } from "../lib/github/credentials.js";
export default githubExtension({
connector: GITHUB_CONNECTOR,
context: factoryRepo,
include: [
"getRepository",
"getIssueContext",
"listIssueComments",
"listBranches",
"getPullRequestContext",
"listPullRequestFiles",
"createPullRequest",
],
requireApproval: {
createPullRequest: createPullRequestPolicy,
},
});factoryRepo supplies { owner, repo }; GITHUB_CONNECTOR names the attached connector. The include list is the complete GitHub capability set, with no stored personal token.
Add two tests at agent/lib/github/approval.test.ts: drafts return "not-applicable", while a ready pull request returns { type: "denied" }.
Finish agent/instructions.md with the pull request body requirements. Include the problem statement, acceptance criteria with verification results, commands run, risks, deviations, and a link to the issue. Call github__createPullRequest with draft: true only after the Verifier approves.
The application was linked, given AI Gateway credentials, and attached to GitHub in Lesson 3.1. Deploy that linked application now:
pnpm exec eve deployUse the same personal GitHub repository configured for the sandboxes. The deployment preserves the channel, tools, subagents, approval policy, and durable session behavior.
Try It
Run the local policy checks before testing the deployed factory:
pnpm test agent/lib/github/approval.test.ts
pnpm typecheck
pnpm exec eve infoCreate an issue from fixtures/issues/bug-example.md, then add the factory label from an account with repository triage access or higher. Follow the run through verification. Its pull request should be a draft with evidence for every criterion.
If nothing starts, verify each connection in order:
vercel connect list
pnpm exec eve infoConfirm the connector targets /eve/v1/github, its UID matches GITHUB_CONNECTOR, the App can access the repository, and the deployment includes agent/channels/github.ts.
Attempt a ready pull request through the policy test. It must be denied. Confirm that the extension exposes no merge tool.
Commit
git add agent/extensions agent/lib/github agent/instructions.md
git commit -m "feat(factory): publish verified drafts"Done-When
- The GitHub extension exposes no merge capability
- Ready pull requests fail the policy test
- Draft creation happens only after verification approval
- The draft body carries the specification and evidence
- The deployed factory processes a labeled GitHub issue
The factory can now produce reviewable work while merge authority remains external. The final lesson turns failed decisions into regression evaluations.
Solution
The complete pull request policy is shown in the exercise. Its test is:
import { describe, expect, it } from "vitest";
import { createPullRequestPolicy } from "./approval.js";
describe("createPullRequestPolicy", () => {
it("permits draft pull requests", () => {
expect(createPullRequestPolicy({ toolInput: { draft: true } } as never))
.toBe("not-applicable");
});
it("denies pull requests that are ready for review", () => {
expect(createPullRequestPolicy({ toolInput: { draft: false } } as never))
.toMatchObject({ type: "denied" });
});
});Was this helpful?