trading-system-docs/tolaria/c5-sync-service
2026-08-26 05:55:38 +00:00
..
.gitignore feat(tolaria): add C5 sync state machine 2026-08-26 05:03:19 +00:00
README.md feat(tolaria): add C5 Forgejo polling and change detection 2026-08-26 05:13:33 +00:00
rq_c5_cli.py feat(tolaria): add C5 Tolaria propagation and drift verification 2026-08-26 05:55:38 +00:00
rq_c5a.py feat(tolaria): add C5 Tolaria propagation and drift verification 2026-08-26 05:55:38 +00:00
rq_c5a_cli.py feat(tolaria): add C5 sync state machine 2026-08-26 05:03:19 +00:00
rq_c5b.py fix(c5b): persist human-review object changes + read content_before from parent commit 2026-08-26 05:20:38 +00:00
rq_c5c.py feat(tolaria): add C5 Tolaria propagation and drift verification 2026-08-26 05:55:38 +00:00
test_c5a.py feat(tolaria): add C5 sync state machine 2026-08-26 05:03:19 +00:00
test_c5b.py fix(c5b): persist human-review object changes + read content_before from parent commit 2026-08-26 05:20:38 +00:00
test_c5c.py feat(tolaria): add C5 Tolaria propagation and drift verification 2026-08-26 05:55:38 +00:00

C5 SYNC SERVICE — CONTRACT & STATE MACHINE (C5A) + FORGEJO POLLING & CHANGE DETECTION (C5B)

Phase: C5A (Contract & State Machine) + C5B (Forgejo Polling & Change Detection) Status: C5A = DONE · C5B = DONE (Polling & Change Detection) — INAKTIV, NO-WRITE Architektur: FORGEJO MASTER → C5 SYNC SERVICE → TOLARIA DERIVED → SEARCH FULL REBUILD

C5A implementiert ausschließlich den Contract + die Sync State Machine + die persistente Progress-State-Struktur. C5B baut darauf die Forgejo Polling & Change Detection Engine (read-only). Beide sind deterministische, inaktive Python-Libraries + CLI + Testsuite — sie führen keine externen Writes aus.


NO-WRITE GUARANTEE (verbindlich, C5A + C5B)

C5A + C5B können NOCH NICHT:

  • Tolaria schreiben
  • Search rebuilden
  • Forgejo schreiben
  • produktiv pollen (kein Poll-Daemon)
  • Netzwerk-Mutationen ausführen

C5A hat keine Netzwerk-/HTTP-/Socket-/Subprocess-Mutationsfunktionen (assert_no_write_guarantee()).

C5B führt ausschließlich read-only git-Befehle gegen den lokalen Clone aus (strikte Whitelist: rev-parse, log, show, diff-tree, ls-tree, cat-file, merge-base, rev-list, diff). Jeder Write-Befehl (push/commit/add/reset/checkout/merge/rebase) wird mit GitWriteBlockedError blockiert. Die No-Downstream-Write-Guarantee wird statisch per assert_no_downstream_write() bewiesen (keine HTTP-Mutationsimporte, keine /api/vault/save-//api/search/rebuild-Endpoints im Code, keine Git-Write-Befehle in der Whitelist) und durch den Test test_no_downstream_write abgesichert.


C5B — FORGEJO POLLING & CHANGE DETECTION ENGINE

FORGEJO_READ_METHOD

A) lokale Git-Mirror/Clone-Analyse (bevorzugt).

  • AUTH_REQUIREMENT: KEIN (read-only gegen lokalen Clone; kein Write-Token)
  • NETWORK_DEPENDENCY: KEIN (lokale .git-Objekte; kein HTTP)

POLLING_MODEL

  • Default: 60 Sekunden
  • Environment-konfigurierbar: C5_POLL_INTERVAL_SECONDS (z.B. =120)
  • C5B implementiert die Polling-Logik, startet aber KEINEN dauerhaften Produktions-Daemon. Tests führen einzelne kontrollierte poll_once-Zyklen aus.

HEAD_DISCOVERY

get_current_head() → Commit-SHA + parent/timestamp/message. Bei nicht erreichbarem Forgejo: RC_FORGEJO_UNAVAILABLE, kein State-Fortschritt.

COMMIT_RANGE_MODEL

