yams/docs/adrs/0003-fusion-strategy-analysis.md
trvon 1e50b0c531
Improve daemon shutdown reliability and timeout handling
SocketServer:
- Increase connection handler wait timeout from 100ms to 2s
- Increase accept loop wait timeout from 500ms to 3s
- Prevents race conditions where handlers are cancelled mid-operation

WorkCoordinator:
- Replace thread detach with extended join attempt
- Prevents use-after-free from detached threads accessing freed resources

Daemon Command:
- Increase shutdown wait timeout from 3s to 6s
- Increase default waitForDaemonStop timeout from 5s to 10s
- Allows time for graceful SocketServer shutdown

These changes address SIGABRT and SIGTERM issues during daemon shutdown
by ensuring async operations complete before resources are freed.
2026-01-29 11:55:07 -05:00

7 KiB

ADR-0003: Fusion Strategy Analysis and Simplification

Status: Draft Date: 2026-01-16 Epic: yams-eg1l Task: yams-7nib

Context

YAMS search engine currently implements 8 fusion strategies to merge results from 7 parallel components (FTS5, Vector, EntityVector, KG, PathTree, Tag, Metadata). This complexity causes:

  1. Quality regression: DENSE_FIRST prioritizes weak vector results (0.02-0.05 scores), causing hybrid MRR=0.002 vs keyword MRR=0.084
  2. Maintenance burden: ~600 lines of fusion code with significant duplication
  3. Configuration confusion: Users must understand 8 strategies to tune search

Current Strategies

Simple Strategies (use fuseSinglePass)

Strategy Formula Lines Usage
WEIGHTED_SUM score * weight 4 Unused
RECIPROCAL_RANK 1 / (k + rank) 5 Default (builder)
BORDA_COUNT weight * (100 - rank) 4 Unused
WEIGHTED_RECIPROCAL weight * 1/(k+rank) * (1+score) 12 Unused
COMB_MNZ weight * 1/(k+rank) * numComponents 16 Default (tuner)

Complex Strategies (custom loops)

Strategy Anchor Threshold Extra Boost Lines Usage
TEXT_ANCHOR text components vector > 0.85 0.15 * vectorScore 120 Unused
DENSE_FIRST vector components text rank < 20 text boost 140 SCIENTIFIC corpus
CONCEPT_ANCHOR vector components text rank < 20 concept match 200 Experimental

Analysis

Redundancy Matrix

                    Uses        Uses        Uses         Anchor    Code
Strategy            RRF         Weight      Score Boost  Logic     Duplication
─────────────────────────────────────────────────────────────────────────────
WEIGHTED_SUM        ✗           ✓           ✗            ✗         Base
RECIPROCAL_RANK     ✓           ✗           ✗            ✗         ~WEIGHTED_SUM
BORDA_COUNT         ✗           ✓           ✗            ✗         ~WEIGHTED_SUM
WEIGHTED_RECIPROCAL ✓           ✓           ✓            ✗         Superset
COMB_MNZ            ✓           ✓           ✗            ✗         +MNZ boost
TEXT_ANCHOR         ✓           ✓           ✓            text      Custom loop
DENSE_FIRST         ✓           ✓           ✓            vector    Custom loop
CONCEPT_ANCHOR      ✓           ✓           ✓            vector    ~DENSE_FIRST

Key Findings

  1. All simple strategies are mathematically similar: They differ only in the scoring function f(rank, score).

  2. TEXT_ANCHOR and DENSE_FIRST are inverses: Same code structure, opposite anchor logic.

  3. CONCEPT_ANCHOR duplicates DENSE_FIRST: ~80% code duplication, adds concept boost.

  4. Root cause of quality regression: DENSE_FIRST anchors on vector results. With weak embeddings (general-purpose all-MiniLM-L6-v2), this returns wrong documents.

  5. Component weights already provide tuning: The weights (textWeight=0.45, vectorWeight=0.15, etc.) control importance. Anchor logic adds unnecessary complexity.

Benchmark Evidence

