# Universal Blueprint (Template)

## 1. Metadata & Context
* **Title:** Integration Plan: Penerapan Dynamic Threshold & Multi-Tenant Security (Sub-task 2)
* **Date:** 16-05-2026
* **Objective:** Mengintegrasikan arsitektur *Dynamic Threshold* (Z-Score) ke dalam aplikasi produksi. Ini mencakup pembuatan skrip *cron job* untuk pembaruan *threshold* di latar belakang, penyisipan file konfigurasi JSON untuk *zero-latency reads*, dan penerapan isolasi *Knowledge Graph* per-project (*Multi-Tenant Security*).
* **Methodology:** 
  Modifikasi akan dilakukan pada file `app/services/ingestion.py` untuk menghilangkan variabel *hardcoded* dan menggantinya dengan fungsi baca JSON. Selain itu, kueri Cypher akan diperbarui untuk mengikat relasi (*Edge*) berdasarkan `project_id`. Secara paralel, sebuah skrip `update_global_threshold.py` akan diciptakan sebagai *background worker* yang bertugas menghitung dan menimpa *file* JSON tersebut setiap jam 02:00 pagi, dengan syarat populasi literatur baru melebihi 100 dokumen.
* **Assumptions & Constraints:** 
  *   **Asumsi:** File `config/thresholds.json` telah diinisialisasi dengan nilai *fallback* statis hasil dari Sub-task 1.
  *   **Constraint:** Tidak ada perubahan pada alur *parsing* PDF, chunking, atau *prompt* LLM.
  *   **Scope:** Modifikasi murni di *Retrieval Gate* (pencarian vektor) dan manajemen konfigurasi.

* **Strategic Decider:**
  | Option/Decision | Alternatives | Rationale | Status |
  | :--- | :--- | :--- | :--- |
  | External JSON Caching | *Database Query* setiap kali pencarian | Melakukan kueri ke *database* untuk mengambil nilai *threshold* menambah latensi I/O. Membaca *file* JSON dari disk jauh lebih cepat dan mengurangi beban Neo4j. | Locked |
  | Hybrid Cron Job (Time + Event) | *Time-based* murni (Setiap hari) atau *Event-based* murni (Tiap n-upload) | Memadukan jadwal waktu (jam 2 pagi) dengan syarat *event* (>100 dokumen baru) memastikan komputasi berat hanya terjadi jika benar-benar dibutuhkan, tanpa mengganggu jam sibuk pengguna. | Locked |

## 2. Structure & Dependencies
* **Folder Tree:** 
```text
/home/daegir/Documents/PERCOBAAN/backend-sre(prompt-modularization-v3)/backend-sre/
├── config/
│   └── thresholds.json                           # [MODIFIED] File cache threshold (akan ditimpa cron)
├── scripts/
│   └── update_global_threshold.py                # [NEW] Skrip Cron Job
├── app/
│   └── services/
│       └── ingestion.py                          # [MODIFIED] Logic baca JSON & Filter project_id
└── Markdown/
    └── Dynamic-Threshold-Retrieval/
        └── Plan/
            └── integration_plan.md               # [FILE INI]
```
* **Dependency Graph:** 
  | Affected File | Main Module | Impact |
  | :--- | :--- | :--- |
  | `ingestion.py` | `app.services.ingestion` | Mengubah deklarasi `PILLAR_THRESHOLD_MATRIX`. Memodifikasi `create_literature_node` dan `find_similar_nodes` untuk menerima argumen `project_id`. |
  | `update_global_threshold.py` | Standalone | Membaca Neo4j, menghitung statistik `numpy`, menulis ulang `config/thresholds.json`. |

## 3. Concept & Logic
* **Flow Diagram (Mermaid):** 
```mermaid
flowchart TD
    classDef init fill:#e3f2fd,stroke:#1565c0,stroke-width:2px;
    classDef logic fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px;
    classDef io fill:#fff3e0,stroke:#f57c00,stroke-width:2px;
    
    subgraph S1 [Background Cron Job: Global Calibration]
        direction TB
        A1[Start 02:00 AM]:::init --> A2{New Nodes > 100?}:::logic
        A2 -- Yes --> A3[Query Neo4j: Get N Random Pairs]:::io
        A3 --> A4[Calculate Mean & StdDev]:::logic
        A4 --> A5[Apply Section-Specific Multipliers]:::logic
        A5 --> A6[Overwrite config/thresholds.json]:::io
        A2 -- No --> A7[Abort Job: Maintain Last State]:::init
    end

    subgraph S2 [Runtime Ingestion Pipeline: Local Edge Gen]
        direction TB
        B1[Worker Edge Gen Triggered]:::init --> B2[Read config/thresholds.json]:::io
        B2 --> B3{Is JSON Valid/Exists?}:::logic
        B3 -- No (Cold Start) --> B4[Apply Static Fallback Threshold]:::logic
        B3 -- Yes --> B5[Apply Dynamic Z-Score Threshold]:::logic
        B4 --> B6
        B5 --> B6[Neo4j: find_similar_nodes]:::io
        B6 --> B7[STRICT FILTER: target.project_id == source.project_id]:::logic
        B7 --> B8{Score >= Pillar Threshold?}:::logic
        B8 -- Yes --> B9[Proceed to Coherence & Domain Gates]:::logic
        B8 -- No --> B10[Reject Candidate Edge]:::init
    end
    
    subgraph S3 [Phase 5: Reset & Rebuild Validation]
        direction TB
        C1[Delete All SIMILAR_* Edges]:::io --> C2[Load New Thresholds from JSON]:::io
        C2 --> C3[Re-run Edge Generation for All Nodes]:::logic
        C3 --> C4[Count New Edges per Pillar]:::io
        C4 --> C5{Edge Count Reasonable?}:::logic
        C5 -- Yes --> C6[Validation Pass]:::init
        C5 -- No --> C7[Investigate: Too Many/Few Edges]:::init
    end
```

