trading-system-docs/tolaria/c5-sync-service/C5_DELETE_EXECUTION_ARCHITECTURE_DECISION.md
Red Queen 3db1c71b68 C5: Human-Gated DELETE Execution Contract (Todo 7-13)
- DeleteExecutor (rq_c5_delete.py): Pre-Gates, Read-Back, idempotenter replay
- TolariaClient.delete() (rq_c5c.py): kontrollierter DELETE, keine Probes
- Approval-Store + Reason Codes RC_DELETE_APPROVAL_MISSING/MISMATCH (rq_c5a.py)
- CLI: c5-delete-approve/execute/replay/status (rq_c5_cli.py)
- C5E: recover()/replay() DELETE-Integration
- C5D: verify_integrity prueft secret_blocked_objects (FAIL CLOSED)
- Security: Path-Traversal-Block in _normalize_vault_path
- Drift nach DELETE -> FAIL CLOSED zurueck zu HUMAN_REVIEW_REQUIRED
- Tests: test_c5_delete (19), test_c5_delete_integration (22),
  test_c5_delete_fresh_checker (17) — alle gruen
- ADR: C5_DELETE_EXECUTION_ARCHITECTURE_DECISION.md (ACCEPTED)
2026-08-26 19:22:44 +00:00

14 KiB
Raw Blame History

C5 DELETE EXECUTION CONTRACT — ARCHITECTURE DECISION RECORD

Status: ACCEPTED (Christian, OPTION 1 — CONTRACT-ERWEITERUNG, 2026-08-26) Scope: C5 Sync Service — kontrollierter Tolaria-DELETE-Pfad NACH expliziter Human-Freigabe Datei: tolaria/c5-sync-service/C5_DELETE_EXECUTION_ARCHITECTURE_DECISION.md


1. Ausgangslage / DELETE_EXECUTION_CONTRACT_GAP

STRICT PRE-FLIGHT (read-only) hat den bestehenden DELETE-Contract vollständig analysiert (rq_c5a.py, rq_c5b.py, rq_c5c.py, rq_c5d.py, rq_c5e.py, rq_c5_cli.py, C5AStore, TolariaClient, Tests C5AC5E). Ergebnis:

Owner Verantwortlich Status
DELETE_DETECTION_OWNER C5B ChangeClassifier.classify() (Z.520523): path_before && !path_afterOP_DELETE_REQUEST vorhanden
DELETE_HUMAN_GATE_OWNER C5A SyncStateMachine.process_commit() (Z.794797) + C5C _propagate_object() (Z.492496) → ST_HUMAN_REVIEW_REQUIRED + RC_AMBIGUOUS_DELETE vorhanden (terminal)
DELETE_APPROVAL_STORAGE fehlt — C5AStore hat keine Approval-Repräsentation (nur meta, commits, objects, bootstrap, health) GAP
DELETE_EXECUTION_OWNER fehltTolariaClient hat keine delete(); kein Executor GAP
DELETE_VERIFICATION_OWNER fehlt für DELETEverify() nur für Writes (Read-Back+Hash); kein DELETE-absent-Read-Back GAP
DELETE_REPLAY_OWNER C5E recover()/replay(): ST_HUMAN_REVIEW_REQUIREDREC_HUMAN_REVIEW (kein Executor) GAP
DELETE_APPLIED_OWNER C5D apply_commit()ST_APPLIED+mark_applied(), startet nur aus ST_UPDATING_SEARCH — DELETE-Commit erreicht das nie GAP

Kernbefund: DELETE wird erkannt und zum Human Gate geführt, aber das Gate ist terminal. Es existiert kein Approval-Storage, kein Executor, kein DELETE-Read-Back, kein Replay- und kein APPLIED-Pfad. Dies ist der DELETE_EXECUTION_CONTRACT_GAP, der durch diese Mission sauber geschlossen wird.


2. ZIELARCHITEKTUR (verbindlich)

DELETE erkannt (C5B)
  → HUMAN_REVIEW_REQUIRED (C5A/C5C, RC_AMBIGUOUS_DELETE)   [bleibt erhalten]
  → explizite Human Approval (CLI approve-delete)          [NEU]
  → Approval persistent/auditierbar (C5AStore)              [NEU]
  → kontrollierter Tolaria DELETE (DeleteExecutor)          [NEU]
  → Read-Back-Verifikation (target absent)                  [NEU]
  → C5D Source Build (bestehender Pfad)                     [wiederverwendet]
  → Search Update (bestehender C5D-Pfad)                    [wiederverwendet]
  → Exact-Set-Verifikation (C5D verify_integrity)           [wiederverwendet]
  → APPLIED (C5D mark_applied)                               [wiederverwendet]