SciFact dataset comparison (50 queries, all-MiniLM-L6-v2 embeddings):

Strategy Hybrid MRR Keyword MRR Delta Improvement
DENSE_FIRST 0.0020 0.0839 -0.0819 (baseline)
TEXT_ANCHOR 0.2402 0.8233 -0.5832 120x vs DENSE_FIRST

Full TEXT_ANCHOR results:

Metric Hybrid Keyword Delta
MRR 0.2402 0.8233 -0.5832
Recall@10 0.8200 0.8800 -0.0600
Precision@10 0.0840 0.0900 -0.0060
nDCG@10 0.3766 0.8376 -0.4610
MAP 0.2417 0.8233 -0.5816

Key observations:

  1. TEXT_ANCHOR is 120x better than DENSE_FIRST for hybrid search
  2. Keyword-only is still 3.4x better than best hybrid strategy
  3. Recall is comparable (0.82 vs 0.88), but ranking quality differs significantly
  4. With weak vector scores (0.02-0.05), anchor-based strategies cannot recover

Proposed Simplification

Unified Fusion Algorithm

Replace all 8 strategies with one configurable algorithm:

struct UnifiedFusionConfig {
    // Scoring parameters (always use weighted RRF with score boost)
    float rrfK = 60.0f;

    // Component weights (existing)
    float textWeight = 0.45f;
    float vectorWeight = 0.15f;
    // ... other weights

    // Optional: MNZ boost for multi-component matches
    bool enableMnzBoost = true;

    // Anchor mode removed - weights handle relative importance
};

double unifiedScore(const ComponentResult& comp, size_t componentCount) {
    double rrfScore = 1.0 / (rrfK + comp.rank);
    double scoreBoost = 1.0 + clamp(comp.score, 0.0, 1.0);
    double mnzBoost = enableMnzBoost ? componentCount : 1.0;
    return weight[comp.source] * rrfScore * scoreBoost * mnzBoost;
}

Why This Works

  1. Weights control importance: If text should dominate, set textWeight=0.70.
  2. MNZ naturally handles agreement: Documents found by multiple components rank higher.
  3. Score boost rewards confidence: High-scoring matches get boosted.
  4. No anchor threshold magic: No 0.85 vector threshold, no "top 20" text fallback.

Migration Path

Current Strategy Equivalent Unified Config
WEIGHTED_SUM rrfK=∞ (disable RRF), no MNZ
RECIPROCAL_RANK No weights, no MNZ
BORDA_COUNT rrfK=∞, no MNZ
WEIGHTED_RECIPROCAL Standard config, no MNZ
COMB_MNZ Standard config
TEXT_ANCHOR textWeight=0.70, vectorWeight=0.10
DENSE_FIRST textWeight=0.25, vectorWeight=0.70
CONCEPT_ANCHOR Separate concept boost phase

Recommendations

  1. Immediate: Benchmark TEXT_ANCHOR vs DENSE_FIRST on SciFact to confirm hypothesis ✅ Done (yams-f5m2)
  2. Phase 1: Implement unified fusion, deprecate simple strategies (yams-30a4)
  3. Phase 2: Update SearchTuner to adjust weights instead of strategies (yams-xssv)
  4. Phase 3: Remove deprecated strategies, simplify config (yams-wz5r)

Conclusion: TEXT_ANCHOR confirms hypothesis - anchor logic is not the solution. Even the best anchor strategy (TEXT_ANCHOR, 120x better than DENSE_FIRST) cannot match keyword-only quality. The unified weighted approach with component weights is the correct path forward.

Code Impact

Component Current LOC After Reduction
ResultFusion methods ~600 ~100 83%
FusionStrategy enum 8 values 0 100%
SearchTuner strategy selection 30 0 100%
Test coverage 8 strategies 1 87%

Decision

Proceed with unified fusion implementation per this analysis.

References

  • Epic: yams-eg1l
  • Benchmark debug log: /tmp/bench_debug.jsonl
  • SciFact dataset: 5,183 docs, 300 queries