mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-10 20:50:08 +02:00
## Thinking Path > - Paperclip manages work for AI agents. > - The runtime claims eligible assigned tasks before it starts an agent. > - The wake tells the agent when the runtime already holds that claim. > - The legacy skill still requires another checkout in every case. > - This PR makes the skill honor the current task and run claim. > - Manual checkout and server ownership checks remain in place for other cases. ## Linked Issues or Issue Description **Where is the issue?** `skills/paperclip/SKILL.md`, in the scoped wake procedure and Step 5. **What's wrong?** The wake can say that the harness already checked out the issue. The skill still tells the agent that it must call checkout. These instructions conflict. **Suggested fix** Skip the second checkout only when the runtime wake explicitly confirms the claim for this issue and run. Retain manual checkout when that statement is absent or the agent selects another task. Refs #14948 for the existing shared prompt reduction. ## What Changed - Honor the explicit runtime claim in the scoped wake procedure and Step 5. - Keep context reads, status writes, deliverable handling and conflict rules. - Add checks for normal and resumed wake text and excluded automatic claims. - Retain successful checkout HTTP activity for legacy stock-task evals. Bind each receipt to the exact company, task, agent and run. Keep this observation separate from the original task grades. ## Verification - Checkout observation calibration: nine tests pass. - Focused skill, wake and database ownership tests: in progress. - Full repository build, typecheck and tests: in progress. - Planned live comparison: the existing assigned-skill document case on legacy Codex and Claude. One attempt per variant and profile. No automatic retries. The baseline and candidate share the observation code and task oracle. - Live results are pending. This draft does not claim behavioral qualification. ## Risks - Agents may misread prompt guidance. The API still enforces ownership; the text grants no new authority. - The exception is specific to the current issue and run. It does not remove ordinary legacy completion writes or authorize another task. - Activity measures successful checkout HTTP calls. Failed attempts require separate run-log inspection. Missing or mismatched observations cannot count as zero calls. - One trial per profile cannot establish general reliability, speed or cost trends. ## Model Used OpenAI Codex, GPT-6 family. The exact model build and context window are not exposed in this session. Used code editing, shell tools and test execution. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [ ] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>
36 lines
2.2 KiB
TypeScript
36 lines
2.2 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { observeCheckoutActivity } from "./checkout-activity.js";
|
|
|
|
const run = { id: "run", companyId: "company", agentId: "agent", status: "succeeded",
|
|
contextSnapshot: { issueId: "issue", paperclipWake: { checkedOutByHarness: true } } };
|
|
const receipt = { id: "receipt", companyId: "company", agentId: "agent", actorType: "agent",
|
|
entityType: "issue", entityId: "issue", action: "issue.checked_out", runId: "run" };
|
|
const input = { companyId: "company", issueId: "issue", agentId: "agent", runs: [run], activity: [] };
|
|
describe("checkout activity observation", () => {
|
|
it("distinguishes a runtime claim from repeated successful HTTP checkout calls", () => {
|
|
expect(observeCheckoutActivity(input)).toMatchObject({ status: "observed", successfulCheckoutRequests: 0,
|
|
runs: [{ checkedOutByHarness: true, successfulCheckoutRequests: 0 }] });
|
|
expect(observeCheckoutActivity({ ...input, activity: [receipt, { ...receipt, id: "second" }] }))
|
|
.toMatchObject({ status: "observed", successfulCheckoutRequests: 2 });
|
|
});
|
|
it.each([
|
|
{ runId: "another-run" }, { companyId: "foreign" }, { entityId: "other-task" },
|
|
{ agentId: "other-agent" }, { actorType: "user" }, { id: null },
|
|
])("does not count unattributed or foreign receipt %j as a comparable zero", mutation => {
|
|
expect(observeCheckoutActivity({ ...input, activity: [{ ...receipt, ...mutation }] }))
|
|
.toMatchObject({ status: "uncomparable", successfulCheckoutRequests: null });
|
|
});
|
|
it("refuses missing, mismatched and duplicate run/receipt identities", () => {
|
|
for (const data of [
|
|
{ ...input, runs: [] }, { ...input, runs: [run, run] },
|
|
{ ...input, runs: [{ ...run, agentId: "other" }] },
|
|
{ ...input, runs: [{ ...run, contextSnapshot: { issueId: "other" } }] },
|
|
{ ...input, activity: [receipt, receipt] },
|
|
]) expect(observeCheckoutActivity(data)).toMatchObject({ status: "uncomparable", successfulCheckoutRequests: null });
|
|
});
|
|
it("does not infer a harness claim from status or task text", () => {
|
|
expect(observeCheckoutActivity({ ...input, runs: [{ ...run, contextSnapshot: { issueId: "issue" } }] }))
|
|
.toMatchObject({ runs: [{ checkedOutByHarness: false }] });
|
|
});
|
|
});
|