KEIN automatischer DELETE. Der bestehende Sicherheitsmechanismus OP_DELETE_REQUEST → HUMAN_REVIEW_REQUIRED → RC_AMBIGUOUS_DELETE bleibt unverändert.


3. Zwingende Invarianten (AN) und ihre Umsetzung

Invariante Umsetzung
A) DELETE bleibt HUMAN_GATED Kein Pfad führt einen DELETE ohne persistierte, explizite Human-Approval aus. _HUMAN_GATE_OPS unverändert.
B) Kein LLM/Agent entscheidet, dass DELETE genehmigt ist Approval wird AUSSCHLIESSLICH durch den CLI-Befehl approve-delete erzeugt (Human Gate). Kein Code erzeugt Approval automatisch.
C) Approval stammt explizit vom Human Gate approve-delete persistiert approved_by (Human-Gate-Provenance) + approval_nonce.
D) Approval commit- und objectchange-spezifisch Approval bindet exakt (workflow_commit, object_change_id, object_id, path). Keine globale DELETE_APPROVED=true-Semantik.
E) Approval für Objekt A nie Objekt B Executor validiert object_id + path gegen die Approval. Abweichung → FAIL CLOSED.
F) Approval für Commit X nie DELETE aus Commit Y Approval bindet workflow_commit. Executor validiert Commit-SHA.
G) Approval persistent und crash-/restart-fest SQLite-Tabelle delete_approvals in C5AStore (atomic, WAL, restart-safe).
H) Execution idempotent Pre-Delete-Drift-Check + Read-Back. Wenn Ziel bereits absent → DELETE_ALREADY_AT_TARGET, kein zweiter destruktiver Write.
I) DELETE nur exakt freigegebenes Objekt Executor nimmt path/object_id ausschließlich aus dem ObjectChange, das durch die Approval gebunden ist.
J) Kein Pfad nimmt beliebige freie Paths/IDs ohne ObjectChange+Approval Executor verlangt existierendes ObjectChange (operation==DELETE) + exakt passende Approval. Kein freier Pfad-Parameter.
K) Tolaria DELETE vor und nach Execution verifiziert Pre-Delete-Drift-Check (Objekt existiert noch) + Post-Read-Back (target absent + kein anderes Objekt verändert).
L) Keine manuelle SQLite-/SQL-State-Manipulation Nur Store-Methoden. Kein direktes SQL im Executor.
M) Keine direkte APPLIED-Markierung APPLIED nur durch C5D apply_commit() nach vollem Search-PASS. Executor markiert NIE APPLIED.
N) Keine automatische Human-Gate-Deaktivierung _HUMAN_GATE_OPS unverändert; DELETE bleibt Gate-pflichtig.

4. APPROVAL CONTRACT

Prüfung bestehender State: C5AStore besitzt KEINE geeignete persistente Approval-Repräsentation (nur meta-KV für source_provenance, commits, objects, bootstrap, health). → Neuer minimaler auditierbarer Approval-State.

4.1 Schema (neue Tabelle delete_approvals in C5AStore)

CREATE TABLE IF NOT EXISTS delete_approvals (
    approval_id      TEXT PRIMARY KEY,          -- UUID, eindeutig
    workflow_commit  TEXT NOT NULL,            -- Commit-SHA (Invariante F)
    object_change_id INTEGER NOT NULL,         -- FK auf objects.id (Invariante D/E)
    object_id        TEXT NOT NULL,            -- exakt freigegebenes Objekt (E/I)
    path             TEXT NOT NULL,            -- exakter Vault-Pfad (E/I)
    operation        TEXT NOT NULL DEFAULT 'DELETE',
    approval_status  TEXT NOT NULL,            -- APPROVED | USED | REVOKED
    approved_by      TEXT NOT NULL,            -- Human-Gate-Provenance (C)
    approval_nonce   TEXT NOT NULL,            -- eindeutig, verhindert Reuse
    approved_at      INTEGER NOT NULL,
    used_at          INTEGER,
    UNIQUE(workflow_commit, object_change_id, object_id, path)
)

Keine Credentials, keine personenbezogenen Daten. approved_by = Human-Gate-Identifier (z.B. human:christian), kein Secret.

4.2 Keying

Approval ist eindeutig gebunden an (workflow_commit, object_change_id, object_id, path). Keine globale DELETE_APPROVED=true-Semantik. Ein Approval autorisiert GENAU EINEN ObjectChange in GENAU EINEM Commit.

4.3 CLI-Modell (kein undifferenzierter approve+delete-Blackbox)

Bevorzugtes Modell (Mission §3):

  1. c5-delete approve --commit <sha> --object-id <id> --path <path> → persistiert Approval (NUR Approval, KEIN DELETE)
  2. c5-delete execute --commit <sha> → führt DELETE aus (verlangt persistierte Approval)
  3. c5-delete status --commit <sha> → read-only Status
  4. c5-delete replay --commit <sha> → idempotenter Replay nach Crash

