Przejdź do treści

Kontrakty między komponentami

Dlaczego kontrakty są rdzeniem

NORBI łączy elementy deterministyczne z modelami niedeterministycznymi. Bez ścisłych kontraktów odpowiedź modelu staje się ukrytą komendą, a błąd jednego adaptera może rozlać się na cały system. Kontrakt określa nie tylko pola JSON, lecz także semantykę, źródło czasu, idempotency, limity, uprawnienia, stany, błędy i dowody.

Wspólna koperta

Każdy obiekt protokołu zawiera co najmniej rodzinę i wersję schematu, stabilne ID, correlation ID, project ID, actor/principal ID, autorytatywny czas brokera, klasyfikację, hash kanonicznej treści i wersję polityki. Schemat odrzuca nieznane pola, duplicate keys, niekanoniczne identyfikatory i nieprawidłowe zakresy. Dla obiektów bezpieczeństwowych odbiorca odrzuca każde nieznane pole.

Główne kontrakty

IntentEnvelope

Przenosi oryginalne polecenie, kanał, użytkownika, załączniki i ograniczenia. Nie ma pól capability ani approval. Tekst pozostaje danymi.

ProposalEnvelope

Zawiera sugerowany rezultat, plan, wywołania narzędzi i uzasadnienie. Wszystkie deklaracje typu „approved” lub „safe” są ignorowane jako opinia providera. Broker sam oblicza policy.

ContextEnvelope

Lista elementów kontekstu z provenance, classification, trust label, token budget i allowed use. Provider otrzymuje snapshot hash; nie ma ogólnego dostępu do pamięci.

JobRecord i WorkflowRun

Job wiąże dokładnie jeden tool/provider/transform, attempt, lease, payload, artifact ports, resource request, timeout, idempotency key i state. Workflow definiuje DAG, ale nie może przeskoczyć walidacji jobów.

ApprovalRequest, ApprovalDecision, ExecutionGrant

Request prezentuje pełne skutki. Decision pochodzi wyłącznie z kanału operatora i wiąże context hash. Grant jest krótkotrwały, jednorazowy i obejmuje exact job/tool/version/payload/paths/accounts/destinations/artifacts/capabilities/policy/nonce/expiry. Grant nie jest dostępny providerowi.

ToolManifest i ToolInvocation

Manifest deklaruje schema wejścia/wyjścia, capability, effects, allowed roots, network destinations, accounts, resource profile, timeout, idempotency, validators i failure semantics. Invocation nie może rozszerzać manifestu.

ExecutionReceipt

Zawiera stan próby, attempt, tool version, input/output hashes, observed effects, ograniczone dowody, czas, resource use i reconciliation hints. Nie zawiera sekretów ani pełnych ciał komunikacji, jeśli wystarczają hashe i klasyfikowane artefakty.

ArtifactMetadata i MemoryRecord

Artifact opisuje blob i lineage. MemoryRecord opisuje twierdzenie, źródło i ważność. Ani etykieta approved, ani confidence nie nadają prawa publikacji lub wykonania.

WorkerAdvertisement, ResourceRequest, WorkerLease

Advertisement mówi, co worker potrafi; nie jest obietnicą zaufania. ResourceRequest prosi o sprzęt; nie jest capability. WorkerLease jest ograniczony do jednego joba/attempt i nie może być delegowany.

Kontrakt błędów

Stabilne prefiksy błędów to validation.*, policy.*, approval.*, lease.*, resource.*, tool.*, artifact.*, provider.*, external.* i recovery.*. Komunikat użytkownika może być prosty, ale audit zachowuje kod i dowody. Błąd nie zawiera surowego sekretu, ścieżki credential store ani odpowiedzi z nagłówkami autoryzacyjnymi.

Przykład kontraktu narzędzia

{
  "tool_id": "document.render_pdf",
  "version": "2.1.0",
  "capability": "create_candidate_artifact",
  "input": {
    "source_artifact_id": "art_...",
    "profile": "print-a4",
    "output_stage": "candidate"
  },
  "effects": ["artifact.create"],
  "idempotency": "pure-by-input-hash",
  "network": "denied",
  "validators": ["pdf.structure", "pdf.page-render", "pdf.no-secret-marker"]
}

To narzędzie nie przyjmuje dowolnej ścieżki wyjściowej, komendy programu ani flag publikacji. Jeśli potrzebny jest nowy profil, powstaje wersjonowana konfiguracja; nie jest przemycany jako surowe argumenty.

Kompatybilność i migracje

Consumer deklaruje obsługiwane wersje. Broker negocjuje tylko kompatybilny zakres i zapisuje wybraną wersję w jobie. Migrator jest oddzielnym, testowanym adapterem. Nie mutuje historycznych eventów; tworzy nową reprezentację z linkiem do źródła. Approval wystawiony dla starego payloadu nie działa po migracji.

Walidacja kontraktów

Każdy kontrakt ma testy pozytywne, negatywne, graniczne i property-based tam, gdzie to przydatne. Testuje się unknown fields, wartości skrajne, Unicode, duże payloady, replay, zmianę hasha, concurrent consume, timeout oraz redakcję. Dla adaptera sprawdza się, że obserwowany efekt nie przekracza deklaracji. Test samego tekstu źródłowego nie jest dowodem bezpieczeństwa.

Ryzyka i scenariusz awaryjny

Najgroźniejsze są „prawie kompatybilne” interpretacje: inne znaczenie pustej listy, lokalny czas zamiast UTC, różny canonical JSON, albo null rozumiane jako brak ograniczenia. Reguła brzmi: puste lub pominięte scope oznacza odmowę, czas bezpieczeństwowy pochodzi od brokera, a hash używa jednej kanonizacji i domain separation.

Jeśli komponent nie rozpoznaje wersji albo nie potrafi zwalidować kontraktu, odmawia wykonania i zapisuje validation.unsupported_version. Nie próbuje heurystycznie naprawiać bezpieczeństwowo istotnego payloadu. Operator może uruchomić jawny migrator albo wybrać zgodną wersję komponentu.