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.
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:
- Quality regression: DENSE_FIRST prioritizes weak vector results (0.02-0.05 scores), causing hybrid MRR=0.002 vs keyword MRR=0.084
- Maintenance burden: ~600 lines of fusion code with significant duplication
- 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
-
All simple strategies are mathematically similar: They differ only in the scoring function
f(rank, score). -
TEXT_ANCHOR and DENSE_FIRST are inverses: Same code structure, opposite anchor logic.
-
CONCEPT_ANCHOR duplicates DENSE_FIRST: ~80% code duplication, adds concept boost.
-
Root cause of quality regression: DENSE_FIRST anchors on vector results. With weak embeddings (general-purpose all-MiniLM-L6-v2), this returns wrong documents.
-
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:
- TEXT_ANCHOR is 120x better than DENSE_FIRST for hybrid search
- Keyword-only is still 3.4x better than best hybrid strategy
- Recall is comparable (0.82 vs 0.88), but ranking quality differs significantly
- 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
- Weights control importance: If text should dominate, set textWeight=0.70.
- MNZ naturally handles agreement: Documents found by multiple components rank higher.
- Score boost rewards confidence: High-scoring matches get boosted.
- 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
Immediate: Benchmark TEXT_ANCHOR vs DENSE_FIRST on SciFact to confirm hypothesis✅ Done (yams-f5m2)- Phase 1: Implement unified fusion, deprecate simple strategies (yams-30a4)
- Phase 2: Update SearchTuner to adjust weights instead of strategies (yams-xssv)
- 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