Der Human-Gate-Beweis (persistierte Approval) geht dadurch NIE verloren.


5. TOLARIA DELETE CLIENT

TolariaClient.delete(vault_path) — minimal, isoliert:

def delete(self, vault_path: str) -> Dict[str, Any]:
    """Loescht ein Vault-Objekt (POST /delete). NUR DeleteExecutor darf rufen."""
    resp = self._post("delete", {"path": vault_path})
    if resp is None:
        resp = {}
    if "error" in resp:
        raise TolariaWriteError(f"Tolaria delete fehlgeschlagen: {resp['error']}",
                                RC_INVALID_SCHEMA)
    return resp

Endpoint-/Payload-Contract wird ausschließlich aus dem vorhandenen Tolaria-Code/Router und der dokumentierten Incident-Evidence bestimmt (POST /api/vault/delete mit {"path": ...}). KEINE produktive Endpoint-Probe. Die Incident-Regel gilt verbindlich: NO MUTATING HTTP METHOD PROBES DURING PRODUCTION READ-ONLY DISCOVERY.


6. DELETE EXECUTION (DeleteExecutor)

6.1 Pre-Delete-Gates (alle MÜSSEN passen, sonst FAIL CLOSED)

  1. ObjectChange existiert (get_object_change)
  2. operation == DELETE
  3. Commit-Status == ST_DELETE_APPROVED (definierter approved state)
  4. Approval existiert, approval_status == APPROVED, gehört exakt zu diesem ObjectChange
  5. object_id stimmt (Approval ↔ ObjectChange)
  6. path stimmt (Approval ↔ ObjectChange)
  7. Pre-Delete-Drift-Check: Tolaria enthält das Objekt noch (erwarteter aktueller Zustand)

Bei irgendeiner Abweichung → FAIL CLOSED (kein DELETE, kein State-Übergang).

6.2 Execution

  • client.delete(vault_path) (nur DeleteExecutor)
  • Read-Back: client.read(vault_path) is None → target absent == TRUE
  • Zusätzlich: client.list() — kein anderes Objekt verändert (Set-Vergleich vor/nach)

6.3 Nach erfolgreichem DELETE

  • Approval → USED (einmalig, verhindert Reuse)
  • Commit → ST_UPDATING_SEARCH (C5D übernimmt Search-Pfad)

7. IDEMPOTENZ / CRASH-RECOVERY (Fälle AF)

Fall Zustand Deterministischer Replay-Pfad
A) Crash vor Approval ST_HUMAN_REVIEW_REQUIRED, keine Approval Nichts zu tun. Kein DELETE. Replay: bleibt Human Gate.
B) Crash nach Approval, vor DELETE ST_DELETE_APPROVED, Approval persistiert Replay: Executor erkennt Approval, führt DELETE aus.
C) Crash während DELETE ST_DELETING Replay: Read-Back. Objekt noch da → DELETE erneut (Approval gültig). Objekt absent → DELETE_ALREADY_AT_TARGET, weiter zu ST_UPDATING_SEARCH.
D) Crash nach DELETE, vor Read-Back ST_DELETING Replay: Read-Back → absent → weiter zu ST_UPDATING_SEARCH.
E) Crash nach Read-Back, vor Search Update ST_UPDATING_SEARCH C5D apply_commit() übernimmt (bestehender Pfad).
F) Crash nach Search Update, vor APPLIED ST_VERIFYING_SEARCH C5D apply_commit() übernimmt (bestehender Pfad).

DELETE_ALREADY_AT_TARGET: Wenn Tolaria das freigegebene Objekt bereits nicht mehr enthält, erkennt der Executor anhand Approval + erwarteter Identität + Read-Back DELETE_ALREADY_AT_TARGET statt blind erneut zu mutieren.


8. SEARCH CONTRACT

Nach erfolgreichem Tolaria-DELETE wird Search NICHT über einen statischen/improvisierten Pfad aktualisiert. Der bestehende C5D-Pfad wird wiederverwendet:

Tolaria current verified state (ohne gelöschtes Objekt)
  → SearchSourceBuilder.build() (read-only, deterministisch)
  → atomic source + Provenance-Persistenz (persist_search_source_provenance)
  → Search-Rebuild (search.rebuild)
  → Exact-Set-Verifikation (verify_integrity: expected IDs/Paths exakt)
  → APPLIED (mark_applied)

Kein APPLIED bei Search-Mismatch (C5D _handle_failure → Human Gate / DEAD).


9. STATE MACHINE CHANGES