Aus last_seen_commit (C5B-Baseline) bis HEAD deterministisch alle fehlenden Commits in chronologischer (parent-korrekter) Reihenfolge. Kein Überspringen, keine Doppelerfassung. Merge-Commits werden explizit behandelt (alle Parents). Divergenz/History-Rewrite wird erkannt.

Wichtig: C5B-Baseline ist last_seen_commit. last_applied_commit bleibt laut C5A-Contract für die spätere Propagation (C5C) reserviert und wird von C5B NIE fortgeschrieben.

HISTORY_SAFETY

Wenn last_seen_commit kein Ancestor von HEAD ist: FAIL CLOSED (RC_OUT_OF_ORDER_COMMIT). Keine automatische Rebase-/Reset-Logik.

DIFF_MODEL

Für jeden neuen Commit: git diff-tree -r --name-status -M -C --rootADDED / MODIFIED / DELETED / RENAMED / MOVED. Bei unsicherer Rename-Detection wird nicht geraten — Ambiguität wird sauber klassifiziert (Human Review).

KNOWLEDGE_SCOPE_MODEL

Nicht jede Forgejo-Datei ist automatisch Tolaria-Knowledge:

  • IN_SCOPE: Markdown im Repo-Root ODER unter notes/trading/system-docs/
  • OUT_OF_SCOPE: tolaria/, a2-5/, red-queen-architecture/, notion-safety-brain/, notion-command-center/, backup_patches/, notes/reference/, Nicht-Markdown, Build-Artefakte
  • LEGACY_SPECIAL: README.md (x2, KEEP_DISTINCT) + vps.md (DEFERRED)
  • HUMAN_REVIEW: Knowledge-Objekt ohne gültige object_id

Keine Secrets/Build-Artefakte in Sync-Change-Sets. Nicht blind .md = Sync.

OBJECT_ID_MODEL

object_id wird aus dem Frontmatter gelesen (id: object/<FULL_UUID>), nicht aus dem Pfad erzeugt, nicht neu generiert, nicht geraten. Ohne gültige ID: UNKNOWN_OBJECT_ID / UNKNOWN_LEGACY_OBJECT → Human Gate.

HASH_MODEL

  • content_hash: SHA-256 des fachlichen Bodies (nach Frontmatter)
  • metadata_hash: SHA-256 der relevanten Knowledge-Schema-Metadaten (id, type, role, representation, state, derived_from, tags, knowledge_schema). Pfad ist separat.
  • Deterministisch und testbar.

CHANGE_CLASSIFICATION

Aus Before/After deterministisch: CREATE, CONTENT_UPDATE, METADATA_UPDATE, STATE_UPDATE, RENAME, MOVE, SOURCE_CANONICAL_RELATION_UPDATE, TAGS_UPDATE, SUPERSEDE, DELETE_REQUEST. Bei mehreren geänderten Aspekten in EINEM Objekt werden mehrere ObjectChange-Einträge erzeugt (keine Information verloren).

RENAME_MOVE_MODEL

Rename/Move nur automatisch, wenn gleiche object_id + eindeutige Git-/Content-Evidence. ID bleibt stabil. Rename = Dateiname geändert (gleiches Verzeichnis); Move = Verzeichnis geändert. Bei Ambiguität: Human Gate.

DELETE_MODEL

Git-Delete wird NIEMALS zu automatischem Hard Delete. C5B erzeugt DELETE_REQUEST → C5A führt es in Human Review. Keine Tolaria-Aktion.

PAIR_MODEL

Wenn Root/source + canonical gemeinsam verändert werden, erkennt C5B die Pair-Beziehung (derived_from). Ordnung für spätere C5C: Source zuerst, Canonical danach. Noch nicht propagiert — Output enthält genug Information für C5C.

SECRET_SAFETY

C5B schreibt keine Secret-Inhalte in Logs/SQLite/Evidence. Bei Secret-/Forbidden-Dateien: nur sichere Metadaten (path, reason_code, commit_sha). Enthält ein Knowledge-Dokument einen möglichen Secret-Wert: RC_SECRET_DETECTED FAIL CLOSED. Kein Wert wird ausgegeben.

C5A_INTEGRATION

