trading-system-docs/tolaria/c5-sync-service/C5D_SEARCH_SOURCE_PIPELINE_ARCHITECTURE_DECISION.md
Red Queen 5e4191578a fix(tolaria): C5D search-source pipeline — build+verify source from current Tolaria before rebuild
- SearchSourceBuilder: deterministischer Vault->Source-Snapshot (read-only),
  atomar (temp->validate->fsync->replace), Secret-Scan fail-closed
- verify_integrity: exakte object_id/path Set-Equality (stale Source kann
  nie APPLIED), Canary implizit ueber erwartetes Objekt-Set
- apply_commit: Source-Build+Verification vor Rebuild; VERIFYING_SEARCH-Resume
  (kein Doppel-Rebuild) — C5E-Replay-Crash-Fall abgedeckt
- Fix: source_object_count ist keine 0-Fehlerbedingung (echter Defekt)
- SearchSourceBuildError + RC_SEARCH_SOURCE_BUILD_FAILURE (Human Gate)
- c4b: source_path env-konfigurierbar, indexed_object_ids/paths Read-Back
- Testsuite: 17 neue Tests (Test-Plan A-O + Realistic C4-Integration)
2026-08-26 12:12:45 +00:00

7.9 KiB
Raw Permalink Blame History

C5D — SEARCH SOURCE PIPELINE REPAIR: ARCHITECTURE_DECISION

Datum: 2026-08-26 · Autor: Red Queen (MAKER) · Status: FREIGEGEBEN (Christian) Mission: fehlende deterministische Verbindung TOLARIA VERIFIED STATE → SEARCH SOURCE BUILD/REFRESH → SEARCH REBUILD → SEARCH VERIFICATION implementieren.


1. Root Cause (aus C5F-Evidence, übernommen & bestätigt)

  • /app/index_source.json ist statisch baked-in (head 35446c03…, count=84) und wird von keinem C4/C5-Producer aktualisiert.
  • /api/search/rebuild konsumiert ausschließlich diese stale Source (server.py SOURCE_JSON hartkodiert).
  • C5D und C5E-Replay führen keinen Source-Build aus.
  • verify_integrity() akzeptiert object_count > 0 → kann einen semantisch stale Search-Stand als PASS bewerten.
  • Produktiver Rebuild (Rain): HTTP 200 status=ok indexed=84 blocked=0 bei Tolaria=85 / index_source=84 / Search=84 → Canary Discovery=FAIL. HTTP-200 allein darf NIE APPLIED ermöglichen.

2. ARCHITECTURE_DECISION

2.1 SOURCE_BUILDER_OWNER = C5DEngine

Der Source-Build gehört in C5D (nicht in C5C, nicht in den Search-Service selbst).

Begründung (gegen die Alternativen):

Kriterium C5C Search-Service (Weg A) C5D (GEWÄHLT)
Eindeutige Ownership ✗ C5C ist Forgejo→Tolaria; Search internes Detail ✗ koppelt Retrieval-Layer an Tolaria ✓ C5D orchestriert bereits den kompletten Search-Schritt
Deterministischer Input ✓ C5D läuft NUR nach Tolaria-Verifikation (DRIFT=0)
Atomare Veröffentlichung ✓ temp→validate→atomic rename→rebuild
Recovery ✓ C5D-State-Machine (idempotent)
Split-Brain-Gefahr hoch (2 Writer) hoch minimal (1 Owner)
Security Boundary ✓ Search-Token bleibt bei C5D
Testbarkeit mittel schlecht hoch (bestehendes Fake-Injektionsmuster)
Keine neue Source of Truth ✓ (ephemeral build artifact)

Gegen C5C: C5C-Docstring verbietet jeden Search-Bezug („Kein Search-Rebuild-Aufruf“); Source-Build würde C5C an Search-Internal koppeln und die Rollentrennung verletzen. Gegen Search-Service selbst: würde dem abgeleiteten Retrieval-Layer eine Tolaria-Leseabhängigkeit aufzwingen und die Ownership aufteilen. Der Service bleibt ein dummer Konsument einer Source-Datei (nur Pfad env-konfigurierbar).

Konsequenz: C5D liest Tolaria read-only (Source-Build). Die bestehende NO-TOLARIA-WRITE-GUARANTEE bleibt unverändert (verbietet nur /api/vault/save). Statische Guards werden entsprechend erweitert.

2.2 SOURCE_BUILD_CONTRACT (exakt, was rebuild_from_source liest)

Der Builder erzeugt deterministisch:

{
  "head": "<commit_sha des verifizierten Commits>",  // Provenance-Anker
  "count": <int>,
  "objects": [
    {
      "path": "<REQUIRED>", "title": str,
      "id": "<object/UUID> | null", "type": str|null,
      "role": str|null, "representation": str|null,
      "state": str|null, "knowledge_schema": str|null,
      "content_hash": sha256(body), "body": str,
      "aliases": [], "tags": [], "derived_from": str|null
    }, ...
  ]
}
  • content_hash = hashlib.sha256(body) (identisch zu rq_c5b.content_hash).
  • knowledge_schema wird von der Engine nicht konsumiert, ist aber Teil des bestehenden Source-Formats → wird aus Tolaria-Metadaten übernommen (kein erfundener Wert).
  • Kein einziges erfundenes Feld. Jedes Feld stammt aus dem gelesenen Tolaria-Objekt.

