/** * Discover facets — curated feature→capability fusion layer (#1623). * * The capability index (hybrid BM25 + dense) is near-optimal for a ~400-item * corpus, but bare domain words ("persona", "expansion") and feature-intent * phrases ("author an expansion", "scaffold a project") have weak lexical * scent: generic artifacts that merely contain the word out-rank the artifacts * that actually OWN the feature. This module adds a small, curated set of * **facets** that map feature intents to their owning capabilities and fuses * that signal into the single-pass `aiwg discover` ranking (reciprocal-rank * spirit: a second ranker combined by max-floor + injection). * * This is the *single-pass fused* form the ADR ships for #1623. The parallel * multi-vector fan-out over these same facets is the deferred #1626 work — the * facet table here is the shared contract both consume, so #1626 reuses * `DISCOVER_FACETS` rather than redefining domains. * * Design notes: * - Facets are *curated*, not learned — appropriate at this corpus size and * portable across all 10 providers (no embedding store, no fine-tuned * router). See `.aiwg/architecture/adr-steward-feature-discoverability.md`. * - Mapped capabilities are matched against an entry's canonical `name` * (falling back to the path basename / parent-dir slug), so the table lists * names — not paths — and survives bundle moves. * - Activation strength tiers the score floor so an exact domain-word query * ("persona") sorts the owning domain to the very top (above generic * capped-at-1.0 substring matches, mirroring the exact-name 1.001 floor in * query-engine), while looser token-overlap activations apply a gentler * floor that lifts the domain into the top-K without dominating. * * @implements #1623 (U3) */ import type { MetadataEntry } from './types.js'; export type FacetKind = 'feature-domain' | 'persona-identity' | 'authoring-surface' | 'provider-capability'; export interface FacetEntry { /** Which facet this entry belongs to (for fan-out weighting in #1626). */ facet: FacetKind; /** Human label for diagnostics / `aiwg index status` surfacing. */ label: string; /** * Lowercase intent phrases. A query activates this entry when it exactly * equals an intent, contains one (or is contained by one), or — for * multi-word intents — overlaps a majority of the intent's tokens. */ intents: string[]; /** * Canonical capability names this intent maps to (matched against * `entry.name`, then path basename / parent-dir slug). */ capabilities: string[]; } /** * The curated facet table. Kept deliberately small and legible — it is a * routing index, not a knowledge base. Guarded by the discover acceptance * test (canonical phrases must rank their targets top-3) and the * metadata-completeness lint (empty triggers cannot recur). */ export declare const DISCOVER_FACETS: FacetEntry[]; /** * Per-facet (and base-ranker) weights for the RRF fusion (#1626). Tunable so * operators can bias the fan-out — e.g. dial down provider-capability when a * project never asks provider questions. Defaults preserve the #1623 ranking. */ export interface FacetWeights { /** Weight of the base lexical ranker in the fusion. */ base: number; /** Per-facet-kind weights (missing kinds default to 1.0). */ facets: Partial>; } export declare const DEFAULT_FACET_WEIGHTS: FacetWeights; /** * Multi-vector facet fan-out + reciprocal-rank fusion (#1623 single-pass → * #1626 parallel fan-out). Fans out across the curated facet vectors in * parallel, fuses them with the base lexical ranker via RRF, and lifts each * facet-matched capability to its activation floor so it out-ranks generic * artifacts that merely mention the domain word. * * The fused result is a SUPERSET of the #1623 single-pass behavior (ADR * contract): facet-matched entries are guaranteed a place at no less than the * activation floor (injected if the lexical pass missed them), base-only * entries keep their lexical score, and RRF consensus orders entries within a * floor tier (so multi-facet / more-relevant capabilities sort first) without * changing the emitted 2-decimal score. Per-facet weighting is configurable. * * The returned list is re-sorted but NOT truncated — the caller applies limit. * Pure/deterministic: no IO, no mutation of inputs. */ export declare function applyFacetFusion(scored: Array<{ entry: MetadataEntry; score: number; }>, candidates: MetadataEntry[], phrase: string, weights?: FacetWeights): Promise>; //# sourceMappingURL=discover-facets.d.ts.map