trading-system-docs/tolaria/c5-sync-service/README.md

207 lines
6.4 KiB
Markdown

# C5A — CONTRACT & STATE MACHINE v1
**Phase:** C5A (erste Phase des C5 Sync Service)
**Status:** DONE (Contract & State Machine) — **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. Es ist eine **deterministische, inaktive
Python-Library + CLI + Testsuite** — es führt **keine** externen Writes aus.
---
## NO-WRITE GUARANTEE (verbindlich)
C5A kann **NOCH NICHT**:
- Tolaria schreiben
- Search rebuilden
- Forgejo schreiben
- produktiv pollen
- Netzwerk-Mutationen ausführen
Es gibt **keine** Netzwerk-/HTTP-/Socket-/Subprocess-Mutationsfunktionen in C5A.
Die No-Write-Guarantee wird statisch per `assert_no_write_guarantee()` bewiesen
(keine `requests`/`urllib`/`http`/`socket`/`subprocess`-Imports) und durch den
Test `test_no_write_guarantee` abgesichert.
---
## State Machine
### 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
```bash
export C5A_DB=/path/to/c5a.db # default: ./c5a.db
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
```
---
## Tests
```bash
python3 test_c5a.py # 25 PASS / 0 FAIL
```
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.
---
## Dateien
- `rq_c5a.py` — Kern-Library (Store + State Machine + No-Write-Check)
- `rq_c5a_cli.py` — CLI
- `test_c5a.py` — Testsuite (25 Tests)
- `README.md` — diese Datei