mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-25 02:26:12 +02:00
Address Greptile review on aggregation PR
Push the review-stage caller filter into SQL so agent callers in busy companies can't get a silently truncated review feed, make the routine reference check a targeted JSONB query instead of a company-wide stage scan, and expose connections on the UI PipelineListItem type. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
60c3ca6e94
commit
973e8f5e98
3 files changed
+17
-14
No files matched your search
@@ -56,10 +56,10 @@ function caseDisplay(row: { case: CaseRow; stage: StageRow; pipeline: PipelineRo
|
||||
// Current review-stage approver semantics: reviewerKind "human" awaits any
|
||||
// human caller, "any" awaits anyone. B1 (stage requireApproval + named
|
||||
// approver) extends this predicate when it lands — keep the seam here.
|
||||
function reviewStageAwaitsCaller(config: Record<string, unknown>, caller: AttentionCaller) {
|
||||
const reviewerKind = typeof config.reviewerKind === "string" ? config.reviewerKind : "human";
|
||||
if (reviewerKind === "any") return true;
|
||||
return caller.type === "user";
|
||||
// SQL-side so busy companies can't truncate an agent's review feed.
|
||||
function reviewStageAwaitsCallerSql(caller: AttentionCaller) {
|
||||
if (caller.type === "user") return sql`true`;
|
||||
return sql`coalesce(${pipelineStages.config}->>'reviewerKind', 'human') = 'any'`;
|
||||
}
|
||||
|
||||
function boundedLimit(limit: number | undefined, fallback: number, max: number) {
|
||||
@@ -129,13 +129,12 @@ export async function listPipelineAttention(
|
||||
eq(pipelines.companyId, input.companyId),
|
||||
eq(pipelineStages.kind, "review"),
|
||||
isNull(pipelineCases.terminalKind),
|
||||
reviewStageAwaitsCallerSql(input.caller),
|
||||
))
|
||||
.orderBy(asc(pipelineCases.createdAt))
|
||||
.limit(PIPELINE_ATTENTION_MAX_LIMIT * 2);
|
||||
.limit(limit);
|
||||
|
||||
const reviews = reviewRows
|
||||
.filter((row) => reviewStageAwaitsCaller((row.stage.config ?? {}) as Record<string, unknown>, input.caller))
|
||||
.slice(0, limit)
|
||||
.map((row) => {
|
||||
const config = (row.stage.config ?? {}) as Record<string, unknown>;
|
||||
return {
|
||||
|
||||
@@ -853,15 +853,18 @@ export function pipelineService(db: Db, deps: { heartbeat?: IssueAssignmentWakeu
|
||||
dbOrTx: PipelineDb,
|
||||
input: { companyId: string; routineId: string; exceptStageId?: string | null },
|
||||
) {
|
||||
const stages = await dbOrTx
|
||||
.select({ id: pipelineStages.id, config: pipelineStages.config })
|
||||
const referencing = await dbOrTx
|
||||
.select({ id: pipelineStages.id })
|
||||
.from(pipelineStages)
|
||||
.innerJoin(pipelines, eq(pipelineStages.pipelineId, pipelines.id))
|
||||
.where(eq(pipelines.companyId, input.companyId));
|
||||
return stages.some((stage) =>
|
||||
stage.id !== input.exceptStageId &&
|
||||
stageAutomationRoutineIdFromConfig((stage.config ?? {}) as PipelineStageConfig) === input.routineId
|
||||
);
|
||||
.where(and(
|
||||
eq(pipelines.companyId, input.companyId),
|
||||
sql`${pipelineStages.config}->'onEnter'->>'type' = 'run_routine'`,
|
||||
sql`${pipelineStages.config}->'onEnter'->>'routineId' = ${input.routineId}`,
|
||||
input.exceptStageId ? ne(pipelineStages.id, input.exceptStageId) : undefined,
|
||||
))
|
||||
.limit(1);
|
||||
return referencing.length > 0;
|
||||
}
|
||||
|
||||
async function clearPipelineAutomationRoutineIfUnreferenced(
|
||||
|
||||
@@ -11,6 +11,7 @@ export interface PipelineListItem {
|
||||
archivedAt: Date | string | null;
|
||||
stageCount: number;
|
||||
openCaseCount: number;
|
||||
connections: { upstreamPipelineIds: string[]; downstreamPipelineIds: string[] };
|
||||
createdAt: Date | string;
|
||||
updatedAt: Date | string;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user