mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-23 09:07:46 +02:00
fix(briefs): tighten release readiness gaps
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
8f3b5ab4b4
commit
a2a1b9d8cc
3 files changed
+75
-8
No files matched your search
@@ -57,6 +57,7 @@ export type BriefsRunInput = {
|
||||
id: string;
|
||||
companyId: string;
|
||||
issueId: string | null;
|
||||
agentId?: string | null;
|
||||
status: string;
|
||||
error?: string | null;
|
||||
startedAt?: Date | string | null;
|
||||
@@ -221,7 +222,8 @@ export function slugifyBriefGrouping(value: string): string {
|
||||
}
|
||||
|
||||
function issueHref(issue: Pick<BriefsIssueInput, "id" | "identifier">): string {
|
||||
return `/PAP/issues/${issue.identifier ?? issue.id}`;
|
||||
const prefix = companyPrefixFromIdentifier(issue.identifier) ?? "PAP";
|
||||
return `/${prefix}/issues/${issue.identifier ?? issue.id}`;
|
||||
}
|
||||
|
||||
function sourceIssue(bundle: BriefsSourceBundle, issueId: string | null | undefined): BriefsIssueInput | null {
|
||||
@@ -229,14 +231,32 @@ function sourceIssue(bundle: BriefsSourceBundle, issueId: string | null | undefi
|
||||
return bundle.issues.find((issue) => issue.id === issueId) ?? null;
|
||||
}
|
||||
|
||||
function sourceLink(bundle: BriefsSourceBundle, kind: string, sourceId: string, issueId: string | null, key?: string): string {
|
||||
function companyPrefixFromIdentifier(identifier: string | null | undefined): string | null {
|
||||
return identifier?.match(/^([A-Z][A-Z0-9]*)-\d+$/)?.[1] ?? null;
|
||||
}
|
||||
|
||||
function companyPrefixForBundle(bundle: BriefsSourceBundle): string {
|
||||
return bundle.issues.map((issue) => companyPrefixFromIdentifier(issue.identifier)).find(Boolean) ?? "PAP";
|
||||
}
|
||||
|
||||
function sourceLink(
|
||||
bundle: BriefsSourceBundle,
|
||||
kind: string,
|
||||
sourceId: string,
|
||||
issueId: string | null,
|
||||
key?: string,
|
||||
agentId?: string | null,
|
||||
): string {
|
||||
const issue = sourceIssue(bundle, issueId);
|
||||
if (kind === "run") return `/PAP/runs/${sourceId}`;
|
||||
if (!issue) return `/PAP/issues/${issueId ?? sourceId}`;
|
||||
const prefix = issue ? companyPrefixFromIdentifier(issue.identifier) ?? companyPrefixForBundle(bundle) : companyPrefixForBundle(bundle);
|
||||
if (kind === "run") {
|
||||
return agentId ? `/${prefix}/agents/${agentId}/runs/${sourceId}` : issue ? issueHref(issue) : `/${prefix}/issues/${issueId ?? sourceId}`;
|
||||
}
|
||||
if (!issue) return `/${prefix}/issues/${issueId ?? sourceId}`;
|
||||
if (kind === "comment") return `${issueHref(issue)}#comment-${sourceId}`;
|
||||
if (kind === "document") return `${issueHref(issue)}#document-${key ?? sourceId}`;
|
||||
if (kind === "interaction") return `${issueHref(issue)}#interaction-${sourceId}`;
|
||||
if (kind === "approval") return `/PAP/approvals/${sourceId}`;
|
||||
if (kind === "approval") return `/${prefix}/approvals/${sourceId}`;
|
||||
if (kind === "work_product") return `${issueHref(issue)}#work-product-${sourceId}`;
|
||||
return issueHref(issue);
|
||||
}
|
||||
@@ -489,7 +509,7 @@ function buildSources(bundle: BriefsSourceBundle, cardId: string, idFactory: ()
|
||||
identifier: issue?.identifier ?? null,
|
||||
titleLine: truncate(issue ? `Run for ${issue.title}` : "Heartbeat run", 160),
|
||||
rightTag: ["failed", "error"].includes(run.status) ? "failed" : run.status,
|
||||
linkPath: sourceLink(bundle, "run", run.id, run.issueId),
|
||||
linkPath: sourceLink(bundle, "run", run.id, run.issueId, undefined, run.agentId),
|
||||
isIntraTreeBlocked: null,
|
||||
eventAt: toIso(runEventTime(run) || Date.now()),
|
||||
metadata: { error: run.error ?? null },
|
||||
@@ -710,8 +730,7 @@ export function isBriefTreeRelevantToUser(bundle: BriefsSourceBundle): boolean {
|
||||
|| (bundle.comments ?? []).some((comment) => comment.authorUserId === bundle.userId)
|
||||
|| (bundle.documents ?? []).some((document) => document.createdByUserId === bundle.userId || document.updatedByUserId === bundle.userId)
|
||||
|| (bundle.interactions ?? []).some((interaction) => interaction.targetUserId === bundle.userId)
|
||||
|| (bundle.approvals ?? []).some((approval) => approval.reviewerUserId === bundle.userId || approval.decidedByUserId === bundle.userId)
|
||||
|| bundle.issues.some((issue) => Boolean(issue.assigneeAgentId && bundle.relevantAgentIds?.includes(issue.assigneeAgentId)));
|
||||
|| (bundle.approvals ?? []).some((approval) => approval.reviewerUserId === bundle.userId || approval.decidedByUserId === bundle.userId);
|
||||
}
|
||||
|
||||
export function selectRelevantBriefTrees(input: {
|
||||
|
||||
@@ -167,6 +167,7 @@ function runToBriefRun(run: BriefsRunInput | Awaited<ReturnType<PluginContext["i
|
||||
id: run.id,
|
||||
companyId: "companyId" in run ? run.companyId : "",
|
||||
issueId: run.issueId,
|
||||
agentId: "agentId" in run ? run.agentId : null,
|
||||
status: run.status,
|
||||
error: run.error ?? null,
|
||||
startedAt: run.startedAt ?? null,
|
||||
|
||||
@@ -104,6 +104,28 @@ describe("deterministic Briefs cards", () => {
|
||||
})).toEqual([relevant]);
|
||||
});
|
||||
|
||||
it("does not treat unrelated assignee summaries as user relevance", () => {
|
||||
const unrelatedAssignedTree = bundle({
|
||||
rootIssueId: "other-root",
|
||||
issues: [issue({
|
||||
id: "other-root",
|
||||
identifier: "PAP-10",
|
||||
createdByUserId: "someone-else",
|
||||
updatedAt: "2026-05-22T10:00:00.000Z",
|
||||
assigneeAgentId: "agent-1",
|
||||
})],
|
||||
relevantAgentIds: ["agent-1"],
|
||||
});
|
||||
|
||||
expect(isBriefTreeRelevantToUser(unrelatedAssignedTree)).toBe(false);
|
||||
expect(selectRelevantBriefTrees({
|
||||
companyId,
|
||||
userId,
|
||||
candidateTrees: [unrelatedAssignedTree],
|
||||
now,
|
||||
})).toEqual([]);
|
||||
});
|
||||
|
||||
it("does not promote intra-tree sequencing blockers to card-level blocked", () => {
|
||||
const child = issue({
|
||||
id: "issue-child",
|
||||
@@ -130,6 +152,31 @@ describe("deterministic Briefs cards", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("builds company-prefixed source links and valid run routes", () => {
|
||||
const card = buildDeterministicBriefCard(bundle({
|
||||
rootIssueId: "issue-root",
|
||||
issues: [issue({
|
||||
identifier: "ACME-42",
|
||||
updatedAt: "2026-05-22T10:00:00.000Z",
|
||||
})],
|
||||
runs: [{
|
||||
id: "run-1",
|
||||
companyId,
|
||||
issueId: "issue-root",
|
||||
agentId: "agent-1",
|
||||
status: "failed",
|
||||
error: "Timed out",
|
||||
createdAt: "2026-05-22T11:00:00.000Z",
|
||||
}],
|
||||
}), { now, idFactory: ids() });
|
||||
|
||||
const issueSource = card.sources.find((source) => source.sourceKind === "issue");
|
||||
const runSource = card.sources.find((source) => source.sourceKind === "run");
|
||||
|
||||
expect(issueSource?.linkPath).toBe("/ACME/issues/ACME-42");
|
||||
expect(runSource?.linkPath).toBe("/ACME/agents/agent-1/runs/run-1");
|
||||
});
|
||||
|
||||
it("gives out-of-tree blockers precedence over waiting and live signals", () => {
|
||||
const blocked = issue({
|
||||
id: "issue-child",
|
||||
|
||||
Reference in new issue
Block a user