2.3 SOURCE_VERIFICATION_CONTRACT (Completeness, §5)

Vor dem Rebuild wird der erzeugte Snapshot vollständig verifiziert (nicht nur object_count):

  • SOURCE_OBJECT_COUNT == len(erwartete_indexierbare_object_ids)
  • UNIQUE_OBJECT_IDS (keine Duplikate)
  • UNIQUE_PATHS (keine Duplikate)
  • INVALID_OBJECTS == [] (fehlendes path, malformed)
  • SECRET_BLOCKED fail-closed
  • EXPECTED_SOURCE_HEAD == commit_sha
  • Objekt-Set-Gleichheit: set(object_ids) legacy_paths == erwartete_indexierbare_menge

Erwartete indexierbare Menge (deterministisch, definiert): = Menge der Tolaria-Vault-Objekte, die IN_SCOPE sind UND eine gültige object_id haben (via C5B KnowledgeScope/extract_object_id), PLUS die LEGACY_SPECIALs (README.md x2, vps.md). Objekte ohne gültige object_id (z.B. das start.md-Orphan, HUMAN_REVIEW) sind nicht indexierbar → werden ausgeschlossen. Das reproduziert deterministisch 84→85-Übergang (Canary hat gültige object_id).

Bei Abweichung: FAIL CLOSED, kein Rebuild.

2.4 ATOMICITY_MODEL (§6)

build temp → validate temp → fsync → os.replace (atomic rename) → erst dann rebuild. Ein Crash hinterlässt nie eine halb geschriebene Source: entweder alte komplette oder neue komplette Datei. Temp-Datei wird bei Restart verworfen (idempotent neu gebaut).

2.5 STATE_MACHINE_CHANGES (§8) — KEINE neuen States

Entscheidung: Source-Build + -Verification laufen atomar innerhalb des bestehenden UPDATING_SEARCH-Schritts, unmittelbar vor dem Rebuild. Kein neuer BUILDING_SEARCH_SOURCE/VERIFYING_SEARCH_SOURCE-State.

Begründung: (1) Build ist idempotent (kein Tolaria-Write → kein Doppel-Write-Risiko); (2) Restart re-entert aus UPDATING_SEARCH/RETRY_PENDING → Build wird deterministisch wiederholt; (3) Fehler werden über den bestehenden Retry/Human-Gate-Mechanismus klassifiziert. Die Invariante („kein Rebuild vor verifizierter Source“) ist gewahrt, ohne die State-Zahl kosmetisch aufzublähen.

Eine notwendige Erweiterung: C5DEngine.apply_commit akzeptiert zusätzlich ST_VERIFYING_SEARCH als Re-Entry (Crash nach Rebuild vor Health, §9-Fall D). Verhalten: idempotenter Re-Entry → vollständige Sequenz (Build→Rebuild→Health→Verify) wird sicher wiederholt.

2.6 SEARCH-SERVICE-ÄNDERUNGEN (Deployment, §10)

  • server.py: SOURCE_JSON wird env-konfigurierbar über TOLARIA_SEARCH_SOURCE (Default = bisheriger Pfad). Die baked-in index_source.json bestimmt nicht mehr die produktive Wahrheit — C5D schreibt den frischen derived Snapshot an den konfigurierten Pfad.
  • search_api.py health(): liefert zusätzlich indexed_object_ids + indexed_paths (Read-Back für deterministische Objekt-Set-Verifikation, §7). Persistierter Index enthält ebenfalls diese Mengen.
  • Keine Netzwerköffnung, kein öffentliches Port-Mapping, Secrets bleiben beim Search-/C5D-Executor-Kontext.

2.7 C5D_VERIFICATION_HARDENING (§7)

verify_integrity ersetzt die object_count > 0-Regel durch exakte Objekt-Set-Verifikation:

  • set(indexed_object_ids) == erwartete_indexierbare_ids
  • set(indexed_paths) == erwartete_indexierbare_paths
  • object_count == len(erwartet)
  • failed_objects == [], secret_blocked gemäß Contract
  • Canary-Beweis: canary_object_id ∈ indexed_object_ids UND canary_path ∈ indexed_paths (statt „HTTP 200 + count>0“).

Ein HTTP-200-Rebuild mit stale Source kann damit NIE APPLIED ermöglichen.


3. Betroffene Dateien

Datei Änderung
tolaria/c5-sync-service/rq_c5d.py + SearchSourceBuilder, verify_integrity-Härtung, apply_commit mit Build+Verify+VERIFYING_SEARCH-ReEntry, Guards
tolaria/c5-sync-service/rq_c5e.py Recovery berücksichtigt Source-Build-Schritt + VERIFYING_SEARCH-Resume
tolaria/c4b-search-service/server.py TOLARIA_SEARCH_SOURCE env-konfigurierbar
tolaria/c4b-search-service/search_api.py health() + persistierter Index liefert indexed_object_ids/indexed_paths
tolaria/c5-sync-service/test_c5d.py erweiterte Tests (AO)
tolaria/c5-sync-service/test_c5e.py Crash-Fälle d5
neu: tolaria/c5-sync-service/test_c5d_source.py Realistic Integration Test (echte C4-Engine, kein Fake)

Nicht angefasst: Produktions-DB, Canary-DB, index_source.json (bleibt als historische Evidence erhalten), Vault-Git-Mods, Forgejo-Master-Write im Repair.