perf: build case-issue map once in stale-case-reminder; add idempotency TODO

- Hoist issues.list() loop outside stale-cases loop — reduces RPCs from
  O(cases × companies × issues) to O(companies × issues)
- Add TODO comment for requestId-based idempotency guard in onWebhook

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-17 10:15:51 +00:00
parent 09acb021eb
commit 98c87a5d70

View File

@@ -722,36 +722,37 @@ const plugin = definePlugin({
total: number;
};
let reminded = 0;
// Build case→issue map once (O(companies × issues)) to avoid N×M RPCs per stale case
const companies = await ctx.companies.list();
for (const staleCase of data.cases) {
for (const company of companies) {
const issues = await ctx.issues.list({ companyId: company.id });
let linkedIssueId: string | null = null;
for (const issue of issues) {
const linkedCase = await ctx.state.get({
scopeKind: "issue",
scopeId: issue.id,
stateKey: "legal-case-number",
});
if (linkedCase === staleCase.case_number) {
linkedIssueId = issue.id;
break;
}
const caseIssueMap = new Map<string, { issueId: string; companyId: string }>();
for (const company of companies) {
const issues = await ctx.issues.list({ companyId: company.id });
for (const issue of issues) {
const linkedCase = await ctx.state.get({
scopeKind: "issue",
scopeId: issue.id,
stateKey: "legal-case-number",
});
if (linkedCase && typeof linkedCase === "string") {
caseIssueMap.set(linkedCase, { issueId: issue.id, companyId: company.id });
}
if (!linkedIssueId) continue;
await ctx.issues.createComment(
linkedIssueId,
`⚠️ **תיק תקוע ${staleCase.case_number}** — ${staleCase.days_stale} ימים ללא עדכון (סטטוס: ${staleCase.status}). האם נדרשת פעולה?`,
company.id,
);
reminded++;
ctx.logger.info(`stale-case-reminder: reminded case ${staleCase.case_number} (${staleCase.days_stale}d)`);
break; // Found the company, move to next case
}
}
let reminded = 0;
for (const staleCase of data.cases) {
const linked = caseIssueMap.get(staleCase.case_number);
if (!linked) continue;
await ctx.issues.createComment(
linked.issueId,
`⚠️ **תיק תקוע ${staleCase.case_number}** — ${staleCase.days_stale} ימים ללא עדכון (סטטוס: ${staleCase.status}). האם נדרשת פעולה?`,
linked.companyId,
);
reminded++;
ctx.logger.info(`stale-case-reminder: reminded case ${staleCase.case_number} (${staleCase.days_stale}d)`);
}
ctx.logger.info(`stale-case-reminder: done. ${reminded}/${data.total} cases reminded`);
});
@@ -801,6 +802,9 @@ const plugin = definePlugin({
async onWebhook(input: PluginWebhookInput): Promise<void> {
if (!pluginCtx) return; // not yet initialized
// TODO: add idempotency guard using input.requestId to prevent duplicate
// comments on rapid retries (store requestId in plugin state with ~60s TTL)
const { endpointKey, parsedBody } = input;
if (endpointKey !== "case-status") return;