C5B speist gefundene Commits + ObjectChanges in den C5A Store ein (nur State/Persistence, kein Network Write downstream). Flow: Poll once → discover commits → classify changes → register commit → add object changes → Status DISCOVERED/VALIDATING (bzw. HUMAN_REVIEW_REQUIRED). Nicht weiter zu PROPAGATING_TOLARIA.

OBSERVABILITY

C5B erweitert sichere Statusdaten: last_seen_commit, poll_timestamp, commits_discovered, objects_discovered, out_of_scope_count, last_poll_status, last_poll_error_code. Keine Content-Logs.


State Machine (C5A)

Normalzustände

DISCOVERED → VALIDATING → READY → PROPAGATING_TOLARIA → VERIFYING_TOLARIA → UPDATING_SEARCH → VERIFYING_SEARCH → APPLIED

Fehlerzustände

RETRY_PENDING, FAILED, DEAD, HUMAN_REVIEW_REQUIRED

Ordering

WAITING_FOR_PREDECESSOR — Commits werden nur in korrekter Reihenfolge verarbeitet (parent_sha / last_applied_commit). Lücken werden nicht übersprungen.

Kerninvariante

last_applied_commit wird NUR nach vollständigem Tolaria+Search PASS fortgeschrieben (mark_applied wird nur nach VERIFYING_SEARCH → APPLIED aufgerufen).


Commit Contract

Eine Verarbeitungseinheit = ein Forgejo Commit:

Feld Beschreibung
commit_sha Primärschlüssel
parent_sha Vorgänger (Ordering)
discovered_at Zeitstempel
sequence Reihenfolge
status Sync-Zustand
retry_count Retry-Zähler
last_error / last_error_code Fehler + Reason Code
created_at / updated_at Zeitstempel

Pro Changed Object

object_id, path_before, path_after, operation, content_hash_before, content_hash_after, metadata_hash_before, metadata_hash_after, representation, state


Operation Model

CREATE, CONTENT_UPDATE, METADATA_UPDATE, STATE_UPDATE, RENAME, MOVE, SOURCE_CANONICAL_RELATION_UPDATE, TAGS_UPDATE, SUPERSEDE, DELETE_REQUEST

DELETE_REQUEST bleibt Human-Gate — kein automatisches Hard Delete.


Idempotenz

Basis: commit_sha + object_id + operation + Hash-Checks.

Ergebnis Bedeutung
ALREADY_APPLIED Commit bereits vollständig angewendet
ALREADY_AT_TARGET Ziel-Hash bereits erreicht
RETRY_SAFE Noch nicht angewendet, sicher zu verarbeiten
CONFLICT Existiert, aber Hash weicht ab

Doppelte Commit-Erkennung ist deterministisch.


Ordering

Commits nur in korrekter Reihenfolge. Fehlender Vorgänger → WAITING_FOR_PREDECESSOR (Reason Code OUT_OF_ORDER_COMMIT). Nach Anwendung des Vorgängers wird der Commit erneut validiert.


Failure / Retry State

  • max_retries = 5 (konfigurierbar)
  • Backoff: 1 / 2 / 4 / 8 / 16 Sekunden (gekappt)
  • Nach Max → DEAD oder HUMAN_REVIEW_REQUIRED
  • Kein Endlos-Retry

Persistence

SQLite (c5a.db), isoliert, restart-fest. Anforderungen erfüllt:

  • atomic (SQLite-Transaktionen, WAL)
  • restart-safe (neuer Store auf gleicher DB behält State)
  • inspectable (CLI health, list-commits, commit-status)
  • backupbar (einzelne Datei)
  • keine externe DB nötig
  • kein Trading-/Forgejo-DB-Coupling (eigene DB)

Bootstrap State

UNINITIALIZED → RECONCILING → BASELINE_READY → ACTIVE

Baseline darf nur aus BASELINE_READY gesetzt werden (Master↔Tolaria- Reconciliation muss sauber sein). In C5A wird keine produktive Baseline gesetzt.


Reason Codes (geschlossene Menge)

UNEXPECTED_TOLARIA_DRIFT, ID_COLLISION, UNKNOWN_OBJECT_ID, INVALID_SCHEMA, DANGLING_DERIVED_FROM, AMBIGUOUS_DELETE, UNKNOWN_LEGACY_OBJECT, AUTH_FAILURE, SEARCH_REBUILD_FAILURE, TOLARIA_UNAVAILABLE, FORGEJO_UNAVAILABLE, OUT_OF_ORDER_COMMIT, SECRET_DETECTED