* **Execution Synopsis:** 
  | Intent | Goal | Step | Status |
  | :--- | :--- | :--- | :--- |
  | Setup Konfigurasi | Menyiapkan keranjang nilai dinamis. | Membuat file `config/thresholds.json` dengan nilai *fallback* statis. | Planned |
  | Refactor `ingestion.py` | Melepas ikatan *hardcoded* threshold dan menyuntikkan `project_id`. | Menambah fungsi `get_current_thresholds()`. Mengubah *Cypher query* di `find_similar_nodes` dan `create_literature_node`. | Planned |
  | Buat Worker Job | Membangun agen penghitung statistik latar belakang. | Menulis `update_global_threshold.py` yang akan memeriksa jumlah node baru dan mengeksekusi perhitungan jika syarat terpenuhi. | Planned |
  | Reset & Rebuild Edge | Validasi threshold baru dengan edge formation. | Hapus semua edge existing, re-run edge generation dengan threshold Z-Score, bandingkan jumlah edge lama vs baru. | Planned |

* **Data Contract:** 
  *   **Cypher (Node Creation):** `MERGE (n:Literature {hash: $hash}) ON CREATE SET n.project_id = $project_id ...`
  *   **Cypher (Vector Retrieval):** `CALL db.index.vector.queryNodes(...) YIELD node, score WHERE node.project_id = $project_id AND score >= $dynamic_threshold`

## 4. Execution & Quality
* **Execution Matrix:**
  | Checkbox | Phase | Task | Detail Task | Description | Acceptance Criteria | Input / Output State | Validation Command | Risk |
  | :---: | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
  | [x] | 1 | Setup | Init JSON Config | Buat direktori `config/` dan file `thresholds.json`. | File eksis dan dapat di-*parse* oleh Python `json.load`. | Text -> JSON Dict | - | Rendah |
  | [x] | 2 | Refactoring | Edit `ingestion.py` (Threshold) | Hapus variabel `PILLAR_THRESHOLD_MATRIX` lama, ganti dengan fungsi pembaca JSON. | Aplikasi tidak *crash* saat dijalankan dan menggunakan nilai dari JSON. | JSON -> Python Dict | `python app/services/ingestion.py` | Menengah |
  | [x] | 3 | Security | Edit `ingestion.py` (Isolasi Data) | Tambah argumen `project_id` ke fungsi pembuat node dan pencari vektor. Sesuaikan kueri Cypher. | Node baru memiliki *property* `project_id`. Pencarian vektor menolak kandidat dari *project_id* yang berbeda. | String ID -> Cypher Parameter | Unit Test / Manual Test | Tinggi |
  | [x] | 4 | Automation | Buat `update_global_threshold.py` | Tulis skrip yang memeriksa jumlah node, menghitung Z-Score, dan menulis ke JSON jika node baru > 100. | Skrip berhasil menghitung nilai yang rasional dan sukses menimpa isi file `thresholds.json`. | DB Status -> JSON File | `python scripts/update_global_threshold.py` | Tinggi |
  | [x] | 5 | Validation | Reset Edge & Rebuild | Hapus semua edge existing (SIMILAR_*), re-run edge generation dengan threshold baru dari JSON, validasi jumlah edge baru. | Edge lama terhapus sempurna. Edge baru terbentuk dengan threshold Z-Score. Tidak ada node isolated yang tidak wajar. | Old Edges -> New Edges | `MATCH ()-[r]->() WHERE type(r) STARTS WITH "SIMILAR_" RETURN type(r), count(r)` | Tinggi |

* **Exit Conditions (Hard Gate):** 
  * Isolasi data terbukti sempurna: Pengguna A tidak dapat menemukan jurnal Pengguna B dalam grafik relasinya.
  * Aplikasi tidak mengalami *slowdown* (latensi I/O disk untuk membaca JSON harus di bawah 5 milidetik).
* **Resource Requirements:** 
  * Akses *read/write* ke *filesystem* lokal untuk *update* JSON.
* **Rollback Strategy:** 
  * *Revert* kode `ingestion.py` via Git. Kembalikan variabel *hardcoded* dan buang fungsi baca JSON. Hapus *property* `project_id` di Neo4j jika diperlukan.