From f78d60e3aebef07f182d2c64f862db05e3b53f19 Mon Sep 17 00:00:00 2001 From: ValhallaBuilder <286693580+4gjnbzb4zf-sudo@users.noreply.github.com> Date: Sun, 7 Jun 2026 19:23:34 -0400 Subject: [PATCH] perf(h70-skill-matcher): cache phrase key subset to eliminate per-task scan --- src/lib/services/h70-skill-matcher.ts | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/lib/services/h70-skill-matcher.ts b/src/lib/services/h70-skill-matcher.ts index b58ee856..3c4a3a2a 100644 --- a/src/lib/services/h70-skill-matcher.ts +++ b/src/lib/services/h70-skill-matcher.ts @@ -556,6 +556,23 @@ const TASK_TYPE_TO_SKILLS: Record = { 'AI Integration': ['llm-architect', 'ai-agents-architect', 'prompt-engineer'], }; +/** + * Multi-word phrase keys precomputed at module load. + * + * `KEYWORD_TO_SKILLS` is a module-level constant whose key set never changes + * at runtime, so the phrase subset is invariant. Recomputing + * `Object.keys(KEYWORD_TO_SKILLS).filter(k => k.includes(' '))` inside + * `extractKeywords` walked all ~180 keys plus an extra phrase-filter pass + * on every call. `matchTaskToSkills` (and therefore `extractKeywords`) is + * invoked once per task by `mission-builder.ts`, `prd-auto-dispatch.ts`, and + * the batch helpers below — a 30-50 task mission paid that scan 30-50 times + * for the exact same result. Caching it here keeps the cost on the module's + * load tick and preserves a stable hot-path cost per task. + */ +const KEYWORD_PHRASES: readonly string[] = Object.freeze( + Object.keys(KEYWORD_TO_SKILLS).filter((k) => k.includes(' ')) +); + /** * Extract keywords from task name/description */ @@ -563,9 +580,8 @@ function extractKeywords(text: string): string[] { const normalized = text.toLowerCase(); const keywords: string[] = []; - // Check multi-word phrases first - const phrases = Object.keys(KEYWORD_TO_SKILLS).filter(k => k.includes(' ')); - for (const phrase of phrases) { + // Check multi-word phrases first (phrase list precomputed once at module load) + for (const phrase of KEYWORD_PHRASES) { if (normalized.includes(phrase)) { keywords.push(phrase); }