# Ingestion-to-Extraction Flow

```mermaid
flowchart TD
    subgraph Scheduler["Scheduler\nservices/scheduler/app.py"]
        S1["schedule_cycle()"]
        S2["Cadence check\nmarket_api: 300s\nnews_api: 300s\nfilings_api: 3600s\nmacro_news: 600s"]
        S3["Rate limit check\ncheck_rate_limit()"]
        S1 --> S2 --> S3
    end

    S3 -->|"rpush"| Q_ING["stonks:queue:ingestion"]

    Q_ING -->|"lpop"| ING

    subgraph ING["Ingestion Worker\nservices/ingestion/worker.py"]
        direction TB
        AD["Adapter Dispatch\nprocess_job()"]
        AD --> PA["PolygonMarketAdapter\nservices/adapters/market_adapter.py"]
        AD --> PB["PolygonNewsAdapter\nservices/adapters/news_adapter.py"]
        AD --> PC["SECEdgarAdapter\nservices/adapters/filings_adapter.py"]
        AD --> PD["MacroNewsAdapter\nservices/adapters/macro_news_adapter.py"]
        AD --> PE["WebScrapeAdapter\nservices/adapters/web_scrape_adapter.py"]
    end

    ING -->|"Content hash check\nstonks:dedupe:*\nTTL 24h"| REDIS_DEDUPE[("Redis\nDedupe Markers")]

    ING -->|"upload_raw_artifact()"| MINIO_RAW

    subgraph MINIO_RAW["MinIO Raw Storage"]
        B1["stonks-raw-market"]
        B2["stonks-raw-news"]
        B3["stonks-raw-filings"]
    end

    ING -->|"persist_ingestion_items()"| PG_ING

    subgraph PG_ING["PostgreSQL"]
        T1["documents"]
        T2["ingestion_runs"]
        T3["document_company_mentions"]
    end

    ING -->|"rpush new doc IDs"| Q_PARSE["stonks:queue:parsing"]

    Q_PARSE -->|"lpop"| PARSER

    subgraph PARSER["Parser Worker\nservices/parser/worker.py"]
        P1["fetch_html() → parse_html()"]
        P2["Quality scoring\nconfidence: high / medium / low"]
        P3["Company mention detection\ndetect_company_mentions()"]
        P4["Routing decision"]
        P1 --> P2 --> P3 --> P4
    end

    PARSER -->|"upload_normalized_text()\nupload_parser_output()"| MINIO_NORM["MinIO\nstonks-normalized"]
    PARSER -->|"update_document_parse_results()"| PG_ING

    P4 -->|"doc_type = macro_event"| Q_MACRO["stonks:queue:macro_classification"]
    P4 -->|"doc_type ≠ macro_event"| Q_EXT["stonks:queue:extraction"]

    Q_EXT -->|"lpop"| EXT
    Q_MACRO -->|"lpop"| EXT

    subgraph EXT["Extractor Worker\nservices/extractor/main.py"]
        E1["Document Intelligence\nExtractor agent\nslug: document-extractor"]
        E2["Global Event Classifier\nslug: event-classifier\nservices/extractor/event_classifier.py"]
        E3["persist_extraction()\nservices/extractor/worker.py"]
    end

    EXT -->|"persist to"| PG_EXT

    subgraph PG_EXT["PostgreSQL"]
        T4["document_intelligence"]
        T5["document_impact_records"]
        T6["global_events"]
        T7["macro_impact_records"]
    end

    EXT -->|"rpush"| Q_AGG["stonks:queue:aggregation"]
```
