mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-04 19:28:46 +01:00
fix(evals): deliver selected status card context and current blocker facts
This commit is contained in:
1 parent
23a561cbd8
commit
c911d97f4a
4 files changed
+52
-4
No files matched your search
@@ -114,3 +114,32 @@ it("a paraphrase cannot replace required saved-card link or source-access eviden
|
||||
expect(gradeIntegratorCards(o).passed).toBe(true);
|
||||
expect(gradeIntegratorCards({ ...o, links: ["/ACORN/issues/source"] }).passed).toBe(false);
|
||||
});
|
||||
|
||||
import type { StatusCard } from "../../../packages/shared/src/validators/status-card.js";
|
||||
import { updateIssueSchema } from "../../../packages/shared/src/validators/issue.js";
|
||||
import { selectedStatusCardReference, refreshedBlockerTaskInput } from "./integrator-status-cards.js";
|
||||
it("delivers only actual selected card and saved generation/source context, not live answers or actions", () => {
|
||||
const o = valid("status-cards.explain-card");
|
||||
const card = { ...o.cards[0]!, fingerprint: { source: { title: "Payment approval blocks launch", status: "blocked" } } } as unknown as StatusCard;
|
||||
const context = selectedStatusCardReference(card, [{ id: "source", identifier: "ACN-1", title: "private-current-title", status: "done" }, { id: "unrelated", title: "unrelated-private-detail" }], "ACN");
|
||||
expect(context).toContain("[Launch blockers](/ACN/status/card)"); expect(context).toContain(card.summaryBody!); expect(context).toContain(card.lastGeneratedAt!);
|
||||
expect(context).toContain("Payment approval blocks launch"); expect(context).toContain("/ACN/issues/ACN-1");
|
||||
expect(context).not.toMatch(/private-current-title|unrelated-private-detail|must|expected|archive|restore|DELETE|PATCH|\/api\//i);
|
||||
expect(() => selectedStatusCardReference(card, [], undefined)).toThrow("actual company issue prefix");
|
||||
const other = selectedStatusCardReference({ ...card, id: "other-card", title: "Research" }, [], "ACN");
|
||||
expect(other).not.toContain("/ACN/status/card)"); expect(other).toContain("/ACN/status/other-card");
|
||||
});
|
||||
it("makes the genuine new blocker visible in the public task title used by bounded generation input", () => {
|
||||
const update = updateIssueSchema.parse(refreshedBlockerTaskInput());
|
||||
expect(update.title).toMatch(/mobile Safari.*checkout regression/i); expect(update.description).toContain("Payment approval has been received");
|
||||
expect(update).not.toHaveProperty("status"); // No task/card/native status fabricated.
|
||||
expect(JSON.stringify(update)).not.toMatch(/summari[sz]e|write|return|tool|generationIssueId|model/i);
|
||||
});
|
||||
it("declares factual human attachments only for the two actual missing selected referents", () => {
|
||||
for (const id of ["status-cards.explain-card", "status-cards.archive-card"]) {
|
||||
const definition = integratorStatusCardsCases.find(row => row.scenarioId === id)!;
|
||||
expect(definition.authentication).toBe("human-session"); expect(definition.queryAttachments).toEqual([{ stateKey: "factualContextAttachment", filename: "selected-status-card.md" }]);
|
||||
}
|
||||
// The refresh query already names Launch and the retained run selected it correctly.
|
||||
expect(integratorStatusCardsCases.find(row => row.scenarioId === "status-cards.refresh-card")!.queryAttachments).toBeUndefined();
|
||||
});
|
||||
@@ -1,3 +1,4 @@
|
||||
import { uploadFactualQueryAttachment, factualContextQueryAttachment } from "../chat-query-attachments.js";
|
||||
import type { StatusCard, StatusCardUpdate, StatusCardSummaryRevision } from "../../../packages/shared/src/validators/status-card.js";
|
||||
import type { ChatCatalogCase, ChatCatalogContext } from "../chat-catalog-contract.js";
|
||||
import { pollUntil } from "../api.js";
|
||||
@@ -125,6 +126,20 @@ async function seedCard(ctx: ChatCatalogContext, authorId: string, summarizerId:
|
||||
reject: card => card.state === "error" ? "Prerequisite card generation failed; no synthetic summary substituted" : undefined });
|
||||
} finally { await ctx.input.api.delete(`/api/agents/${authorId}/keys/${key.id}`, { allowNotFound: true }); }
|
||||
}
|
||||
/** Human-visible selected resource data, copied from actual saved public rows.
|
||||
* Live task state is deliberately left for the responding agent to inspect. */
|
||||
export function selectedStatusCardReference(card: StatusCard, sources: Entity[], issuePrefix: string | null | undefined) {
|
||||
if (!issuePrefix) throw new Error("Selected status card context requires the actual company issue prefix");
|
||||
const sourceReferences = sources.filter(source => !!card.fingerprint?.[source.id]).map(source => ({ id: source.id, identifier: source.identifier,
|
||||
title: card.fingerprint?.[source.id]?.title, href: `/${issuePrefix}/issues/${source.identifier ?? source.id}` }));
|
||||
return `# Selected status card\n\n[${card.title ?? "Status card"}](/${issuePrefix}/status/${card.id})\n\n` + JSON.stringify({
|
||||
cardId: card.id, title: card.title, authorAgentId: card.createdByAgentId, summarizerAgentId: card.agentId,
|
||||
lastGeneratedAt: card.lastGeneratedAt, summaryBody: card.summaryBody, sourceReferences,
|
||||
}, null, 2);
|
||||
}
|
||||
export function refreshedBlockerTaskInput() {
|
||||
return { title: "Mobile Safari checkout regression blocks launch", description: "Payment approval has been received. The remaining blocker is the mobile Safari checkout regression." };
|
||||
}
|
||||
const experiments = new WeakMap<ChatCatalogContext, boolean>();
|
||||
function define(id: string, seed: "none" | "ceo" | "morgan", extra: Partial<ChatCatalogCase> = {}): ChatCatalogCase {
|
||||
return integratorDefinition("status-cards", id, async ctx => {
|
||||
@@ -149,18 +164,20 @@ function define(id: string, seed: "none" | "ceo" | "morgan", extra: Partial<Chat
|
||||
if (["status-cards.refresh-card", "status-cards.explain-card"].includes(id)) {
|
||||
sourceTasks[0] = await ctx.input.api.patch<Entity>(`/api/issues/${t.id}`, id === "status-cards.explain-card"
|
||||
? { status: "done", description: "The payment approval has been received; this blocker is resolved." }
|
||||
: { description: "Payment approval has been received. The remaining blocker is the mobile Safari checkout regression." });
|
||||
: refreshedBlockerTaskInput());
|
||||
}
|
||||
return { project: p, sourceTasks, worker, card, otherCard, originalUpdates: await ctx.input.api.get<StatusCardUpdate[]>(`/api/status-cards/${card.id}/updates`), originalSummaries: await ctx.input.api.get<StatusCardSummaryRevision[]>(`/api/status-cards/${card.id}/summary-revisions`) };
|
||||
const factualContextAttachment = ["status-cards.explain-card", "status-cards.archive-card"].includes(id)
|
||||
? await uploadFactualQueryAttachment(ctx, { filename: "selected-status-card.md", body: selectedStatusCardReference(card, sourceTasks, ctx.input.fixtures.company.issuePrefix) }) : undefined;
|
||||
return { project: p, sourceTasks, worker, card, otherCard, factualContextAttachment, originalUpdates: await ctx.input.api.get<StatusCardUpdate[]>(`/api/status-cards/${card.id}/updates`), originalSummaries: await ctx.input.api.get<StatusCardSummaryRevision[]>(`/api/status-cards/${card.id}/summary-revisions`) };
|
||||
} catch (error) { await ctx.input.api.patch("/api/instance/settings/experimental", { enableStatusCards: previous.enableStatusCards }); experiments.delete(ctx); throw error; }
|
||||
}, observeCards, gradeIntegratorCards, verifyCard, { expectedRunCount: 2, attemptTimeoutMs: 540_000, ...extra,
|
||||
async cleanup(ctx) { await cleanupIdentityProbes(ctx); const previous = experiments.get(ctx); if (previous !== undefined) { await ctx.input.api.patch("/api/instance/settings/experimental", { enableStatusCards: previous }); experiments.delete(ctx); } } });
|
||||
}
|
||||
export const integratorStatusCardsCases: readonly ChatCatalogCase[] = [
|
||||
define("status-cards.create-card", "none"),
|
||||
define("status-cards.archive-card", "ceo", { expectedRunCount: 3, attemptTimeoutMs: 720_000 }),
|
||||
define("status-cards.archive-card", "ceo", { authentication: "human-session", queryAttachments: factualContextQueryAttachment("selected-status-card.md"), expectedRunCount: 3, attemptTimeoutMs: 720_000 }),
|
||||
define("status-cards.other-authors-card", "morgan"),
|
||||
define("status-cards.explain-card", "ceo", { expectedRunCount: 3, expectedHumanMessageCount: 2, attemptTimeoutMs: 720_000,
|
||||
define("status-cards.explain-card", "ceo", { authentication: "human-session", queryAttachments: factualContextQueryAttachment("selected-status-card.md"), expectedRunCount: 3, expectedHumanMessageCount: 2, attemptTimeoutMs: 720_000,
|
||||
sourceFiles: ["chat-catalog-domains/integrator-status-cards.ts", "chat-catalog-domains/identity-support.ts", "chat-catalog-domains/internal-definition.ts", "chat-catalog-domains/internal-support.ts", "chat-catalog-domains/integrator-support.ts", "chat-scenarios/status-cards.yaml"],
|
||||
async act(ctx, state) {
|
||||
const first = await pollUntil({ label: "accessible source explanation before actual access change", deadlineAt: Date.now() + 180_000, intervalMs: 1000,
|
||||
|
||||
@@ -44,6 +44,7 @@ it("independently resolves declared receipt IDs and rejects undeclared, duplicat
|
||||
});
|
||||
|
||||
it.each([
|
||||
["status-cards.explain-card", "selected-status-card.md"], ["status-cards.archive-card", "selected-status-card.md"],
|
||||
["routines.webhook-trigger", "selected-routine.md"],
|
||||
["developer-labs.gated-lab", "historical-lab-link.md"],
|
||||
["documents-artifacts.resolve-feedback", "selected-source.md"], ["documents-artifacts.restore-document", "selected-source.md"],
|
||||
|
||||
@@ -21,6 +21,7 @@ const factualFiles: Readonly<Record<string, string>> = {
|
||||
"skills.author-skill": "skill-context.md", "skills.studio-test": "skill-context.md",
|
||||
"skills.review-update": "skill-context.md", "skills.find-and-install": "skill-context.md", "skills.fork-read-only": "skill-context.md",
|
||||
"connection-setup.secure-api-key": "provider-account.md",
|
||||
"status-cards.explain-card": "selected-status-card.md", "status-cards.archive-card": "selected-status-card.md",
|
||||
"routines.webhook-trigger": "selected-routine.md",
|
||||
"cases.create-case": "customer-bug-brief.md", "app-permissions.ask-before-action": "launch-update.md",
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user