# Three-Layer Signal Merging

```mermaid
flowchart TD
    subgraph Layer1["Layer 1 — Entity Signals"]
        DIR["document_impact_records\n(per-entity extraction output)"]
        DIR -->|"build_weighted_signals()"| WS1["WeightedSignal[]\nweight = 1.0 (full)"]
    end

    subgraph Layer2["Layer 2 — Macro Signals"]
        MIR["macro_impact_records\n(global event interpolation)"]
        MIR -->|"build_macro_weighted_signals()"| WS2["WeightedSignal[]\nimpact × MACRO_SIGNAL_WEIGHT\n(0.3)"]
        TOGGLE_M{"macro_enabled\nin risk_configs?"}
        TOGGLE_M -->|"true"| MIR
        TOGGLE_M -->|"false"| SKIP_M["Layer skipped\ngraceful degradation"]
    end

    subgraph Layer3["Layer 3 — Competitive Signals"]
        CSR["competitive_signal_records\n(pattern mining + propagation)"]
        CSR -->|"build_pattern_weighted_signals()\nservices/aggregation/signal_propagation.py"| WS3["WeightedSignal[]\nimpact × COMPETITIVE_SIGNAL_WEIGHT\n(0.2)"]
        TOGGLE_C{"competitive_enabled\nin risk_configs?"}
        TOGGLE_C -->|"true"| CSR
        TOGGLE_C -->|"false"| SKIP_C["Layer skipped\ngraceful degradation"]
    end

    WS1 --> MERGE["Concatenate all WeightedSignal lists"]
    WS2 --> MERGE
    WS3 --> MERGE

    MERGE --> AGG

    subgraph AGG["Aggregation Engine\nservices/aggregation/worker.py"]
        A1["weighted_sentiment_average()"]
        A2["detect_contradictions()\nservices/aggregation/contradiction.py"]
        A3["derive_trend_direction()"]
        A4["compute_trend_confidence()"]
        A5["rank_evidence()"]
        A1 --> A2 --> A3 --> A4 --> A5
    end

    AGG -->|"assemble_trend_summary()"| TS["TrendSummary\nservices/shared/schemas.py"]

    TS -->|"persist_trend_summary()"| PG_TREND

    subgraph PG_TREND["PostgreSQL"]
        TW["trend_windows\n(upserted each cycle)"]
        TH["trend_history\n(time-series snapshots)"]
        TE["trend_evidence\n(per-document rankings)"]
    end

    AGG -->|"rpush"| Q_REC["app:queue:recommendation"]
```