Kein freier String-Wildwuchs als einziges Fehlerformat — unbekannte Reason Codes werden abgelehnt (InvalidReasonCodeError).


Security Boundary (dokumentiert, NICHT implementiert)

  • Forgejo credential: READ ONLY (C5A hält keinen Forgejo-Write)
  • Tolaria write: nur später C5 Service
  • Search rebuild: nur C5 Service Token
  • Agents: keine direkten Tolaria-/Search-Admin-Writes
  • Netzwerk-Härtung wird später beim Deployment umgesetzt, NICHT jetzt

Health Contract

status, bootstrap_state, last_seen_commit, last_applied_commit, pending_commits, failed_commits, dead_commits, human_review_required, forgejo_status, tolaria_status, search_status, drift_count, last_success_at, last_error_code

C5A führt keine echten externen Health-Probes aus (rein State-Machine) — forgejo_status/tolaria_status/search_status sind UNKNOWN.


CLI

export C5A_DB=/path/to/c5a.db   # default: ./c5a.db
export C5_REPO=/path/to/forgejo-clone   # default: .
export C5_POLL_INTERVAL_SECONDS=60      # default: 60

# C5A
python3 rq_c5a_cli.py health
python3 rq_c5a_cli.py bootstrap-state
python3 rq_c5a_cli.py bootstrap-transition --to RECONCILING
python3 rq_c5a_cli.py set-baseline --commit <sha>
python3 rq_c5a_cli.py ingest-commit --json <file>
python3 rq_c5a_cli.py process-commit --json <file>
python3 rq_c5a_cli.py list-commits [--status <s>]
python3 rq_c5a_cli.py commit-status --sha <sha>
python3 rq_c5a_cli.py no-write-check

# C5B (rq_c5_cli.py)
python3 rq_c5_cli.py poll-once            # ein kontrollierter Poll-Zyklus (read-only)
python3 rq_c5_cli.py show-pending         # ausstehende Commits anzeigen
python3 rq_c5_cli.py show-commit <sha>    # Details eines Commits anzeigen
python3 rq_c5_cli.py dry-run-diff <sha>   # Diff eines Commits anzeigen (read-only)
python3 rq_c5_cli.py health               # Health-Contract anzeigen
python3 rq_c5_cli.py no-write-check       # No-Downstream-Write-Guarantee pruefen

Kein daemon/start-forever-Befehl in C5B — der produktive Poll-Daemon kommt erst beim Deployment (C5).


Tests

python3 test_c5a.py   # 25 PASS / 0 FAIL
python3 -m unittest test_c5b -v   # 39 PASS / 0 FAIL

C5A abgedeckte Fälle: normal commit lifecycle, multi-object commit, duplicate commit, already-applied, out-of-order commit, missing predecessor, retry progression, max retry → DEAD, human gate transition, restart/reload persistence, state corruption handling, idempotency, delete request → human gate, dangling relation → human gate, unexpected drift → human gate, no-write guarantee, bootstrap lifecycle, baseline-only-from-BASELINE_READY, reason codes closed set, operations closed set, health contract fields, last_applied-only-after-full-pass, retry available bound, backoff sequence, invalid reason code rejected.

C5B abgedeckte Fälle: head discovery, single commit, multi commit, added knowledge object, content update, metadata update, state update, tags update, relation update, rename, move, delete request, out-of-scope file, legacy object, unknown ID, secret detection, duplicate poll, lost poll, ordering, Forgejo unavailable, history divergence, C5A persistence integration, no downstream write, poll interval config.


Dateien

  • rq_c5a.py — C5A Kern-Library (Store + State Machine + No-Write-Check)
  • rq_c5a_cli.py — C5A CLI
  • test_c5a.py — C5A Testsuite (25 Tests)
  • rq_c5b.py — C5B Kern-Library (GitReader + Scope + Classifier + Poller + No-Downstream-Write-Check)
  • rq_c5_cli.py — C5 CLI (C5A + C5B: poll-once, show-pending, show-commit, dry-run-diff, health, no-write-check)
  • test_c5b.py — C5B Testsuite (39 Tests)
  • README.md — diese Datei