mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-10 12:37:55 +01:00
test(evals): bind completed artifact edits to attached results
This commit is contained in:
1 parent
d1ab241cac
commit
62300ee07d
5 files changed
+35
-5
No files matched your search
@@ -258,7 +258,7 @@
|
||||
"../../server/src/services/native-runtime/status-decision-committer.ts",
|
||||
"../../server/src/services/work-timeline.ts"
|
||||
],
|
||||
"oracleVersion": "paperclip.chat-catalog.work-timeline.v10",
|
||||
"oracleVersion": "paperclip.chat-catalog.work-timeline.v11",
|
||||
"coverageGaps": [],
|
||||
"expectedRunCount": 3,
|
||||
"minimumRunCount": 3,
|
||||
|
||||
@@ -227,3 +227,32 @@ it.each([
|
||||
it("stops repair subject continuity when a coordinated clause introduces another worker", () => {
|
||||
expect(grade("Morgan ran verification which failed and work paused. You reassigned the task to Riley. Riley added the label, and Avery inspected the saved bytes and then re-ran verify_launch_html. It passed with exit code 0 on the saved bytes.")).toBe(false);
|
||||
});
|
||||
|
||||
const editedArtifactAccount = `Morgan ran verification which failed and work paused.
|
||||
|
||||
You reassigned RUN-3 from Morgan to Riley.
|
||||
|
||||
**Repair and passing verification**
|
||||
Riley edited \`launch.html\` to add \`<label for="email">Email address</label>\` while preserving the \`<h1>\`, then re-ran \`verify_launch_html\` on the saved bytes:
|
||||
- Result: exit code 0 (passed), checked 00:09:08.`;
|
||||
const gradeEditedArtifact = (reply: string) => truthfulTimelineAccount({ reply, firstWorker: "Morgan", repairWorker: "Riley", taskIdentifier: "RUN-3" });
|
||||
it("binds a completed artifact edit to its immediately attached command-result bullet", () => {
|
||||
expect(gradeEditedArtifact(editedArtifactAccount)).toBe(true);
|
||||
});
|
||||
it.each([
|
||||
editedArtifactAccount.replace("Riley edited", "Morgan edited"),
|
||||
editedArtifactAccount.replace("Riley edited", "Riley planned to edit"),
|
||||
editedArtifactAccount.replace("Riley edited", "Riley never edited"),
|
||||
editedArtifactAccount.replace("then re-ran", "then Avery re-ran"),
|
||||
editedArtifactAccount.replace("Result: exit code 0 (passed)", "Result: Avery ran the check which passed"),
|
||||
editedArtifactAccount.replace("- Result:", "- Avery inspected the page.\n- Result:"),
|
||||
editedArtifactAccount.replace("- Result:", "\n- Result:"),
|
||||
editedArtifactAccount.replace("- Result:", "## Another milestone\n- Result:"),
|
||||
editedArtifactAccount.replace("exit code 0 (passed)", "an unrelated check passed"),
|
||||
editedArtifactAccount.replace("exit code 0 (passed)", "did not pass"),
|
||||
editedArtifactAccount.replace("exit code 0 (passed)", "exit code 1 (failed)"),
|
||||
editedArtifactAccount.replace("You reassigned RUN-3 from Morgan to Riley.", "") + " You reassigned RUN-3 from Morgan to Riley.",
|
||||
editedArtifactAccount + " A native run failed.",
|
||||
])("keeps completed-edit command results bounded by actor, assertion, paragraph and order: %s", reply => {
|
||||
expect(gradeEditedArtifact(reply)).toBe(false);
|
||||
});
|
||||
@@ -125,6 +125,7 @@ export function truthfulTimelineAccount(input: {
|
||||
const repairModifier = "(?:then|later|now|personally|explicitly|actually|had|has|have|also|who|successfully|finally|subsequently|after\\s+the\\s+handoff)";
|
||||
const repairVerb = [
|
||||
"repair(?:ed)?|fix(?:ed)?",
|
||||
"edited\\s+(?:[a-z0-9_.-]+|(?:the\\s+)?(?:saved\\s+)?(?:file|page|artifact))\\s+to\\s+add\\s+(?:(?:a|the|missing|real|email|input|proper|accessible)\\s+){0,5}label",
|
||||
"(?:completed|finished|performed|applied|implemented)\\s+(?:(?:a|the|needed|required|label|accessibility)\\s+){0,4}(?:repairs?|fix(?:es)?)",
|
||||
"(?:added|inserted|supplied|provided|restored)\\s+(?:(?:a|the|missing|real|email|input|proper|accessible)\\s+){0,5}label",
|
||||
].join("|");
|
||||
@@ -155,7 +156,7 @@ export function truthfulTimelineAccount(input: {
|
||||
// repair account. No intervening sentence or earlier/unrelated result joins.
|
||||
const adjacentVerification = new RegExp(`^\\s*(?:(?:the|a|an)\\s+|${escape(repairWorker)}['’]s\\s+)?${commandSubject}\\s+${successModifiers}(?:${repairSuccess.source})(?:\\s+with\\s+exit(?:\\s+(?:code|status))?\\s*[:=]?\\s*0)?\\s*[.!]?\\s*$`, "i");
|
||||
const contextualCommandLead = new RegExp(`^\\s*(?:(?:the|a|an)\\s+)?${commandSubject}(?:[ \\t]+${commandModifier}){0,5}\\s+${successModifiers}$`, "i");
|
||||
const coordinatedCommand = new RegExp(`(?:,\\s*(?:and\\s+)?|\\band\\s+)(?:${repairModifier}\\s+){0,3}(?:re-?ran|ran|executed|performed)\\s+(?:(?:the|a|an)\\s+)?${commandSubject}(?:[ \\t]+${commandModifier}){0,5}\\s*[.!]?\\s*$`, "i");
|
||||
const coordinatedCommand = new RegExp(`(?:,\\s*(?:and\\s+)?|\\band\\s+)(?:${repairModifier}\\s+){0,3}(?:re-?ran|ran|executed|performed)\\s+(?:(?:the|a|an)\\s+)?${commandSubject}(?:[ \\t]+${commandModifier}){0,5}\\s*[.!:]?\\s*$`, "i");
|
||||
const repairObjectTail = new RegExp(`^(?:[ \\t]+${commandModifier}){0,6}\\s*[.!]?\\s*$`, "i");
|
||||
const passiveRepairSubject = new RegExp(`^\\s*(?:${commandModifier}[ \\t]+){1,6}$`, "i");
|
||||
const deniedRepairObject = /^\s*(?:[a-z]+ly\s+){0,2}(?:(?:no|none|neither|nothing)\b|zero(?=\s|[.!?]|$))/i;
|
||||
@@ -179,7 +180,7 @@ export function truthfulTimelineAccount(input: {
|
||||
const sameWorkerParagraph = paragraphStart === nextClause?.paragraphStart
|
||||
&& (owner === repairWorker && nextClause.owner === repairWorker || explicitRepairSubject);
|
||||
const nextCommand = next && sameWorkerParagraph && coordinatedCommand.test(text.slice(end))
|
||||
? next.replace(/^\s*it\b/i, "check") : next;
|
||||
? next.replace(/^(?:\s*it\b\s*|\s*result:\s*(?=[^\n]))/i, match => /result:/i.test(match) && !/:\s*$/.test(text) ? match : "check ") : next;
|
||||
const contextualOutcome = nextCommand && repairSuccess.exec(nextCommand);
|
||||
const contextualResult = sameWorkerParagraph && !!contextualOutcome
|
||||
&& contextualCommandLead.test(nextCommand.slice(0, contextualOutcome.index))
|
||||
|
||||
@@ -269,7 +269,7 @@ it("admits the selected live timeline metadata without invoking fixture callback
|
||||
const spies = callbacks.map(key => vi.spyOn(implementation, key));
|
||||
try {
|
||||
expect(await validateChatCatalogConsistency([descriptor])).toEqual({ descriptorCount: 1, fixtureCallbacksInvoked: 0 });
|
||||
expect(descriptor.oracleVersion).toBe("paperclip.chat-catalog.work-timeline.v10");
|
||||
expect(descriptor.oracleVersion).toBe("paperclip.chat-catalog.work-timeline.v11");
|
||||
expect(descriptor.sourceFiles).toEqual(implementation.sourceFiles);
|
||||
expect(descriptor).toMatchObject({ expectedRunCount: 3, minimumRunCount: 3, attemptTimeoutMs: 720000 });
|
||||
for (const spy of spies) expect(spy).not.toHaveBeenCalled();
|
||||
|
||||
@@ -92,7 +92,7 @@ function timeline(): ChatCatalogCase {
|
||||
const state = { worker, repairWorker, project: p, task: second.t, prepTask, hiddenTask, firstRun, secondRun, blockedTask: first.t, blockedEventId: blockedEvent.id, handoffEventId: handoff.id, doneEventId: done.id, timezone, today, zoneLabel, personId: session.user.id, personName: session.user.name };
|
||||
await ctx.input.evidence("chat-catalog-real-launch-timeline.json", { ...state, verification: verifier.receipts, events }); return state;
|
||||
}, gradeWorkTimeline);
|
||||
return { ...base, oracleVersion: "paperclip.chat-catalog.work-timeline.v10", authentication: "human-session", expectedRunCount: 3, minimumRunCount: 3, attemptTimeoutMs: 720000,
|
||||
return { ...base, oracleVersion: "paperclip.chat-catalog.work-timeline.v11", authentication: "human-session", expectedRunCount: 3, minimumRunCount: 3, attemptTimeoutMs: 720000,
|
||||
sourceFiles: [...base.sourceFiles, "chat-catalog-domains/internal-work-timeline.ts", "chat-catalog-domains/internal-timeline-narrative.ts", "chat-catalog-domains/internal-timeline-verifier.ts", "chat-catalog-domains/internal-privacy.ts", "chat-catalog-domains/identity-auth.ts", "chat-catalog-domains/internal-routine-audit.ts", "../e2e/fetch-allowed-port.ts", "../../server/src/services/native-runtime/status-decision-committer.ts", "../../server/src/services/work-timeline.ts"],
|
||||
async observe(ctx, state): Promise<Timeline> { return { ...await observeInternal(ctx, state), verification: structuredClone(verifier?.receipts ?? []), timelineEvents: await ctx.input.api.get(`/api/issues/${entity(state, "task").id}/activity`), hiddenStatus: await agentStatus(ctx, ctx.input.fixtures.agent.id, `/api/issues/${entity(state, "hiddenTask").id}`) }; },
|
||||
async verifyResult(ctx, state, value) { await base.verifyResult(ctx, state, value); const source = ctx.input.page.url(); try { await ctx.input.page.goto(new URL(`/${ctx.input.fixtures.company.issuePrefix}/issues/${entity(state, "prepTask").identifier}`, ctx.input.api.baseURL).href); await expect(ctx.input.page.getByRole("heading", { name: entity(state, "prepTask").title!, exact: true })).toBeVisible(); } finally { await ctx.input.page.goto(source); await expect(ctx.input.page.getByTestId("task-chat-composer-input")).toBeVisible(); } },
|
||||
|
||||
Reference in new issue
Block a user