Neue Zustände (minimal, nur für den DELETE-Execution-Pfad):

  • ST_DELETE_APPROVED = "DELETE_APPROVED" — nach Approval, vor Execution
  • ST_DELETING = "DELETING" — während Execution (Crash-Window)

Neue erlaubte Transitionen (NUR durch DeleteExecutor/Approval auslösbar, nie automatisch):

(ST_HUMAN_REVIEW_REQUIRED, ST_DELETE_APPROVED)   # nur via expliziter Approval
(ST_DELETE_APPROVED,       ST_DELETING)          # Executor startet
(ST_DELETING,              ST_UPDATING_SEARCH)   # nach DELETE + Read-Back → C5D
(ST_DELETING,              ST_HUMAN_REVIEW_REQUIRED)  # Fehler → zurück zum Gate
(ST_DELETE_APPROVED,       ST_HUMAN_REVIEW_REQUIRED)  # Revoke/Fehler

Keine Änderung an _HUMAN_GATE_OPS, RC_AMBIGUOUS_DELETE, oder den bestehenden Transitions. Der bestehende OP_DELETE_REQUEST → HUMAN_REVIEW_REQUIRED-Pfad bleibt unverändert.


10. CLI CHANGES

Neue Befehle in rq_c5_cli.py:

  • c5-delete approve --commit <sha> --object-id <id> --path <path> → persistiert Approval (NUR Approval)
  • c5-delete execute --commit <sha> → führt DELETE aus (verlangt persistierte Approval)
  • c5-delete status --commit <sha> → read-only Status
  • c5-delete replay --commit <sha> → idempotenter Replay nach Crash

Kein undifferenzierter approve+delete-Blackbox-Befehl.


11. SECURITY REVIEW (Design-Zusicherungen)

  • Keine arbitrary path deletion: Executor nimmt path nur aus dem ObjectChange, das durch Approval gebunden ist.
  • Keine path traversal: path wird gegen VAULT_PREFIX validiert.
  • Keine ungeprüften freien Delete-Targets: Kein freier Pfad-Parameter im Executor.
  • Keine Approval-Reuse: approval_id eindeutig; nach Nutzung → USED.
  • Keine Cross-Commit-Approval: Approval bindet workflow_commit.
  • Keine Cross-Object-Approval: Approval bindet object_id + path.
  • Keine automatische Approval-Eskalation: Nur expliziter CLI-Befehl erzeugt Approval.
  • Keine Secrets: Approval speichert keine Credentials/personenbezogenen Daten.
  • Keine Credential-Ausgabe: Kein Secret im Approval-/Status-Output.
  • Keine manuelle DB-Manipulation: Nur Store-Methoden.
  • Keine allgemeine DELETE-Automatisierung: Kein Pfad führt DELETE ohne Approval.

12. TESTPLAN (mindestens)

  • DELETE ohne Human Approval → BLOCKED
  • DELETE mit Approval für anderes Objekt → BLOCKED
  • DELETE mit Approval für anderen Commit → BLOCKED
  • manipulierte object_id → BLOCKED
  • manipulierter path → BLOCKED
  • gültiges Approval → exakt ein DELETE
  • anderes Objekt bleibt unverändert
  • DELETE_ALREADY_AT_TARGET → kein zweiter destruktiver Write
  • Restart nach Approval → Approval erhalten
  • Crash nach DELETE → Replay sicher
  • Search Exact-Set nach DELETE
  • Search-Mismatch → kein APPLIED
  • Secret Safety
  • keine SQL-/State-Bypässe
  • bestehende WRITE-/UPDATE-Flows ohne Regression
  • HUMAN_REVIEW_REQUIRED bleibt Default für neue DELETE requests
  • keine automatische Approval-Erzeugung
  • Multi-Commit-Test: Approval A darf DELETE B niemals autorisieren

13. INCIDENT LESSON (verbindlich, bleibt)

INCIDENT_OCCURRED = TRUE
INCIDENT_RECOVERED = TRUE
PERMANENT_DAMAGE = NONE VERIFIED
ROOT_CAUSE = unsafe mutating endpoint probe during read-only discovery

Neue verbindliche Regel: NO MUTATING HTTP METHOD PROBES DURING PRODUCTION READ-ONLY DISCOVERY. Diese Regel darf durch die neue DELETE-Implementierung NICHT aufgeweicht werden.


14. HARD STOP

Nach erfolgreicher Implementation, Tests, Push und Fresh Checker: STOPP. KEIN realer Canary-Delete. KEIN produktiver Search-Rebuild. KEIN C5F-Cleanup. KEIN C5G. KEINE Netzwerkänderung. KEINE Hermes-Rechte. KEINE Hermes-Autonomie. Christian erteilt danach separat die Freigabe zur produktiven Canary-Cleanup-Validierung.