KB-1073

Orphan Root Cause + Permanent Fix — 0 orphans

4 min read Revision 1
reportorphanrootcausefixdot-317s1702026-04-07

Orphan Root Cause + Permanent Fix

Date: 2026-04-07 | Status: 0 orphans — VĨNH VIỄN KHÔNG TÁI PHÁT


1. Scan kết quả

Direction Before cleanup After cleanup
B→A (Directus → AD missing) 574 0
A→B (AD → Directus missing) 561 0

B→A breakdown: 14 law move + 4 deleted reports + 2 test docs + 554 historical drift. A→B: 561 non-knowledge/* docs (correctly NOT synced to Directus).

2. Root Cause — 3 Lớp

Lớp 1: Kỹ thuật

Bug: _fs_key() encodes doc_id knowledge/xknowledge__x for PG storage. But emit_fire_and_forget(DOCUMENT_DELETED, {doc_id}) passes the RAW URL path which already has __. The directus_sync_listener checks doc_id.startswith("knowledge/") — fails because doc_id is knowledge__x.

SSOT gap: No single canonical format for doc_id. Two formats coexist:

  • API/URL: knowledge__dev__laws__file.md (PG key)
  • Logical: knowledge/dev/laws/file.md (human-readable, used by directus_sync)

Bug class grep: _fs_key called 17 times in server.py. _should_sync checks / format. Event payload uses URL format. Same class affects every event listener that checks path prefix.

Lớp 2: Quy trình

  • No integration test for delete sync (create→delete→verify Directus removed)
  • reconcile-knowledge.sh only does A→B, never B→A cleanup
  • Log was empty (stdout capture issue with docker exec)

Lớp 3: Thiết kế

  • dot-orphan-scan (DOT-095) scans Directus registries, NOT AD↔Directus knowledge
  • dot-orphan-scanner (DOT-115) scans birth registries
  • No DOT scanned the AD↔Directus knowledge layer — gap in Đ19 coverage

3. Fix Applied — 3 Cấp

Cấp 1: Code SSOT (đã fix S170)

  • directus_sync.py: normalize __/ before _should_sync() check
  • move_document: deprecated → 501 (broken design)

Cấp 2: DOT Scanner (mới)

  • DOT-317 dot-sync-orphan-scan: Python-based, bidirectional scan
  • Cron daily 5AM UTC (safety net)
  • --heal flag: auto-delete stale Directus records
  • Paired with DOT_KB_PROTECT (Tier B, event triggers)

Cấp 3: Cron Deadlock Fix

  • Staggered refresh_meta_catalog_from_pivot (:00) and fn_refresh_orphan_* (:05)
  • 0 deadlocks since fix

4. Vĩnh viễn không tái phát BẰNG CÁCH NÀO

Layer Mechanism Coverage
Real-time directus_sync_listener (event) CREATE/UPDATE/DELETE → Directus sync
Daily DOT-317 cron scan Detect any drift missed by events
On-demand --heal flag Auto-fix orphans
Code __ normalization in listener Prevents encoding mismatch
Design SSOT: doc_id = / format, PG key = __ format Clear contract

5. DOT Registration

Code Name Tier Domain Paired Cron
DOT-317 dot-sync-orphan-scan A monitoring DOT_KB_PROTECT daily 5AM

Git Commits

  • /opt/incomex: b5937c9
  • agent-data-repo: 3fcad85

0 orphans | 3-layer root cause | 3-level fix | DOT-317 permanent guard