diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index cf956f5..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: ci - -on: - push: - branches: [main] - pull_request: - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-go@v5 - with: - go-version: '1.22' - - - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - - name: Install schema validation dependencies - run: pip install --quiet jsonschema pyyaml - - # The spec-derived fixtures are validated first: if the specification and - # the schemas have diverged, nothing downstream is worth building. - - name: Validate schemas against spec fixtures - run: make validate - - # Generated types must match schemas/. A stale internal/contract means a - # schema change landed without regeneration. - - name: Check generated code is current - run: make check-generated - - - name: Build - run: make build - - - name: Vet - run: make vet - - - name: Test - run: make test diff --git a/Makefile b/Makefile deleted file mode 100644 index 5d445bb..0000000 --- a/Makefile +++ /dev/null @@ -1,54 +0,0 @@ -# FLUID core — build, generation and conformance. -# -# The record types in internal/contract are generated from schemas/. Never edit -# them by hand: run `make generate`. - -GO ?= go -PYTHON ?= python3 -PKGS := ./... - -.PHONY: all -all: generate build vet test validate - -.PHONY: generate -generate: ## Regenerate Go types from schemas/ - $(GO) run ./tools/schemagen - gofmt -w internal/contract - -.PHONY: build -build: - $(GO) build $(PKGS) - -.PHONY: vet -vet: - $(GO) vet $(PKGS) - -.PHONY: test -test: validate - $(GO) test $(PKGS) - -.PHONY: validate -validate: ## Validate spec-derived fixtures against the schemas - $(PYTHON) conformance/validate_schemas.py - -.PHONY: check-generated -check-generated: ## Fail if generated code is stale relative to schemas/ - @$(MAKE) --no-print-directory generate - @if ! git diff --quiet -- internal/contract; then \ - echo "internal/contract is stale — run 'make generate' and commit the result"; \ - git --no-pager diff --stat -- internal/contract; \ - exit 1; \ - fi - -.PHONY: conformance -conformance: ## Full conformance suite (grows through FLUID-WP-0007) - $(GO) test ./conformance/... $(PKGS) - -.PHONY: clean -clean: - rm -rf bin internal/contract/*.broken - -.PHONY: help -help: - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \ - awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-18s\033[0m %s\n", $$1, $$2}' diff --git a/WORK-RECORDS.md b/WORK-RECORDS.md deleted file mode 100644 index 5326a58..0000000 --- a/WORK-RECORDS.md +++ /dev/null @@ -1,77 +0,0 @@ -# Work Records — fluid-core - -> Generated by `statehub fix-consistency` (CUST-WP-0061-T04, work-record -> stage 3). Do not edit by hand — edit the source file/block listed for -> each record and re-run fix-consistency to refresh this index. Archived -> workplans are omitted; closed decisions/intakes/engagements stay listed -> so recently-resolved work is still visible. [auto] - -| Kind | ID | Status | Lane | Source | -| --- | --- | --- | --- | --- | -| workplan | FLUID-WP-0001 | ready | — | workplans/FLUID-WP-0001-statehub-bootstrap.md | -| workplan | FLUID-WP-0002 | active | — | workplans/FLUID-WP-0002-foundations-and-wire-contract.md | -| workplan | FLUID-WP-0003 | active | — | workplans/FLUID-WP-0003-deterministic-data-plane.md | -| workplan | FLUID-WP-0004 | active | — | workplans/FLUID-WP-0004-publication-and-control-apis.md | -| workplan | FLUID-WP-0005 | active | — | workplans/FLUID-WP-0005-fluid-insight.md | -| workplan | FLUID-WP-0006 | active | — | workplans/FLUID-WP-0006-fluid-science.md | -| workplan | FLUID-WP-0007 | active | — | workplans/FLUID-WP-0007-conformance-and-self-validation.md | -| workplan | FLUID-WP-0008 | active | — | workplans/FLUID-WP-0008-fluid-telegram-handover.md | -| task | FLUID-WP-0001-T01 | todo | — | workplans/FLUID-WP-0001-statehub-bootstrap.md | -| task | FLUID-WP-0001-T02 | todo | — | workplans/FLUID-WP-0001-statehub-bootstrap.md | -| task | FLUID-WP-0001-T03 | todo | — | workplans/FLUID-WP-0001-statehub-bootstrap.md | -| task | FLUID-WP-0002-T01 | done | — | workplans/FLUID-WP-0002-foundations-and-wire-contract.md | -| task | FLUID-WP-0002-T02 | todo | — | workplans/FLUID-WP-0002-foundations-and-wire-contract.md | -| task | FLUID-WP-0002-T03 | todo | — | workplans/FLUID-WP-0002-foundations-and-wire-contract.md | -| task | FLUID-WP-0002-T04 | todo | — | workplans/FLUID-WP-0002-foundations-and-wire-contract.md | -| task | FLUID-WP-0002-T05 | todo | — | workplans/FLUID-WP-0002-foundations-and-wire-contract.md | -| task | FLUID-WP-0002-T06 | todo | — | workplans/FLUID-WP-0002-foundations-and-wire-contract.md | -| task | FLUID-WP-0002-T07 | todo | — | workplans/FLUID-WP-0002-foundations-and-wire-contract.md | -| task | FLUID-WP-0003-T01 | todo | — | workplans/FLUID-WP-0003-deterministic-data-plane.md | -| task | FLUID-WP-0003-T02 | todo | — | workplans/FLUID-WP-0003-deterministic-data-plane.md | -| task | FLUID-WP-0003-T03 | todo | — | workplans/FLUID-WP-0003-deterministic-data-plane.md | -| task | FLUID-WP-0003-T04 | todo | — | workplans/FLUID-WP-0003-deterministic-data-plane.md | -| task | FLUID-WP-0003-T05 | todo | — | workplans/FLUID-WP-0003-deterministic-data-plane.md | -| task | FLUID-WP-0003-T06 | todo | — | workplans/FLUID-WP-0003-deterministic-data-plane.md | -| task | FLUID-WP-0003-T07 | todo | — | workplans/FLUID-WP-0003-deterministic-data-plane.md | -| task | FLUID-WP-0003-T08 | todo | — | workplans/FLUID-WP-0003-deterministic-data-plane.md | -| task | FLUID-WP-0003-T09 | todo | — | workplans/FLUID-WP-0003-deterministic-data-plane.md | -| task | FLUID-WP-0003-T10 | todo | — | workplans/FLUID-WP-0003-deterministic-data-plane.md | -| task | FLUID-WP-0004-T01 | todo | — | workplans/FLUID-WP-0004-publication-and-control-apis.md | -| task | FLUID-WP-0004-T02 | todo | — | workplans/FLUID-WP-0004-publication-and-control-apis.md | -| task | FLUID-WP-0004-T03 | todo | — | workplans/FLUID-WP-0004-publication-and-control-apis.md | -| task | FLUID-WP-0004-T04 | todo | — | workplans/FLUID-WP-0004-publication-and-control-apis.md | -| task | FLUID-WP-0004-T05 | todo | — | workplans/FLUID-WP-0004-publication-and-control-apis.md | -| task | FLUID-WP-0004-T06 | todo | — | workplans/FLUID-WP-0004-publication-and-control-apis.md | -| task | FLUID-WP-0005-T01 | todo | — | workplans/FLUID-WP-0005-fluid-insight.md | -| task | FLUID-WP-0005-T02 | todo | — | workplans/FLUID-WP-0005-fluid-insight.md | -| task | FLUID-WP-0005-T03 | todo | — | workplans/FLUID-WP-0005-fluid-insight.md | -| task | FLUID-WP-0005-T04 | todo | — | workplans/FLUID-WP-0005-fluid-insight.md | -| task | FLUID-WP-0005-T05 | todo | — | workplans/FLUID-WP-0005-fluid-insight.md | -| task | FLUID-WP-0005-T06 | todo | — | workplans/FLUID-WP-0005-fluid-insight.md | -| task | FLUID-WP-0005-T07 | todo | — | workplans/FLUID-WP-0005-fluid-insight.md | -| task | FLUID-WP-0005-T08 | todo | — | workplans/FLUID-WP-0005-fluid-insight.md | -| task | FLUID-WP-0005-T09 | todo | — | workplans/FLUID-WP-0005-fluid-insight.md | -| task | FLUID-WP-0006-T01 | todo | — | workplans/FLUID-WP-0006-fluid-science.md | -| task | FLUID-WP-0006-T02 | todo | — | workplans/FLUID-WP-0006-fluid-science.md | -| task | FLUID-WP-0006-T03 | todo | — | workplans/FLUID-WP-0006-fluid-science.md | -| task | FLUID-WP-0006-T04 | todo | — | workplans/FLUID-WP-0006-fluid-science.md | -| task | FLUID-WP-0006-T05 | todo | — | workplans/FLUID-WP-0006-fluid-science.md | -| task | FLUID-WP-0006-T06 | todo | — | workplans/FLUID-WP-0006-fluid-science.md | -| task | FLUID-WP-0006-T07 | todo | — | workplans/FLUID-WP-0006-fluid-science.md | -| task | FLUID-WP-0006-T08 | todo | — | workplans/FLUID-WP-0006-fluid-science.md | -| task | FLUID-WP-0006-T09 | todo | — | workplans/FLUID-WP-0006-fluid-science.md | -| task | FLUID-WP-0007-T01 | todo | — | workplans/FLUID-WP-0007-conformance-and-self-validation.md | -| task | FLUID-WP-0007-T02 | todo | — | workplans/FLUID-WP-0007-conformance-and-self-validation.md | -| task | FLUID-WP-0007-T03 | todo | — | workplans/FLUID-WP-0007-conformance-and-self-validation.md | -| task | FLUID-WP-0007-T04 | todo | — | workplans/FLUID-WP-0007-conformance-and-self-validation.md | -| task | FLUID-WP-0007-T05 | todo | — | workplans/FLUID-WP-0007-conformance-and-self-validation.md | -| task | FLUID-WP-0007-T06 | todo | — | workplans/FLUID-WP-0007-conformance-and-self-validation.md | -| task | FLUID-WP-0008-T01 | todo | — | workplans/FLUID-WP-0008-fluid-telegram-handover.md | -| task | FLUID-WP-0008-T02 | todo | — | workplans/FLUID-WP-0008-fluid-telegram-handover.md | -| task | FLUID-WP-0008-T03 | todo | — | workplans/FLUID-WP-0008-fluid-telegram-handover.md | -| task | FLUID-WP-0008-T04 | todo | — | workplans/FLUID-WP-0008-fluid-telegram-handover.md | -| task | FLUID-WP-0008-T05 | todo | — | workplans/FLUID-WP-0008-fluid-telegram-handover.md | -| task | FLUID-WP-0008-T06 | todo | — | workplans/FLUID-WP-0008-fluid-telegram-handover.md | -| task | FLUID-WP-0008-T07 | todo | — | workplans/FLUID-WP-0008-fluid-telegram-handover.md | -| task | FLUID-WP-0008-T08 | todo | — | workplans/FLUID-WP-0008-fluid-telegram-handover.md | -| task | FLUID-WP-0008-T09 | todo | — | workplans/FLUID-WP-0008-fluid-telegram-handover.md | diff --git a/conformance/fixtures/README.md b/conformance/fixtures/README.md deleted file mode 100644 index 7e82221..0000000 --- a/conformance/fixtures/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# Schema fixtures - -Every file here is transcribed from a worked example in `spec/`. They exist so -that a change to the specification which is not reflected in `schemas/` fails -the build. - -Two deviations from the spec text are deliberate and are the only ones allowed: - -1. **Placeholder digests.** The spec writes `sha256:`. Fixtures use a - syntactically valid digest, because the schema constrains the format. -2. **Placeholder artifact paths.** The spec writes `contractRef: ...`. Fixtures - use a plausible reference. - -Anything else that differs from the spec is a bug in the fixture or in the -schema, not a licence to relax the schema. - -| Fixture | Source | -|---|---| -| `hypothesis.yaml` | `FluidHypothesisRevisionSchema.md` §3 | -| `revision.yaml` | `FluidHypothesisRevisionSchema.md` §8 | -| `experiment.yaml` | `FluidHypothesisRevisionSchema.md` §12 | -| `pressure.yaml` | `FluidHypothesisRevisionSchema.md` §13 | -| `backend-requirement.yaml` | `FluidHypothesisRevisionSchema.md` §14 | -| `event.yaml` | `FluidHypothesisRevisionSchema.md` §15 | -| `revision-descriptor.yaml` | `ArchitectureBlueprint.md` §36 | -| `routing-policy.yaml` | `ArchitectureBlueprint.md` §17 | -| `feedback.yaml` | `FluidAPIStandards.md` §15, `ArchitectureBlueprint.md` §38 | -| `telemetry-envelope.yaml` | `ArchitectureBlueprint.md` §6.1 | diff --git a/conformance/fixtures/backend-requirement.yaml b/conformance/fixtures/backend-requirement.yaml deleted file mode 100644 index 1df6b76..0000000 --- a/conformance/fixtures/backend-requirement.yaml +++ /dev/null @@ -1,37 +0,0 @@ -fluid_backend_requirement: - schema_version: "0.1" - - id: "BR-0041" - originating_interface: "customer-context-api" - originating_revision: "R-0081" - originating_hypothesis: "H-0212" - - backend_service: "payments-service" - - capability: - title: "Historical payment-state query" - description: > - Retrieve payment state as of a specified timestamp. - - semantics: - consistency: "snapshot" - required_fields: - - "payment_id" - - "state" - - "effective_at" - - quality: - p95_latency_ms: 250 - availability: 0.999 - - security: - authorization_scope: "payments.read" - tenant_isolation: "required" - - expected_usage: - requests_per_day: 120000 - - disposition: - state: "PLANNED" - reason: null - target_ref: "payments-service/roadmap#412" diff --git a/conformance/fixtures/event.yaml b/conformance/fixtures/event.yaml deleted file mode 100644 index 51e0c8e..0000000 --- a/conformance/fixtures/event.yaml +++ /dev/null @@ -1,24 +0,0 @@ -fluid_event: - schema_version: "0.1" - - id: "EV-990281" - occurred_at: "2026-09-09T11:31:02Z" - - entity_type: "revision" - entity_id: "R-000221" - - event_type: "PROMOTION_RECOMMENDED" - - actor: - type: "daimon" - id: "fluid-daimon/customer-finance-api" - - inputs: - - "H-000184" - - "E-000093" - - reason: > - Candidate met primary success target and violated no guardrails. - - evidence_refs: - - "metrics:E-000093/window-4" diff --git a/conformance/fixtures/experiment.yaml b/conformance/fixtures/experiment.yaml deleted file mode 100644 index 0de15a6..0000000 --- a/conformance/fixtures/experiment.yaml +++ /dev/null @@ -1,41 +0,0 @@ -fluid_experiment: - schema_version: "0.1" - - id: "E-000093" - interface_id: "customer-finance-api" - - hypothesis_refs: - - "H-000184" - - control_revision: "R-000220" - candidate_revisions: - - "R-000221" - - cohorts: - - "partner-integrations" - - "coding-agents" - - allocation: - control: 0.72 - candidate: 0.28 - - start_at: "2026-09-05T08:00:00Z" - planned_end_at: "2026-10-05T08:00:00Z" - - metrics: - primary: - - "requests_per_completed_task" - guardrails: - - "p95_latency_ms" - - "error_rate" - - "authorization_scope_change" - - stop_conditions: - - "hard_guardrail_violation" - - "security_failure" - - "manual_stop" - - result: - state: "RUNNING" - preferred_revision: null - evidence_refs: [] diff --git a/conformance/fixtures/feedback.yaml b/conformance/fixtures/feedback.yaml deleted file mode 100644 index 25986ca..0000000 --- a/conformance/fixtures/feedback.yaml +++ /dev/null @@ -1,22 +0,0 @@ -fluid_feedback: - schema_version: "0.1" - - id: "F-9821" - interface_id: "customer-finance-api" - received_at: "2026-08-28T09:12:00Z" - cohort: "coding-agents" - revision: "R-000220" - - goal: "produce monthly account statement" - missing_capability: "consolidated-period-summary" - attempted_operations: - - "list-transactions" - - "get-balances" - outcome: "capability unavailable" - workaround: - - "aggregate-locally" - - "infer-closing-balance" - confidence: 0.94 - impact: - calls: 18 - latency_ms: 2810 diff --git a/conformance/fixtures/hypothesis.yaml b/conformance/fixtures/hypothesis.yaml deleted file mode 100644 index aae29d4..0000000 --- a/conformance/fixtures/hypothesis.yaml +++ /dev/null @@ -1,156 +0,0 @@ -fluid_hypothesis: - schema_version: "0.1" - - id: "H-000184" - interface_id: "customer-finance-api" - - state: "PRIORITIZED" - - created_at: "2026-09-04T12:00:00Z" - created_by: - type: "daimon" - id: "fluid-daimon/customer-finance-api" - - title: "Provide direct latest-invoice capability" - - observation: - summary: > - A significant consumer cohort retrieves invoice collections, - sorts them by issue date, and discards all but the first item. - evidence_refs: - - "telemetry:invoice-pattern-2026w36" - - "feedback:F-9821" - affected_cohorts: - - "partner-integrations" - - "coding-agents" - observation_window: - start: "2026-08-15T00:00:00Z" - end: "2026-09-01T00:00:00Z" - - pressure: - classes: - - "successful_but_inefficient_usage" - - "repeated_expectation_mismatch" - severity: 0.61 - confidence: 0.88 - - explanation: - claim: > - Consumers treat "latest invoice" as a domain-level concept - and the existing generic collection interface does not present - that concept directly. - reach: - score: 0.42 - explains: - - "P-1831" - - "P-1834" - notes: > - Medium reach: explains several related request patterns - but not broader financial-context pressure. - - proposed_adaptation: - class: "contract" - summary: > - Add an explicit latest-invoice retrieval capability. - candidate_contract: - method: "GET" - path: "/customers/{id}/invoices/latest" - implementation_scope: "interface_only" - - backend_requirements: - required: false - requirement_refs: [] - - expected_outcomes: - - metric: "requests_per_completed_task" - cohort: "all" - baseline: 2.7 - target: 1.2 - direction: "lower" - - metric: "client_side_sort_workarounds" - cohort: "all" - baseline: 1.0 - target: 0.2 - direction: "lower" - - guardrails: - - metric: "p95_latency_ms" - operator: "<=" - threshold: 315 - - metric: "authorization_scope_change" - operator: "==" - threshold: false - - metric: "backend_query_count_delta" - operator: "<=" - threshold: 0 - - fitness_dimensions: - expected: - client_utility: 0.70 - correctness: 0.00 - reliability: 0.00 - performance: 0.10 - discoverability: 0.65 - simplicity: -0.15 - compatibility: 0.00 - security: 0.00 - maintainability: -0.05 - operational_cost: 0.00 - - complexity: - expected_delta: - surface_area: 1 - concept_count: 0 - operation_count: 1 - semantic_overlap: 0.1 - cognitive_load: -0.2 - score: 0.18 - - risk: - level: "LOW" - reasons: - - "read-only operation" - - "no authorization expansion" - - economics: - estimated_experiment_cost: 18.00 - estimated_implementation_cost: 40.00 - currency: "EUR" - expected_value_class: "MEDIUM" - - learning_value: - score: 0.56 - notes: > - Useful signal for whether first-class convenience resources - improve agent and partner integration behavior. - - priority: - score: 0.71 - decided_by: "optimization-policy-v3" - - success_criteria: - expression: > - requests_per_completed_task <= 1.2 - AND no hard guardrail violation - - failure_criteria: - expression: > - any hard guardrail violation - OR adoption below 5% after 30 days when equally discoverable - - candidate_revision_refs: - - "R-000221" - - experiment_refs: - - "E-000093" - - outcome: - status: null - summary: null - evidence_refs: [] - - audit: - decision_refs: - - "D-2811" - immutable_event_refs: - - "EV-990218" diff --git a/conformance/fixtures/json/backend-requirement.json b/conformance/fixtures/json/backend-requirement.json deleted file mode 100644 index 5c2478a..0000000 --- a/conformance/fixtures/json/backend-requirement.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "fluid_backend_requirement": { - "backend_service": "payments-service", - "capability": { - "description": "Retrieve payment state as of a specified timestamp.\n", - "title": "Historical payment-state query" - }, - "disposition": { - "reason": null, - "state": "PLANNED", - "target_ref": "payments-service/roadmap#412" - }, - "expected_usage": { - "requests_per_day": 120000 - }, - "id": "BR-0041", - "originating_hypothesis": "H-0212", - "originating_interface": "customer-context-api", - "originating_revision": "R-0081", - "quality": { - "availability": 0.999, - "p95_latency_ms": 250 - }, - "schema_version": "0.1", - "security": { - "authorization_scope": "payments.read", - "tenant_isolation": "required" - }, - "semantics": { - "consistency": "snapshot", - "required_fields": [ - "payment_id", - "state", - "effective_at" - ] - } - } -} diff --git a/conformance/fixtures/json/event.json b/conformance/fixtures/json/event.json deleted file mode 100644 index 0fbd04f..0000000 --- a/conformance/fixtures/json/event.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "fluid_event": { - "actor": { - "id": "fluid-daimon/customer-finance-api", - "type": "daimon" - }, - "entity_id": "R-000221", - "entity_type": "revision", - "event_type": "PROMOTION_RECOMMENDED", - "evidence_refs": [ - "metrics:E-000093/window-4" - ], - "id": "EV-990281", - "inputs": [ - "H-000184", - "E-000093" - ], - "occurred_at": "2026-09-09T11:31:02Z", - "reason": "Candidate met primary success target and violated no guardrails.\n", - "schema_version": "0.1" - } -} diff --git a/conformance/fixtures/json/experiment.json b/conformance/fixtures/json/experiment.json deleted file mode 100644 index ddd4919..0000000 --- a/conformance/fixtures/json/experiment.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "fluid_experiment": { - "allocation": { - "candidate": 0.28, - "control": 0.72 - }, - "candidate_revisions": [ - "R-000221" - ], - "cohorts": [ - "partner-integrations", - "coding-agents" - ], - "control_revision": "R-000220", - "hypothesis_refs": [ - "H-000184" - ], - "id": "E-000093", - "interface_id": "customer-finance-api", - "metrics": { - "guardrails": [ - "p95_latency_ms", - "error_rate", - "authorization_scope_change" - ], - "primary": [ - "requests_per_completed_task" - ] - }, - "planned_end_at": "2026-10-05T08:00:00Z", - "result": { - "evidence_refs": [], - "preferred_revision": null, - "state": "RUNNING" - }, - "schema_version": "0.1", - "start_at": "2026-09-05T08:00:00Z", - "stop_conditions": [ - "hard_guardrail_violation", - "security_failure", - "manual_stop" - ] - } -} diff --git a/conformance/fixtures/json/feedback.json b/conformance/fixtures/json/feedback.json deleted file mode 100644 index 2b3a2f4..0000000 --- a/conformance/fixtures/json/feedback.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "fluid_feedback": { - "attempted_operations": [ - "list-transactions", - "get-balances" - ], - "cohort": "coding-agents", - "confidence": 0.94, - "goal": "produce monthly account statement", - "id": "F-9821", - "impact": { - "calls": 18, - "latency_ms": 2810 - }, - "interface_id": "customer-finance-api", - "missing_capability": "consolidated-period-summary", - "outcome": "capability unavailable", - "received_at": "2026-08-28T09:12:00Z", - "revision": "R-000220", - "schema_version": "0.1", - "workaround": [ - "aggregate-locally", - "infer-closing-balance" - ] - } -} diff --git a/conformance/fixtures/json/hypothesis.json b/conformance/fixtures/json/hypothesis.json deleted file mode 100644 index 2d4826d..0000000 --- a/conformance/fixtures/json/hypothesis.json +++ /dev/null @@ -1,164 +0,0 @@ -{ - "fluid_hypothesis": { - "audit": { - "decision_refs": [ - "D-2811" - ], - "immutable_event_refs": [ - "EV-990218" - ] - }, - "backend_requirements": { - "required": false, - "requirement_refs": [] - }, - "candidate_revision_refs": [ - "R-000221" - ], - "complexity": { - "expected_delta": { - "cognitive_load": -0.2, - "concept_count": 0, - "operation_count": 1, - "semantic_overlap": 0.1, - "surface_area": 1 - }, - "score": 0.18 - }, - "created_at": "2026-09-04T12:00:00Z", - "created_by": { - "id": "fluid-daimon/customer-finance-api", - "type": "daimon" - }, - "economics": { - "currency": "EUR", - "estimated_experiment_cost": 18.0, - "estimated_implementation_cost": 40.0, - "expected_value_class": "MEDIUM" - }, - "expected_outcomes": [ - { - "baseline": 2.7, - "cohort": "all", - "direction": "lower", - "metric": "requests_per_completed_task", - "target": 1.2 - }, - { - "baseline": 1.0, - "cohort": "all", - "direction": "lower", - "metric": "client_side_sort_workarounds", - "target": 0.2 - } - ], - "experiment_refs": [ - "E-000093" - ], - "explanation": { - "claim": "Consumers treat \"latest invoice\" as a domain-level concept and the existing generic collection interface does not present that concept directly.\n", - "reach": { - "explains": [ - "P-1831", - "P-1834" - ], - "notes": "Medium reach: explains several related request patterns but not broader financial-context pressure.\n", - "score": 0.42 - } - }, - "failure_criteria": { - "expression": "any hard guardrail violation OR adoption below 5% after 30 days when equally discoverable\n" - }, - "fitness_dimensions": { - "expected": { - "client_utility": 0.7, - "compatibility": 0.0, - "correctness": 0.0, - "discoverability": 0.65, - "maintainability": -0.05, - "operational_cost": 0.0, - "performance": 0.1, - "reliability": 0.0, - "security": 0.0, - "simplicity": -0.15 - } - }, - "guardrails": [ - { - "metric": "p95_latency_ms", - "operator": "<=", - "threshold": 315 - }, - { - "metric": "authorization_scope_change", - "operator": "==", - "threshold": false - }, - { - "metric": "backend_query_count_delta", - "operator": "<=", - "threshold": 0 - } - ], - "id": "H-000184", - "interface_id": "customer-finance-api", - "learning_value": { - "notes": "Useful signal for whether first-class convenience resources improve agent and partner integration behavior.\n", - "score": 0.56 - }, - "observation": { - "affected_cohorts": [ - "partner-integrations", - "coding-agents" - ], - "evidence_refs": [ - "telemetry:invoice-pattern-2026w36", - "feedback:F-9821" - ], - "observation_window": { - "end": "2026-09-01T00:00:00Z", - "start": "2026-08-15T00:00:00Z" - }, - "summary": "A significant consumer cohort retrieves invoice collections, sorts them by issue date, and discards all but the first item.\n" - }, - "outcome": { - "evidence_refs": [], - "status": null, - "summary": null - }, - "pressure": { - "classes": [ - "successful_but_inefficient_usage", - "repeated_expectation_mismatch" - ], - "confidence": 0.88, - "severity": 0.61 - }, - "priority": { - "decided_by": "optimization-policy-v3", - "score": 0.71 - }, - "proposed_adaptation": { - "candidate_contract": { - "method": "GET", - "path": "/customers/{id}/invoices/latest" - }, - "class": "contract", - "implementation_scope": "interface_only", - "summary": "Add an explicit latest-invoice retrieval capability.\n" - }, - "risk": { - "level": "LOW", - "reasons": [ - "read-only operation", - "no authorization expansion" - ] - }, - "schema_version": "0.1", - "state": "PRIORITIZED", - "success_criteria": { - "expression": "requests_per_completed_task <= 1.2 AND no hard guardrail violation\n" - }, - "title": "Provide direct latest-invoice capability" - } -} diff --git a/conformance/fixtures/json/pressure.json b/conformance/fixtures/json/pressure.json deleted file mode 100644 index 9f90354..0000000 --- a/conformance/fixtures/json/pressure.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "fluid_pressure": { - "affected_cohorts": [ - "partner-integrations" - ], - "class": "successful_but_inefficient_usage", - "evidence_refs": [ - "telemetry:query-pattern-712" - ], - "first_seen": "2026-08-15T10:11:00Z", - "frequency": { - "independent_consumers": 83, - "observations": 1740 - }, - "id": "P-1831", - "interface_id": "customer-finance-api", - "last_seen": "2026-09-01T12:15:00Z", - "linked_hypotheses": [ - "H-000184" - ], - "schema_version": "0.1", - "status": "OPEN", - "summary": "Consumers repeatedly retrieve complete invoice collections to determine the latest invoice.\n" - } -} diff --git a/conformance/fixtures/json/revision-descriptor.json b/conformance/fixtures/json/revision-descriptor.json deleted file mode 100644 index 81b908c..0000000 --- a/conformance/fixtures/json/revision-descriptor.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "revision": { - "contract": { - "digest": "sha256:3b1f9c2e7a4d8b6051e2c9f0a7d4b83e15c6f2a9d0b7e4c1836a5f2d9c0b7e41", - "source": "artifact:openapi/customer-finance-api/r22", - "type": "openapi" - }, - "id": "R-000221", - "intent": { - "version": "IEI-7" - }, - "interface": "customer-finance-api", - "policy": { - "compatibility": "additive", - "policy_check": "passed", - "rollback_to": "R-000220", - "security_check": "passed" - }, - "routing": { - "eligible_cohorts": [ - "partner-integrations", - "coding-agents" - ], - "max_traffic_share": 0.3 - }, - "runtime": { - "digest": "sha256:3b1f9c2e7a4d8b6051e2c9f0a7d4b83e15c6f2a9d0b7e4c1836a5f2d9c0b7e41", - "image": "registry/fluid/customer-finance:R-42", - "timeout_ms": 5000, - "upstream": "http://adapter-r22.fluid-runtime.svc:8080" - }, - "schema_version": "0.1", - "state": "candidate" - } -} diff --git a/conformance/fixtures/json/revision.json b/conformance/fixtures/json/revision.json deleted file mode 100644 index 501a722..0000000 --- a/conformance/fixtures/json/revision.json +++ /dev/null @@ -1,150 +0,0 @@ -{ - "fluid_revision": { - "adaptation_classes": [ - "contract" - ], - "adoption": { - "active_consumers": 119, - "adoption_rate": 0.289, - "eligible_consumers": 412, - "retained_adoption_rate": null - }, - "audit": { - "immutable_event_refs": [ - "EV-990221", - "EV-990244", - "EV-990281" - ] - }, - "backend_requirements": [], - "compatibility": { - "breaking_changes": [], - "class": "BACKWARD_COMPATIBLE", - "compatibility_evidence_refs": [ - "test:compat-suite-775" - ], - "deprecations": [], - "supersedes": [] - }, - "complexity": { - "after": { - "operation_count": 40, - "surface_area": 42 - }, - "before": { - "operation_count": 39, - "surface_area": 41 - }, - "budget_status": "WITHIN_BUDGET", - "delta_score": 0.18 - }, - "contract": { - "artifact_ref": "artifact:openapi/customer-finance-api/r22", - "digest": "sha256:3b1f9c2e7a4d8b6051e2c9f0a7d4b83e15c6f2a9d0b7e4c1836a5f2d9c0b7e41", - "type": "openapi", - "version": "3.1" - }, - "created_at": "2026-09-04T13:10:00Z", - "created_by": { - "id": "fluid-daimon/customer-finance-api", - "type": "daimon" - }, - "deployment": { - "current_exposure": { - "cohorts": [ - "partner-integrations", - "coding-agents" - ], - "traffic_share": 0.28 - }, - "environments": [ - { - "name": "production", - "routing_policy_ref": "route-policy:rp-118", - "started_at": "2026-09-05T08:00:00Z" - } - ] - }, - "economics": { - "build_cost": 38.2, - "currency": "EUR", - "experiment_cost_to_date": 12.4 - }, - "fitness": { - "baseline_revision": "R-000220", - "measurement_window": { - "end": null, - "start": "2026-09-05T08:00:00Z" - }, - "metrics": { - "error_rate": { - "baseline": 0.008, - "current": 0.007 - }, - "p95_latency_ms": { - "baseline": 300, - "current": 302, - "guardrail": 315 - }, - "requests_per_completed_task": { - "baseline": 2.7, - "current": 1.15, - "target": 1.2 - } - } - }, - "id": "R-000221", - "implementation": { - "artifact_ref": "artifact:adapter/customer-finance-api/r22", - "build_ref": "build:8871", - "digest": "sha256:3b1f9c2e7a4d8b6051e2c9f0a7d4b83e15c6f2a9d0b7e4c1836a5f2d9c0b7e41", - "source_ref": "git:9f4c2ab" - }, - "interface_evolution_intent": { - "artifact_ref": "artifact:intent/customer-finance-api/IEI-7", - "version": "IEI-7" - }, - "interface_id": "customer-finance-api", - "originating_hypotheses": [ - "H-000184" - ], - "parent_revision": "R-000220", - "promotion": { - "authorized_at": null, - "authorized_by": null, - "eligible": true, - "recommendation_reason": "Target fitness reached with no guardrail violations.\n", - "recommended_state": "STABLE" - }, - "provenance": { - "decision_refs": [ - "D-2811" - ], - "experiment_refs": [ - "E-000093" - ], - "telemetry_refs": [ - "telemetry:invoice-pattern-2026w36" - ] - }, - "revision_number": 22, - "rollback": { - "procedure_ref": "runbook:rollback-r22", - "supported": true, - "target_revision": "R-000220" - }, - "schema_version": "0.1", - "state": "CANDIDATE", - "verification": { - "policy_check": "PASSED", - "security_check": "PASSED", - "status": "PASSED", - "test_refs": [ - "test:unit-12081", - "test:contract-8821", - "test:security-610", - "test:compat-suite-775" - ] - } - } -} diff --git a/conformance/fixtures/json/routing-policy.json b/conformance/fixtures/json/routing-policy.json deleted file mode 100644 index 91e71e2..0000000 --- a/conformance/fixtures/json/routing-policy.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "routing_policy": { - "default_revision": "R-000220", - "generation": 118, - "id": "rp-118", - "interface": "customer-finance-api", - "issued_at": "2026-09-05T08:00:00Z", - "issued_by": { - "id": "fluid-experiment-controller", - "type": "system" - }, - "rules": [ - { - "allocation": { - "R-000220": 0.8, - "R-000221": 0.2 - }, - "cohort": "coding-agents", - "experiment": "E-000093", - "sticky_by": "consumer_id" - } - ], - "schema_version": "0.1" - } -} diff --git a/conformance/fixtures/json/telemetry-envelope.json b/conformance/fixtures/json/telemetry-envelope.json deleted file mode 100644 index 6d6967d..0000000 --- a/conformance/fixtures/json/telemetry-envelope.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "fluid_telemetry": { - "cohort": "coding-agents", - "consumer_ref": "psu-4d1e9c", - "correlation_id": "c-7f21ab90", - "experiment": "E-000093", - "id": "tl-01J8Z3XK7QW2", - "interface_id": "customer-finance-api", - "kind": "request", - "occurred_at": "2026-09-05T08:14:22Z", - "redaction": { - "applied": true, - "rules": [ - "drop-path-parameters", - "pseudonymize-consumer" - ] - }, - "request": { - "latency_ms": 118.4, - "method": "GET", - "operation": "getLatestInvoice", - "request_bytes": 0, - "response_bytes": 812, - "route": "/customers/{id}/invoices/latest", - "status": 200 - }, - "resolution": { - "policy_generation": 118, - "reason": "experiment_assignment" - }, - "revision": "R-000221", - "schema_version": "0.1" - } -} diff --git a/conformance/fixtures/pressure.yaml b/conformance/fixtures/pressure.yaml deleted file mode 100644 index 472c0ae..0000000 --- a/conformance/fixtures/pressure.yaml +++ /dev/null @@ -1,29 +0,0 @@ -fluid_pressure: - schema_version: "0.1" - - id: "P-1831" - interface_id: "customer-finance-api" - - class: "successful_but_inefficient_usage" - - first_seen: "2026-08-15T10:11:00Z" - last_seen: "2026-09-01T12:15:00Z" - - affected_cohorts: - - "partner-integrations" - - frequency: - observations: 1740 - independent_consumers: 83 - - summary: > - Consumers repeatedly retrieve complete invoice collections - to determine the latest invoice. - - evidence_refs: - - "telemetry:query-pattern-712" - - status: "OPEN" - - linked_hypotheses: - - "H-000184" diff --git a/conformance/fixtures/revision-descriptor.yaml b/conformance/fixtures/revision-descriptor.yaml deleted file mode 100644 index 111d4c2..0000000 --- a/conformance/fixtures/revision-descriptor.yaml +++ /dev/null @@ -1,32 +0,0 @@ -revision: - schema_version: "0.1" - - id: "R-000221" - interface: "customer-finance-api" - state: "candidate" - - contract: - type: "openapi" - digest: "sha256:3b1f9c2e7a4d8b6051e2c9f0a7d4b83e15c6f2a9d0b7e4c1836a5f2d9c0b7e41" - source: "artifact:openapi/customer-finance-api/r22" - - runtime: - upstream: "http://adapter-r22.fluid-runtime.svc:8080" - image: "registry/fluid/customer-finance:R-42" - digest: "sha256:3b1f9c2e7a4d8b6051e2c9f0a7d4b83e15c6f2a9d0b7e4c1836a5f2d9c0b7e41" - timeout_ms: 5000 - - intent: - version: "IEI-7" - - routing: - eligible_cohorts: - - "partner-integrations" - - "coding-agents" - max_traffic_share: 0.30 - - policy: - compatibility: "additive" - security_check: "passed" - policy_check: "passed" - rollback_to: "R-000220" diff --git a/conformance/fixtures/revision.yaml b/conformance/fixtures/revision.yaml deleted file mode 100644 index c1bf796..0000000 --- a/conformance/fixtures/revision.yaml +++ /dev/null @@ -1,134 +0,0 @@ -fluid_revision: - schema_version: "0.1" - - id: "R-000221" - interface_id: "customer-finance-api" - - revision_number: 22 - parent_revision: "R-000220" - - state: "CANDIDATE" - - created_at: "2026-09-04T13:10:00Z" - created_by: - type: "daimon" - id: "fluid-daimon/customer-finance-api" - - contract: - type: "openapi" - version: "3.1" - artifact_ref: "artifact:openapi/customer-finance-api/r22" - digest: "sha256:3b1f9c2e7a4d8b6051e2c9f0a7d4b83e15c6f2a9d0b7e4c1836a5f2d9c0b7e41" - - implementation: - artifact_ref: "artifact:adapter/customer-finance-api/r22" - source_ref: "git:9f4c2ab" - build_ref: "build:8871" - digest: "sha256:3b1f9c2e7a4d8b6051e2c9f0a7d4b83e15c6f2a9d0b7e4c1836a5f2d9c0b7e41" - - interface_evolution_intent: - version: "IEI-7" - artifact_ref: "artifact:intent/customer-finance-api/IEI-7" - - originating_hypotheses: - - "H-000184" - - backend_requirements: [] - - compatibility: - class: "BACKWARD_COMPATIBLE" - breaking_changes: [] - deprecations: [] - supersedes: [] - compatibility_evidence_refs: - - "test:compat-suite-775" - - adaptation_classes: - - "contract" - - verification: - status: "PASSED" - test_refs: - - "test:unit-12081" - - "test:contract-8821" - - "test:security-610" - - "test:compat-suite-775" - security_check: "PASSED" - policy_check: "PASSED" - - complexity: - before: - surface_area: 41 - operation_count: 39 - after: - surface_area: 42 - operation_count: 40 - delta_score: 0.18 - budget_status: "WITHIN_BUDGET" - - deployment: - environments: - - name: "production" - started_at: "2026-09-05T08:00:00Z" - routing_policy_ref: "route-policy:rp-118" - current_exposure: - traffic_share: 0.28 - cohorts: - - "partner-integrations" - - "coding-agents" - - fitness: - baseline_revision: "R-000220" - measurement_window: - start: "2026-09-05T08:00:00Z" - end: null - metrics: - requests_per_completed_task: - baseline: 2.7 - current: 1.15 - target: 1.2 - p95_latency_ms: - baseline: 300 - current: 302 - guardrail: 315 - error_rate: - baseline: 0.008 - current: 0.007 - - adoption: - eligible_consumers: 412 - active_consumers: 119 - adoption_rate: 0.289 - retained_adoption_rate: null - - economics: - build_cost: 38.20 - experiment_cost_to_date: 12.40 - currency: "EUR" - - promotion: - eligible: true - recommended_state: "STABLE" - recommendation_reason: > - Target fitness reached with no guardrail violations. - authorized_by: null - authorized_at: null - - rollback: - supported: true - target_revision: "R-000220" - procedure_ref: "runbook:rollback-r22" - - provenance: - decision_refs: - - "D-2811" - experiment_refs: - - "E-000093" - telemetry_refs: - - "telemetry:invoice-pattern-2026w36" - - audit: - immutable_event_refs: - - "EV-990221" - - "EV-990244" - - "EV-990281" diff --git a/conformance/fixtures/routing-policy.yaml b/conformance/fixtures/routing-policy.yaml deleted file mode 100644 index dc40783..0000000 --- a/conformance/fixtures/routing-policy.yaml +++ /dev/null @@ -1,20 +0,0 @@ -routing_policy: - schema_version: "0.1" - - id: "rp-118" - interface: "customer-finance-api" - generation: 118 - issued_at: "2026-09-05T08:00:00Z" - issued_by: - type: "system" - id: "fluid-experiment-controller" - - default_revision: "R-000220" - - rules: - - cohort: "coding-agents" - experiment: "E-000093" - allocation: - R-000220: 0.80 - R-000221: 0.20 - sticky_by: "consumer_id" diff --git a/conformance/fixtures/telemetry-envelope.yaml b/conformance/fixtures/telemetry-envelope.yaml deleted file mode 100644 index 8550496..0000000 --- a/conformance/fixtures/telemetry-envelope.yaml +++ /dev/null @@ -1,32 +0,0 @@ -fluid_telemetry: - schema_version: "0.1" - - id: "tl-01J8Z3XK7QW2" - occurred_at: "2026-09-05T08:14:22Z" - interface_id: "customer-finance-api" - kind: "request" - - correlation_id: "c-7f21ab90" - consumer_ref: "psu-4d1e9c" - cohort: "coding-agents" - revision: "R-000221" - experiment: "E-000093" - - resolution: - reason: "experiment_assignment" - policy_generation: 118 - - request: - operation: "getLatestInvoice" - route: "/customers/{id}/invoices/latest" - method: "GET" - status: 200 - latency_ms: 118.4 - request_bytes: 0 - response_bytes: 812 - - redaction: - applied: true - rules: - - "drop-path-parameters" - - "pseudonymize-consumer" diff --git a/conformance/validate_schemas.py b/conformance/validate_schemas.py deleted file mode 100755 index 1a1236b..0000000 --- a/conformance/validate_schemas.py +++ /dev/null @@ -1,193 +0,0 @@ -#!/usr/bin/env python3 -"""Validate the schema fixtures against the FLUID wire-contract schemas. - -Every fixture is transcribed from a worked example in spec/. If the -specification moves and the schemas do not, this fails the build. -""" -import json -import pathlib -import sys - -import yaml -from jsonschema import Draft202012Validator, RefResolver - -ROOT = pathlib.Path(__file__).resolve().parent.parent -SCHEMAS = ROOT / "schemas" -FIXTURES = ROOT / "conformance" / "fixtures" - -# fixture stem -> schema stem -PAIRS = { - "pressure": "pressure", - "hypothesis": "hypothesis", - "revision": "revision", - "experiment": "experiment", - "event": "event", - "feedback": "feedback", - "backend-requirement": "backend-requirement", - "revision-descriptor": "revision-descriptor", - "routing-policy": "routing-policy", - "telemetry-envelope": "telemetry-envelope", -} - - -def load_store(): - """Map every schema by both its $id and its bare filename. - - Schemas cross-reference each other as "common.schema.json#/$defs/...", - a relative reference, so the bare filename has to resolve too. - """ - store = {} - for path in SCHEMAS.glob("*.schema.json"): - schema = json.loads(path.read_text()) - store[path.name] = schema - if "$id" in schema: - store[schema["$id"]] = schema - return store - - -# Instances that MUST be rejected. Without these the suite could pass -# vacuously if reference resolution silently degraded to "accept anything". -NEGATIVE = [ - ( - "pressure", - "pressure without evidence references is not auditable", - {"fluid_pressure": { - "schema_version": "0.1", "id": "P-1", "interface_id": "x", - "class": "natural_usage", "first_seen": "2026-01-01T00:00:00Z", - "last_seen": "2026-01-01T00:00:00Z", "summary": "s", - "evidence_refs": [], "status": "OPEN"}}, - ), - ( - "pressure", - "pressure class outside the standard taxonomy", - {"fluid_pressure": { - "schema_version": "0.1", "id": "P-1", "interface_id": "x", - "class": "vibes", "first_seen": "2026-01-01T00:00:00Z", - "last_seen": "2026-01-01T00:00:00Z", "summary": "s", - "evidence_refs": ["telemetry:1"], "status": "OPEN"}}, - ), - ( - "hypothesis", - "hypothesis with no falsifiable expected outcome", - {"fluid_hypothesis": { - "schema_version": "0.1", "id": "H-1", "interface_id": "x", - "state": "DRAFT", "title": "t", - "observation": {"summary": "s", "evidence_refs": ["telemetry:1"]}, - "pressure": {"classes": ["natural_usage"]}, - "explanation": {"claim": "c"}, - "proposed_adaptation": {"class": "contract", "summary": "s"}, - "expected_outcomes": [], - "guardrails": [], - "complexity": {"expected_delta": {}}, - "risk": {"level": "LOW"}, - "success_criteria": {"expression": "e"}}}, - ), - ( - "experiment", - "experiment with no stop condition is not interruptible by policy", - {"fluid_experiment": { - "schema_version": "0.1", "id": "E-1", "interface_id": "x", - "hypothesis_refs": ["H-1"], "control_revision": "R-1", - "candidate_revisions": ["R-2"], - "allocation": {"control": 0.9, "candidate": 0.1}, - "metrics": {"primary": ["m"]}, - "stop_conditions": [], - "result": {"state": "PLANNED"}}}, - ), - ( - "revision-descriptor", - "descriptor with no governing intent cannot be audited", - {"revision": { - "schema_version": "0.1", "id": "R-1", "interface": "x", - "state": "stable", - "contract": {"type": "openapi", "digest": "sha256:" + "0" * 64}, - "runtime": {"upstream": "http://a:8080"}, - "policy": {"compatibility": "additive", "security_check": "passed"}}}, - ), - ( - "revision-descriptor", - "unknown revision state", - {"revision": { - "schema_version": "0.1", "id": "R-1", "interface": "x", - "state": "probably-fine", - "contract": {"type": "openapi", "digest": "sha256:" + "0" * 64}, - "runtime": {"upstream": "http://a:8080"}, - "intent": {"version": "IEI-1"}, - "policy": {"compatibility": "additive", "security_check": "passed"}}}, - ), -] - - -def run_negative(store): - failures = 0 - for schema_stem, why, instance in NEGATIVE: - schema = json.loads((SCHEMAS / f"{schema_stem}.schema.json").read_text()) - resolver = RefResolver(base_uri="", referrer=schema, store=store) - validator = Draft202012Validator(schema, resolver=resolver) - if validator.is_valid(instance): - print(f"FAIL rejected-case accepted: {why}") - failures += 1 - else: - print(f"ok rejects: {why}") - return failures - - -def main() -> int: - store = load_store() - failures = 0 - - for fixture_stem, schema_stem in sorted(PAIRS.items()): - fixture_path = FIXTURES / f"{fixture_stem}.yaml" - schema_path = SCHEMAS / f"{schema_stem}.schema.json" - - if not fixture_path.exists(): - print(f"MISSING {fixture_path.relative_to(ROOT)}") - failures += 1 - continue - - schema = json.loads(schema_path.read_text()) - instance = yaml.safe_load(fixture_path.read_text()) - - resolver = RefResolver(base_uri="", referrer=schema, store=store) - validator = Draft202012Validator(schema, resolver=resolver) - - errors = sorted(validator.iter_errors(instance), key=lambda e: list(e.path)) - if errors: - failures += 1 - print(f"FAIL {fixture_stem}") - for err in errors: - where = "/".join(str(p) for p in err.absolute_path) or "" - print(f" {where}: {err.message}") - else: - print(f"ok {fixture_stem}") - - # A schema that no fixture exercises is a schema nothing protects. - unexercised = { - p.stem.removesuffix(".schema") - for p in SCHEMAS.glob("*.schema.json") - } - set(PAIRS.values()) - {"common"} - for stem in sorted(unexercised): - print(f"WARN {stem}.schema.json has no fixture") - - # Emit JSON copies so the Go round-trip test can read fixtures without a - # YAML dependency (the build stays free of third-party Go modules). - json_dir = FIXTURES / "json" - json_dir.mkdir(exist_ok=True) - for fixture_stem in PAIRS: - instance = yaml.safe_load((FIXTURES / f"{fixture_stem}.yaml").read_text()) - (json_dir / f"{fixture_stem}.json").write_text( - json.dumps(instance, indent=2, sort_keys=True) + "\n" - ) - - print() - failures += run_negative(store) - - if failures: - print(f"\n{failures} check(s) failed") - return 1 - print(f"\nAll {len(PAIRS)} fixtures validate; all {len(NEGATIVE)} rejected cases refused") - return 0 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/docs/adr/0001-go-as-implementation-language.md b/docs/adr/0001-go-as-implementation-language.md deleted file mode 100644 index cc10a9f..0000000 --- a/docs/adr/0001-go-as-implementation-language.md +++ /dev/null @@ -1,46 +0,0 @@ -# ADR-0001 — Go as the implementation language - -**Status:** accepted · **Date:** 2026-09-04 - -## Context - -fluid-core had no implementation. Three stacks were credible: Go, TypeScript -and Python. The specification (`ArchitectureBlueprint.md` §42–43) points at a -long-running gateway, a router, an append-only store and, later, a Kubernetes -controller with custom resources. - -Python is the obvious first reach — pydantic maps almost literally onto the -record schemas, and it matches existing tooling in sibling repositories. -TypeScript is attractive because the first consumers are platform SDKs with -strong TypeScript clients. - -## Decision - -fluid-core is written in Go. - -## Rationale - -The always-on component is infrastructure: proxy, router, registry, evidence -store. That is Go's centre of mass. A single static binary is also the most -credible form of "drop this in front of any API" — the promise ADR-0002 makes. - -Blueprint §43 maps 1:1 onto controller-runtime, so the Kubernetes path stays -open at no extra cost. - -Go is the lowest-entropy of the three to debug, which matters under a per-task -budget policy. - -## What we gave up - -Record modeling is more verbose in Go than in Python. This is paid once, behind -generated types (ADR-0003), rather than continuously. - -The generative Daimon (Blueprint Phase D) would be easier in Python. ADR-0002 -makes that a non-issue: the Daimon is a separate process speaking the wire -contract, so it may be written in whatever suits it. - -## Consequences - -- No third-party Go modules are used where the standard library suffices; the - build stays offline-capable. -- Python remains a build-time dependency for schema validation only. diff --git a/docs/adr/0002-out-of-process-attachment.md b/docs/adr/0002-out-of-process-attachment.md deleted file mode 100644 index 40e2d4d..0000000 --- a/docs/adr/0002-out-of-process-attachment.md +++ /dev/null @@ -1,43 +0,0 @@ -# ADR-0002 — Out-of-process attachment - -**Status:** accepted · **Date:** 2026-09-04 - -## Context - -A FLUID interface has to be able to wrap an existing API. The question is -whether fluid-core is a library that API imports, or a process that sits in -front of it. - -Blueprint §42 already answers this implicitly: `gateway`, `revision-router` and -`adapter-r41`/`adapter-r42` are separate deployments. That is process-level -integration, not library-level. - -## Decision - -fluid-core attaches out of process. The gateway and router run in front of -adapter processes reached over the network. The target API contributes no code, -imports no library, and may be written in any stack. - -An in-process SDK is explicitly deferred, not rejected. It would buy richer -signal — call-sequence correlation and structured consumer feedback that a -proxy cannot see from outside — at the cost of SDK versioning across languages. - -## Rationale - -Minimal conformance (`FluidAPIStandards.md` §36) requires a deterministic -contract, revision identity, telemetry, declared intent, an evidence link, -responsibility boundaries and deterministic security. Every one of those is -observable from the proxy. Nothing in the conformance core needs to be inside -the target process. - -The language-agnostic promise is only credible if the framework never asks for -an import. Making the SDK optional from the start, rather than retrofitting the -proxy later, is what keeps that true. - -## Consequences - -- The revision descriptor carries `runtime.upstream`: the adapter's address. -- Interaction topology (Blueprint §6.4) is reconstructed from correlation IDs - observed at the gateway, not reported by the consumer. -- Explicit feedback (`FluidAPIStandards.md` §15) arrives over the wire as its - own endpoint rather than through an SDK call. diff --git a/docs/adr/0003-wire-contract-as-boundary.md b/docs/adr/0003-wire-contract-as-boundary.md deleted file mode 100644 index ef34eec..0000000 --- a/docs/adr/0003-wire-contract-as-boundary.md +++ /dev/null @@ -1,43 +0,0 @@ -# ADR-0003 — The wire contract is the boundary - -**Status:** accepted · **Date:** 2026-09-04 - -## Context - -ADR-0002 puts fluid-core in a separate process from everything it serves. -Something has to define how those processes agree. - -## Decision - -The boundary is a set of on-the-wire artifacts, not a Go API: - -1. the revision descriptor (Blueprint §36); -2. the routing policy (Blueprint §17); -3. the telemetry envelope; -4. the control APIs (Blueprint §44). - -These live in `schemas/` as JSON Schema. Go types in `internal/contract` are -**generated** from those schemas and are never hand-written. - -## Rationale - -Field names and semantics are the interop surface — the schema specification -says so directly in its §1. Generating from the schema means the specification -cannot drift from the implementation without failing the build. - -It also makes the Go choice reversible where it matters. A future high-throughput -gateway in another language, a Python Daimon, a TypeScript adapter: none of them -need anything from this repository except the schemas. - -## Enforcement - -- `make check-generated` fails CI when `internal/contract` is stale. -- `conformance/validate_schemas.py` validates fixtures transcribed from the - spec's own worked examples, so a spec change that the schemas miss fails too. -- The Go round-trip test decodes those fixtures with `DisallowUnknownFields`, - catching any field the generated types have no home for. - -## Consequence - -Any change that would leak a Go type across this boundary is a design failure, -not a convenience. If a consumer needs something, it goes in a schema first. diff --git a/docs/adr/0004-evidence-store-sqlite-to-postgres.md b/docs/adr/0004-evidence-store-sqlite-to-postgres.md deleted file mode 100644 index 90016c1..0000000 --- a/docs/adr/0004-evidence-store-sqlite-to-postgres.md +++ /dev/null @@ -1,37 +0,0 @@ -# ADR-0004 — Append-only evidence store, SQLite then Postgres - -**Status:** accepted · **Date:** 2026-09-04 - -## Context - -Blueprint §26 requires an evidence store favouring append-only history, with -mutable summaries derived from immutable events. §45 suggests a relational -starting model and explicitly argues it is simpler than a graph database for a -first implementation. - -## Decision - -One relational schema, two backends. SQLite for development, single-node -deployments and the CI conformance suite; PostgreSQL for anything shared. The -event table is append-only: no `UPDATE`, no `DELETE`, enforced by trigger rather -than by convention. - -Summary tables are derived views, rebuildable from events at any time. - -## Rationale - -The first real workload — publishing hall-of-helix entries to a Telegram channel -— produces a handful of events per day. Requiring Postgres to run the framework -at that scale would be an operational tax with no return. - -Keeping one schema across both means the CI suite exercises the same statements -production runs, which is where divergence usually hides. - -## Consequences - -- Portable SQL only; no backend-specific features in the core path. -- Blueprint invariant 8 (every promotion is auditable) is a storage property, - not an application convention: rewriting history has to be blocked at the - database. -- Evidence-store failure must not stop the data plane (Blueprint §34.6). The - gateway serves from cached published configuration and buffers telemetry. diff --git a/docs/adr/0005-revision-identity.md b/docs/adr/0005-revision-identity.md deleted file mode 100644 index efc40a2..0000000 --- a/docs/adr/0005-revision-identity.md +++ /dev/null @@ -1,47 +0,0 @@ -# ADR-0005 — Revision identity - -**Status:** accepted · **Date:** 2026-09-04 - -## Context - -Blueprint §54 lists this as deliberately open: "Should revision numbering be -global, per interface, semantic, or content-addressed?" It asks for the answer -to come from implementation experience rather than premature standardization. - -An answer is nonetheless needed before anything can be published, so this ADR -picks the smallest one that does not foreclose the others. - -## Decision - -Two identifiers, with different jobs. - -**A human-facing revision id, sequential per interface.** `R-000221` with a -`revision_number` of 22, both scoped to one interface. This is what appears in -audit trails, CLI output and conversation. - -**A content address for every artifact the revision names.** The contract and -the implementation each carry a `sha256:` digest. A revision is reproducibly -associated with its artifacts through those digests, per Blueprint §25. - -Identity is therefore human-sequential; equality is content-addressed. - -## Rationale - -Global numbering was rejected: it couples unrelated interfaces and makes the -number meaningless as a lineage signal. - -Purely content-addressed identity was rejected as the primary handle. Digests -are correct but unreadable, and Blueprint §16 of the schema spec is explicit -that human-readable prefixes are recommended for operational tooling. A framework -whose central concept cannot be said out loud will not get used carefully. - -Semantic versioning was rejected because compatibility is already declared -separately, in `compatibility.class`. Encoding it a second time in the -identifier invites the two to disagree. - -## What stays open - -Whether `revision_number` should be dense (no gaps) is unresolved. Failed -candidates currently consume a number. That is defensible — the attempt is part -of the history — but it may prove noisy. Revisit after the first real interface -has produced enough failed candidates to tell. diff --git a/internal/contract/backend_requirement_gen.go b/internal/contract/backend_requirement_gen.go deleted file mode 100644 index 9ee21f9..0000000 --- a/internal/contract/backend_requirement_gen.go +++ /dev/null @@ -1,100 +0,0 @@ -// Code generated by tools/schemagen. DO NOT EDIT. -// Source: schemas/. Regenerate with `make generate`. - -package contract - -type FluidBackendRequirementCapability struct { - Description string `json:"description" yaml:"description"` - Title string `json:"title" yaml:"title"` -} - -type FluidBackendRequirementDispositionState string - -const ( - FluidBackendRequirementDispositionStateOPEN FluidBackendRequirementDispositionState = "OPEN" - FluidBackendRequirementDispositionStateACCEPTED FluidBackendRequirementDispositionState = "ACCEPTED" - FluidBackendRequirementDispositionStatePLANNED FluidBackendRequirementDispositionState = "PLANNED" - FluidBackendRequirementDispositionStateAVAILABLE FluidBackendRequirementDispositionState = "AVAILABLE" - FluidBackendRequirementDispositionStatePARTIALLYAVAILABLE FluidBackendRequirementDispositionState = "PARTIALLY_AVAILABLE" - FluidBackendRequirementDispositionStateOUTOFSCOPE FluidBackendRequirementDispositionState = "OUT_OF_SCOPE" - FluidBackendRequirementDispositionStateREJECTED FluidBackendRequirementDispositionState = "REJECTED" - FluidBackendRequirementDispositionStateSUPERSEDED FluidBackendRequirementDispositionState = "SUPERSEDED" -) - -// Valid reports whether v is a defined FluidBackendRequirementDispositionState. -func (v FluidBackendRequirementDispositionState) Valid() bool { - switch v { - case FluidBackendRequirementDispositionStateOPEN, FluidBackendRequirementDispositionStateACCEPTED, FluidBackendRequirementDispositionStatePLANNED, FluidBackendRequirementDispositionStateAVAILABLE, FluidBackendRequirementDispositionStatePARTIALLYAVAILABLE, FluidBackendRequirementDispositionStateOUTOFSCOPE, FluidBackendRequirementDispositionStateREJECTED, FluidBackendRequirementDispositionStateSUPERSEDED: - return true - } - return false -} - -// The backend decides. Repeated OUT_OF_SCOPE feeds boundary learning -// (ArchitectureBlueprint.md section 22). -type FluidBackendRequirementDisposition struct { - Reason *string `json:"reason,omitempty" yaml:"reason,omitempty"` - State FluidBackendRequirementDispositionState `json:"state" yaml:"state"` - TargetRef *string `json:"target_ref,omitempty" yaml:"target_ref,omitempty"` -} - -type FluidBackendRequirementExpectedUsage struct { - RequestsPerDay *int64 `json:"requests_per_day,omitempty" yaml:"requests_per_day,omitempty"` -} - -type FluidBackendRequirementQuality struct { - Availability *UnitInterval `json:"availability,omitempty" yaml:"availability,omitempty"` - P95LatencyMS *float64 `json:"p95_latency_ms,omitempty" yaml:"p95_latency_ms,omitempty"` -} - -type FluidBackendRequirementSecurity struct { - AuthorizationScope string `json:"authorization_scope,omitempty" yaml:"authorization_scope,omitempty"` - TenantIsolation string `json:"tenant_isolation,omitempty" yaml:"tenant_isolation,omitempty"` -} - -type FluidBackendRequirementSemantics struct { - Consistency string `json:"consistency,omitempty" yaml:"consistency,omitempty"` - RequiredFields []string `json:"required_fields,omitempty" yaml:"required_fields,omitempty"` -} - -type FluidBackendRequirementUrgency string - -const ( - FluidBackendRequirementUrgencyLOW FluidBackendRequirementUrgency = "LOW" - FluidBackendRequirementUrgencyMEDIUM FluidBackendRequirementUrgency = "MEDIUM" - FluidBackendRequirementUrgencyHIGH FluidBackendRequirementUrgency = "HIGH" -) - -// Valid reports whether v is a defined FluidBackendRequirementUrgency. -func (v FluidBackendRequirementUrgency) Valid() bool { - switch v { - case FluidBackendRequirementUrgencyLOW, FluidBackendRequirementUrgencyMEDIUM, FluidBackendRequirementUrgencyHIGH: - return true - } - return false -} - -type FluidBackendRequirement struct { - BackendService string `json:"backend_service" yaml:"backend_service"` - Capability FluidBackendRequirementCapability `json:"capability" yaml:"capability"` - - // The backend decides. Repeated OUT_OF_SCOPE feeds boundary learning - // (ArchitectureBlueprint.md section 22). - Disposition FluidBackendRequirementDisposition `json:"disposition" yaml:"disposition"` - ExpectedUsage *FluidBackendRequirementExpectedUsage `json:"expected_usage,omitempty" yaml:"expected_usage,omitempty"` - ID BackendRequirementID `json:"id" yaml:"id"` - OriginatingHypothesis *HypothesisID `json:"originating_hypothesis,omitempty" yaml:"originating_hypothesis,omitempty"` - OriginatingInterface InterfaceID `json:"originating_interface" yaml:"originating_interface"` - OriginatingRevision *RevisionID `json:"originating_revision,omitempty" yaml:"originating_revision,omitempty"` - Quality *FluidBackendRequirementQuality `json:"quality,omitempty" yaml:"quality,omitempty"` - SchemaVersion SchemaVersion `json:"schema_version" yaml:"schema_version"` - Security *FluidBackendRequirementSecurity `json:"security,omitempty" yaml:"security,omitempty"` - Semantics *FluidBackendRequirementSemantics `json:"semantics,omitempty" yaml:"semantics,omitempty"` - Urgency *FluidBackendRequirementUrgency `json:"urgency,omitempty" yaml:"urgency,omitempty"` -} - -// Capability the interface needs but has no authority to build. FLUID escalates -// rather than crossing the boundary (FluidAPIStandards.md section 30). -type BackendRequirementDocument struct { - FluidBackendRequirement FluidBackendRequirement `json:"fluid_backend_requirement" yaml:"fluid_backend_requirement"` -} diff --git a/internal/contract/common_gen.go b/internal/contract/common_gen.go deleted file mode 100644 index 7eba741..0000000 --- a/internal/contract/common_gen.go +++ /dev/null @@ -1,207 +0,0 @@ -// Code generated by tools/schemagen. DO NOT EDIT. -// Source: schemas/. Regenerate with `make generate`. - -package contract - -import "time" - -type ActorType string - -const ( - ActorTypeHuman ActorType = "human" - ActorTypeDaimon ActorType = "daimon" - ActorTypePolicy ActorType = "policy" - ActorTypeSystem ActorType = "system" - ActorTypeConsumer ActorType = "consumer" -) - -// Valid reports whether v is a defined ActorType. -func (v ActorType) Valid() bool { - switch v { - case ActorTypeHuman, ActorTypeDaimon, ActorTypePolicy, ActorTypeSystem, ActorTypeConsumer: - return true - } - return false -} - -// Who or what performed an action. FLUID distinguishes these because trust levels -// differ (ArchitectureBlueprint.md section 47). -type Actor struct { - ID string `json:"id" yaml:"id"` - ModelOrAgent string `json:"model_or_agent,omitempty" yaml:"model_or_agent,omitempty"` - Type ActorType `json:"type" yaml:"type"` -} - -// FluidAPIStandards.md section 16. -type AdaptationClass string - -const ( - AdaptationClassPresentation AdaptationClass = "presentation" - AdaptationClassContract AdaptationClass = "contract" - AdaptationClassComposition AdaptationClass = "composition" - AdaptationClassImplementation AdaptationClass = "implementation" - AdaptationClassRequirementEscalation AdaptationClass = "requirement_escalation" -) - -// Valid reports whether v is a defined AdaptationClass. -func (v AdaptationClass) Valid() bool { - switch v { - case AdaptationClassPresentation, AdaptationClassContract, AdaptationClassComposition, AdaptationClassImplementation, AdaptationClassRequirementEscalation: - return true - } - return false -} - -// Opaque reference into the artifact store. -type ArtifactRef string - -type BackendRequirementID string - -type CohortID string - -// Standard dimensions, implementation-defined scoring (FluidAPIStandards.md section -// 22). -type ComplexityDelta struct { - ActiveRevisionCount *float64 `json:"active_revision_count,omitempty" yaml:"active_revision_count,omitempty"` - AlternativePathCount *float64 `json:"alternative_path_count,omitempty" yaml:"alternative_path_count,omitempty"` - BackendCompositionCount *float64 `json:"backend_composition_count,omitempty" yaml:"backend_composition_count,omitempty"` - CognitiveLoad *float64 `json:"cognitive_load,omitempty" yaml:"cognitive_load,omitempty"` - ConceptCount *float64 `json:"concept_count,omitempty" yaml:"concept_count,omitempty"` - DependencyCount *float64 `json:"dependency_count,omitempty" yaml:"dependency_count,omitempty"` - ExceptionCount *float64 `json:"exception_count,omitempty" yaml:"exception_count,omitempty"` - OperationCount *float64 `json:"operation_count,omitempty" yaml:"operation_count,omitempty"` - ParameterDimensionality *float64 `json:"parameter_dimensionality,omitempty" yaml:"parameter_dimensionality,omitempty"` - SemanticOverlap *float64 `json:"semantic_overlap,omitempty" yaml:"semantic_overlap,omitempty"` - SurfaceArea *float64 `json:"surface_area,omitempty" yaml:"surface_area,omitempty"` -} - -type DecisionID string - -// Content address of an artifact. -type Digest string - -type EventID string - -// Opaque reference into the evidence store, conventionally ':' such -// as 'telemetry:invoice-pattern-2026w36'. -type EvidenceRef string - -type ExpectedOutcomeDirection string - -const ( - ExpectedOutcomeDirectionLower ExpectedOutcomeDirection = "lower" - ExpectedOutcomeDirectionHigher ExpectedOutcomeDirection = "higher" - ExpectedOutcomeDirectionUnchanged ExpectedOutcomeDirection = "unchanged" -) - -// Valid reports whether v is a defined ExpectedOutcomeDirection. -func (v ExpectedOutcomeDirection) Valid() bool { - switch v { - case ExpectedOutcomeDirectionLower, ExpectedOutcomeDirectionHigher, ExpectedOutcomeDirectionUnchanged: - return true - } - return false -} - -type ExpectedOutcome struct { - Baseline *float64 `json:"baseline,omitempty" yaml:"baseline,omitempty"` - Cohort string `json:"cohort,omitempty" yaml:"cohort,omitempty"` - Direction ExpectedOutcomeDirection `json:"direction" yaml:"direction"` - Metric string `json:"metric" yaml:"metric"` - Target float64 `json:"target" yaml:"target"` -} - -type ExperimentID string - -type FeedbackID string - -// FluidAPIStandards.md section 20. No universal scalar is defined on purpose. -type FitnessDimensions struct { - Availability *float64 `json:"availability,omitempty" yaml:"availability,omitempty"` - ClientUtility *float64 `json:"client_utility,omitempty" yaml:"client_utility,omitempty"` - Compatibility *float64 `json:"compatibility,omitempty" yaml:"compatibility,omitempty"` - Correctness *float64 `json:"correctness,omitempty" yaml:"correctness,omitempty"` - Discoverability *float64 `json:"discoverability,omitempty" yaml:"discoverability,omitempty"` - ImplementationCost *float64 `json:"implementation_cost,omitempty" yaml:"implementation_cost,omitempty"` - Maintainability *float64 `json:"maintainability,omitempty" yaml:"maintainability,omitempty"` - OperationalCost *float64 `json:"operational_cost,omitempty" yaml:"operational_cost,omitempty"` - Performance *float64 `json:"performance,omitempty" yaml:"performance,omitempty"` - Reliability *float64 `json:"reliability,omitempty" yaml:"reliability,omitempty"` - ResourceConsumption *float64 `json:"resource_consumption,omitempty" yaml:"resource_consumption,omitempty"` - Security *float64 `json:"security,omitempty" yaml:"security,omitempty"` - Simplicity *float64 `json:"simplicity,omitempty" yaml:"simplicity,omitempty"` -} - -type GuardrailOperator string - -const ( - GuardrailOperatorLt GuardrailOperator = "<" - GuardrailOperatorLte GuardrailOperator = "<=" - GuardrailOperatorEq GuardrailOperator = "==" - GuardrailOperatorNeq GuardrailOperator = "!=" - GuardrailOperatorGte GuardrailOperator = ">=" - GuardrailOperatorGt GuardrailOperator = ">" -) - -// Valid reports whether v is a defined GuardrailOperator. -func (v GuardrailOperator) Valid() bool { - switch v { - case GuardrailOperatorLt, GuardrailOperatorLte, GuardrailOperatorEq, GuardrailOperatorNeq, GuardrailOperatorGte, GuardrailOperatorGt: - return true - } - return false -} - -// A constraint that must not regress beyond threshold. Guardrails are evaluated -// deterministically. -type Guardrail struct { - Metric string `json:"metric" yaml:"metric"` - Operator GuardrailOperator `json:"operator" yaml:"operator"` - Threshold any `json:"threshold" yaml:"threshold"` -} - -type HypothesisID string - -// Stable identifier of the owning interface. -type InterfaceID string - -type ObservationWindow struct { - // Null while the window is still open. - End *Timestamp `json:"end,omitempty" yaml:"end,omitempty"` - Start Timestamp `json:"start" yaml:"start"` -} - -// FluidAPIStandards.md section 13. -type PressureClass string - -const ( - PressureClassNaturalUsage PressureClass = "natural_usage" - PressureClassSuccessfulButInefficientUsage PressureClass = "successful_but_inefficient_usage" - PressureClassRecoverableMisunderstanding PressureClass = "recoverable_misunderstanding" - PressureClassRepeatedExpectationMismatch PressureClass = "repeated_expectation_mismatch" - PressureClassPoorDiscoverability PressureClass = "poor_discoverability" - PressureClassMissingInterfaceCapability PressureClass = "missing_interface_capability" - PressureClassMissingBackendCapability PressureClass = "missing_backend_capability" - PressureClassOutOfScopeDemand PressureClass = "out_of_scope_demand" - PressureClassProhibitedDemand PressureClass = "prohibited_demand" - PressureClassImplementationFailure PressureClass = "implementation_failure" -) - -// Valid reports whether v is a defined PressureClass. -func (v PressureClass) Valid() bool { - switch v { - case PressureClassNaturalUsage, PressureClassSuccessfulButInefficientUsage, PressureClassRecoverableMisunderstanding, PressureClassRepeatedExpectationMismatch, PressureClassPoorDiscoverability, PressureClassMissingInterfaceCapability, PressureClassMissingBackendCapability, PressureClassOutOfScopeDemand, PressureClassProhibitedDemand, PressureClassImplementationFailure: - return true - } - return false -} - -type PressureID string - -type RevisionID string - -type SchemaVersion string - -type Timestamp = time.Time - -type UnitInterval float64 diff --git a/internal/contract/contract_test.go b/internal/contract/contract_test.go deleted file mode 100644 index 68e7482..0000000 --- a/internal/contract/contract_test.go +++ /dev/null @@ -1,203 +0,0 @@ -package contract - -import ( - "encoding/json" - "os" - "path/filepath" - "reflect" - "testing" -) - -// fixtureDir holds JSON copies of the spec-derived YAML fixtures, written by -// conformance/validate_schemas.py. -const fixtureDir = "../../conformance/fixtures/json" - -// roundTrip decodes a fixture into T, re-encodes it, and reports any field the -// generated types dropped on the way through. -// -// This is the drift check that matters: a schema change that schemagen did not -// pick up shows here as a field that vanishes. -func roundTrip[T any](t *testing.T, name string) { - t.Helper() - - raw, err := os.ReadFile(filepath.Join(fixtureDir, name+".json")) - if err != nil { - t.Fatalf("read fixture: %v (run `make validate` first)", err) - } - - var typed T - dec := json.NewDecoder(bytesReader(raw)) - dec.DisallowUnknownFields() - if err := dec.Decode(&typed); err != nil { - t.Fatalf("decode into %T: %v", typed, err) - } - - encoded, err := json.Marshal(typed) - if err != nil { - t.Fatalf("re-encode: %v", err) - } - - var before, after map[string]any - if err := json.Unmarshal(raw, &before); err != nil { - t.Fatalf("unmarshal fixture: %v", err) - } - if err := json.Unmarshal(encoded, &after); err != nil { - t.Fatalf("unmarshal re-encoded: %v", err) - } - - if missing := missingKeys(before, after, ""); len(missing) > 0 { - t.Errorf("round trip dropped fields: %v", missing) - } -} - -// missingKeys walks want and reports paths absent from got. -// -// Explicit nulls and empty collections are skipped. The schemas treat -// "end: null" and an absent "end" as the same statement (the window is still -// open), so omitempty dropping them on re-encode is correct rather than drift. -// Genuine drift -- a fixture field the generated types have no home for -- is -// caught by DisallowUnknownFields on the way in. -func missingKeys(want, got map[string]any, prefix string) []string { - var missing []string - for k, wv := range want { - path := k - if prefix != "" { - path = prefix + "." + k - } - if isEmptyValue(wv) { - continue - } - gv, ok := got[k] - if !ok { - missing = append(missing, path) - continue - } - wm, wok := wv.(map[string]any) - gm, gok := gv.(map[string]any) - if wok && gok { - missing = append(missing, missingKeys(wm, gm, path)...) - } - } - return missing -} - -// isEmptyValue reports values the schema treats as equivalent to absent. -func isEmptyValue(v any) bool { - switch t := v.(type) { - case nil: - return true - case []any: - return len(t) == 0 - case map[string]any: - return len(t) == 0 - } - return false -} - -func TestFixturesRoundTrip(t *testing.T) { - t.Run("pressure", func(t *testing.T) { roundTrip[PressureDocument](t, "pressure") }) - t.Run("hypothesis", func(t *testing.T) { roundTrip[HypothesisDocument](t, "hypothesis") }) - t.Run("revision", func(t *testing.T) { roundTrip[RevisionDocument](t, "revision") }) - t.Run("experiment", func(t *testing.T) { roundTrip[ExperimentDocument](t, "experiment") }) - t.Run("event", func(t *testing.T) { roundTrip[EventDocument](t, "event") }) - t.Run("feedback", func(t *testing.T) { roundTrip[FeedbackDocument](t, "feedback") }) - t.Run("backend-requirement", func(t *testing.T) { - roundTrip[BackendRequirementDocument](t, "backend-requirement") - }) - t.Run("revision-descriptor", func(t *testing.T) { - roundTrip[RevisionDescriptorDocument](t, "revision-descriptor") - }) - t.Run("routing-policy", func(t *testing.T) { roundTrip[RoutingPolicyDocument](t, "routing-policy") }) - t.Run("telemetry-envelope", func(t *testing.T) { - roundTrip[TelemetryEnvelopeDocument](t, "telemetry-envelope") - }) -} - -func TestKindOf(t *testing.T) { - cases := map[string]EntityKind{ - "H-000184": KindHypothesis, - "R-000221": KindRevision, - "E-000093": KindExperiment, - "P-1831": KindPressure, - "BR-0041": KindBackendRequirement, - "D-2811": KindDecision, - "EV-990281": KindEvent, - "F-9821": KindFeedback, - "C-17": KindCohort, - } - for id, want := range cases { - got, ok := KindOf(id) - if !ok || got != want { - t.Errorf("KindOf(%q) = %q, %v; want %q", id, got, ok, want) - } - } - - // BR- and EV- must not be swallowed by the single-letter prefixes. - if k, _ := KindOf("BR-1"); k != KindBackendRequirement { - t.Errorf("BR- prefix mis-classified as %q", k) - } - if k, _ := KindOf("EV-1"); k != KindEvent { - t.Errorf("EV- prefix mis-classified as %q", k) - } - - if _, ok := KindOf("something"); ok { - t.Error("unprefixed identifier should not classify") - } -} - -func TestRequireKind(t *testing.T) { - if err := RequireKind("R-1", KindRevision); err != nil { - t.Errorf("valid revision id rejected: %v", err) - } - err := RequireKind("H-1", KindRevision) - if err == nil { - t.Fatal("hypothesis id accepted where a revision id was required") - } - var wrong *ErrWrongKind - if !errorsAs(err, &wrong) { - t.Fatalf("expected *ErrWrongKind, got %T", err) - } - if wrong.Got != KindHypothesis || wrong.Want != KindRevision { - t.Errorf("unexpected error detail: %+v", wrong) - } -} - -// Small helpers keep the test file free of imports the generated code does not -// already require. -func bytesReader(b []byte) *jsonReader { return &jsonReader{b: b} } - -type jsonReader struct { - b []byte - i int -} - -func (r *jsonReader) Read(p []byte) (int, error) { - if r.i >= len(r.b) { - return 0, errEOF - } - n := copy(p, r.b[r.i:]) - r.i += n - return n, nil -} - -var errEOF = errorString("EOF") - -type errorString string - -func (e errorString) Error() string { return string(e) } - -func errorsAs(err error, target any) bool { - tv := reflect.ValueOf(target).Elem() - for err != nil { - if reflect.TypeOf(err).AssignableTo(tv.Type()) { - tv.Set(reflect.ValueOf(err)) - return true - } - u, ok := err.(interface{ Unwrap() error }) - if !ok { - return false - } - err = u.Unwrap() - } - return false -} diff --git a/internal/contract/event_gen.go b/internal/contract/event_gen.go deleted file mode 100644 index a6f478a..0000000 --- a/internal/contract/event_gen.go +++ /dev/null @@ -1,48 +0,0 @@ -// Code generated by tools/schemagen. DO NOT EDIT. -// Source: schemas/. Regenerate with `make generate`. - -package contract - -type FluidEventEntityType string - -const ( - FluidEventEntityTypePressure FluidEventEntityType = "pressure" - FluidEventEntityTypeHypothesis FluidEventEntityType = "hypothesis" - FluidEventEntityTypeRevision FluidEventEntityType = "revision" - FluidEventEntityTypeExperiment FluidEventEntityType = "experiment" - FluidEventEntityTypeBackendRequirement FluidEventEntityType = "backend_requirement" - FluidEventEntityTypeIntent FluidEventEntityType = "intent" - FluidEventEntityTypeDecision FluidEventEntityType = "decision" - FluidEventEntityTypeRoutingPolicy FluidEventEntityType = "routing_policy" -) - -// Valid reports whether v is a defined FluidEventEntityType. -func (v FluidEventEntityType) Valid() bool { - switch v { - case FluidEventEntityTypePressure, FluidEventEntityTypeHypothesis, FluidEventEntityTypeRevision, FluidEventEntityTypeExperiment, FluidEventEntityTypeBackendRequirement, FluidEventEntityTypeIntent, FluidEventEntityTypeDecision, FluidEventEntityTypeRoutingPolicy: - return true - } - return false -} - -type FluidEvent struct { - Actor Actor `json:"actor" yaml:"actor"` - EntityID string `json:"entity_id" yaml:"entity_id"` - EntityType FluidEventEntityType `json:"entity_type" yaml:"entity_type"` - EventType string `json:"event_type" yaml:"event_type"` - EvidenceRefs []EvidenceRef `json:"evidence_refs,omitempty" yaml:"evidence_refs,omitempty"` - ID EventID `json:"id" yaml:"id"` - - // Identifiers of the records this transition drew on. - Inputs []string `json:"inputs,omitempty" yaml:"inputs,omitempty"` - OccurredAt Timestamp `json:"occurred_at" yaml:"occurred_at"` - Reason string `json:"reason,omitempty" yaml:"reason,omitempty"` - SchemaVersion SchemaVersion `json:"schema_version" yaml:"schema_version"` -} - -// Append-only record of a material lifecycle transition. FLUID evolution is itself -// a system behavior that must remain reconstructable -// (FluidHypothesisRevisionSchema.md section 15). -type EventDocument struct { - FluidEvent FluidEvent `json:"fluid_event" yaml:"fluid_event"` -} diff --git a/internal/contract/experiment_gen.go b/internal/contract/experiment_gen.go deleted file mode 100644 index bf5cf9f..0000000 --- a/internal/contract/experiment_gen.go +++ /dev/null @@ -1,108 +0,0 @@ -// Code generated by tools/schemagen. DO NOT EDIT. -// Source: schemas/. Regenerate with `make generate`. - -package contract - -type FluidExperimentAmendmentsItem struct { - Actor Actor `json:"actor" yaml:"actor"` - At Timestamp `json:"at" yaml:"at"` - Change string `json:"change" yaml:"change"` - Reason string `json:"reason" yaml:"reason"` -} - -type FluidExperimentMechanism string - -const ( - FluidExperimentMechanismSandbox FluidExperimentMechanism = "sandbox" - FluidExperimentMechanismShadow FluidExperimentMechanism = "shadow" - FluidExperimentMechanismSynthetic FluidExperimentMechanism = "synthetic" - FluidExperimentMechanismReplay FluidExperimentMechanism = "replay" - FluidExperimentMechanismCanary FluidExperimentMechanism = "canary" - FluidExperimentMechanismOptIn FluidExperimentMechanism = "opt_in" - FluidExperimentMechanismCohort FluidExperimentMechanism = "cohort" - FluidExperimentMechanismTenant FluidExperimentMechanism = "tenant" - FluidExperimentMechanismPercentage FluidExperimentMechanism = "percentage" -) - -// Valid reports whether v is a defined FluidExperimentMechanism. -func (v FluidExperimentMechanism) Valid() bool { - switch v { - case FluidExperimentMechanismSandbox, FluidExperimentMechanismShadow, FluidExperimentMechanismSynthetic, FluidExperimentMechanismReplay, FluidExperimentMechanismCanary, FluidExperimentMechanismOptIn, FluidExperimentMechanismCohort, FluidExperimentMechanismTenant, FluidExperimentMechanismPercentage: - return true - } - return false -} - -// Primary metrics and guardrails are kept distinct so a scalar can never hide a -// regression (ArchitectureBlueprint.md section 48.5). -type FluidExperimentMetrics struct { - Guardrails []string `json:"guardrails,omitempty" yaml:"guardrails,omitempty"` - Learning []string `json:"learning,omitempty" yaml:"learning,omitempty"` - Primary []string `json:"primary" yaml:"primary"` - Secondary []string `json:"secondary,omitempty" yaml:"secondary,omitempty"` -} - -type FluidExperimentResultState string - -const ( - FluidExperimentResultStatePLANNED FluidExperimentResultState = "PLANNED" - FluidExperimentResultStateRUNNING FluidExperimentResultState = "RUNNING" - FluidExperimentResultStateSTOPPED FluidExperimentResultState = "STOPPED" - FluidExperimentResultStateCOMPLETED FluidExperimentResultState = "COMPLETED" - FluidExperimentResultStateABANDONED FluidExperimentResultState = "ABANDONED" -) - -// Valid reports whether v is a defined FluidExperimentResultState. -func (v FluidExperimentResultState) Valid() bool { - switch v { - case FluidExperimentResultStatePLANNED, FluidExperimentResultStateRUNNING, FluidExperimentResultStateSTOPPED, FluidExperimentResultStateCOMPLETED, FluidExperimentResultStateABANDONED: - return true - } - return false -} - -type FluidExperimentResult struct { - EvidenceRefs []EvidenceRef `json:"evidence_refs,omitempty" yaml:"evidence_refs,omitempty"` - PreferredRevision *RevisionID `json:"preferred_revision,omitempty" yaml:"preferred_revision,omitempty"` - State FluidExperimentResultState `json:"state" yaml:"state"` - StoppedReason *string `json:"stopped_reason,omitempty" yaml:"stopped_reason,omitempty"` -} - -type FluidExperiment struct { - // Shares by revision id or by the reserved key 'control'. Allocation is - // deterministic and enacted by the router, never by this record. - Allocation map[string]UnitInterval `json:"allocation" yaml:"allocation"` - - // Success criteria may not be changed after results are visible without recording - // the amendment (ArchitectureBlueprint.md section 18). - Amendments []FluidExperimentAmendmentsItem `json:"amendments,omitempty" yaml:"amendments,omitempty"` - CandidateRevisions []RevisionID `json:"candidate_revisions" yaml:"candidate_revisions"` - Cohorts []CohortID `json:"cohorts,omitempty" yaml:"cohorts,omitempty"` - ControlRevision RevisionID `json:"control_revision" yaml:"control_revision"` - - // An experiment without a hypothesis measures nothing in particular. - HypothesisRefs []HypothesisID `json:"hypothesis_refs" yaml:"hypothesis_refs"` - ID ExperimentID `json:"id" yaml:"id"` - InterfaceID InterfaceID `json:"interface_id" yaml:"interface_id"` - MaxDurationHours *float64 `json:"max_duration_hours,omitempty" yaml:"max_duration_hours,omitempty"` - Mechanism *FluidExperimentMechanism `json:"mechanism,omitempty" yaml:"mechanism,omitempty"` - - // Primary metrics and guardrails are kept distinct so a scalar can never hide a - // regression (ArchitectureBlueprint.md section 48.5). - Metrics FluidExperimentMetrics `json:"metrics" yaml:"metrics"` - PlannedEndAt *Timestamp `json:"planned_end_at,omitempty" yaml:"planned_end_at,omitempty"` - Result FluidExperimentResult `json:"result" yaml:"result"` - SchemaVersion SchemaVersion `json:"schema_version" yaml:"schema_version"` - StartAt *Timestamp `json:"start_at,omitempty" yaml:"start_at,omitempty"` - StartConditions []string `json:"start_conditions,omitempty" yaml:"start_conditions,omitempty"` - - // Every experiment has a stop condition (ArchitectureBlueprint.md section 55, - // invariant 7). - StopConditions []string `json:"stop_conditions" yaml:"stop_conditions"` -} - -// Connects hypotheses to revisions under bounded conditions. Experiments must be -// interruptible (ArchitectureBlueprint.md section 16). -type ExperimentDocument struct { - FluidExperiment FluidExperiment `json:"fluid_experiment" yaml:"fluid_experiment"` -} diff --git a/internal/contract/feedback_gen.go b/internal/contract/feedback_gen.go deleted file mode 100644 index b6b0a63..0000000 --- a/internal/contract/feedback_gen.go +++ /dev/null @@ -1,33 +0,0 @@ -// Code generated by tools/schemagen. DO NOT EDIT. -// Source: schemas/. Regenerate with `make generate`. - -package contract - -type FluidFeedbackImpact struct { - Calls *int64 `json:"calls,omitempty" yaml:"calls,omitempty"` - Description string `json:"description,omitempty" yaml:"description,omitempty"` - LatencyMS *float64 `json:"latency_ms,omitempty" yaml:"latency_ms,omitempty"` -} - -type FluidFeedback struct { - Attempt string `json:"attempt,omitempty" yaml:"attempt,omitempty"` - AttemptedOperations []string `json:"attempted_operations,omitempty" yaml:"attempted_operations,omitempty"` - Cohort *CohortID `json:"cohort,omitempty" yaml:"cohort,omitempty"` - Confidence *UnitInterval `json:"confidence,omitempty" yaml:"confidence,omitempty"` - Goal string `json:"goal" yaml:"goal"` - ID FeedbackID `json:"id" yaml:"id"` - Impact *FluidFeedbackImpact `json:"impact,omitempty" yaml:"impact,omitempty"` - InterfaceID InterfaceID `json:"interface_id" yaml:"interface_id"` - MissingCapability string `json:"missing_capability,omitempty" yaml:"missing_capability,omitempty"` - Outcome string `json:"outcome,omitempty" yaml:"outcome,omitempty"` - ReceivedAt Timestamp `json:"received_at" yaml:"received_at"` - Revision *RevisionID `json:"revision,omitempty" yaml:"revision,omitempty"` - SchemaVersion SchemaVersion `json:"schema_version" yaml:"schema_version"` - Workaround any `json:"workaround,omitempty" yaml:"workaround,omitempty"` -} - -// Structured feedback from a consumer. Treated as evidence and never as authority -// to change anything (FluidAPIStandards.md section 15). -type FeedbackDocument struct { - FluidFeedback FluidFeedback `json:"fluid_feedback" yaml:"fluid_feedback"` -} diff --git a/internal/contract/hypothesis_gen.go b/internal/contract/hypothesis_gen.go deleted file mode 100644 index a733919..0000000 --- a/internal/contract/hypothesis_gen.go +++ /dev/null @@ -1,254 +0,0 @@ -// Code generated by tools/schemagen. DO NOT EDIT. -// Source: schemas/. Regenerate with `make generate`. - -package contract - -type FluidHypothesisAudit struct { - DecisionRefs []DecisionID `json:"decision_refs,omitempty" yaml:"decision_refs,omitempty"` - ImmutableEventRefs []EventID `json:"immutable_event_refs,omitempty" yaml:"immutable_event_refs,omitempty"` -} - -type FluidHypothesisBackendRequirements struct { - Required bool `json:"required" yaml:"required"` - RequirementRefs []BackendRequirementID `json:"requirement_refs,omitempty" yaml:"requirement_refs,omitempty"` -} - -// Competing hypotheses are legitimate (FluidAPIStandards.md section 19). -type FluidHypothesisCompetition struct { - Alternatives []HypothesisID `json:"alternatives,omitempty" yaml:"alternatives,omitempty"` - GroupID string `json:"group_id" yaml:"group_id"` -} - -type FluidHypothesisComplexity struct { - ExpectedDelta ComplexityDelta `json:"expected_delta" yaml:"expected_delta"` - Score *float64 `json:"score,omitempty" yaml:"score,omitempty"` -} - -type FluidHypothesisEconomicsExpectedValueClass string - -const ( - FluidHypothesisEconomicsExpectedValueClassLOW FluidHypothesisEconomicsExpectedValueClass = "LOW" - FluidHypothesisEconomicsExpectedValueClassMEDIUM FluidHypothesisEconomicsExpectedValueClass = "MEDIUM" - FluidHypothesisEconomicsExpectedValueClassHIGH FluidHypothesisEconomicsExpectedValueClass = "HIGH" -) - -// Valid reports whether v is a defined FluidHypothesisEconomicsExpectedValueClass. -func (v FluidHypothesisEconomicsExpectedValueClass) Valid() bool { - switch v { - case FluidHypothesisEconomicsExpectedValueClassLOW, FluidHypothesisEconomicsExpectedValueClassMEDIUM, FluidHypothesisEconomicsExpectedValueClassHIGH: - return true - } - return false -} - -// A hypothesis may be valuable but not yet worth exploring -// (ArchitectureBlueprint.md section 29). -type FluidHypothesisEconomics struct { - Currency string `json:"currency,omitempty" yaml:"currency,omitempty"` - EstimatedExperimentCost *float64 `json:"estimated_experiment_cost,omitempty" yaml:"estimated_experiment_cost,omitempty"` - EstimatedImplementationCost *float64 `json:"estimated_implementation_cost,omitempty" yaml:"estimated_implementation_cost,omitempty"` - ExpectedValueClass *FluidHypothesisEconomicsExpectedValueClass `json:"expected_value_class,omitempty" yaml:"expected_value_class,omitempty"` -} - -// Explanatory reach is a prioritization signal, never a correctness claim -// (FluidAPIStandards.md section 18). -type FluidHypothesisExplanationReach struct { - Explains []PressureID `json:"explains,omitempty" yaml:"explains,omitempty"` - Notes string `json:"notes,omitempty" yaml:"notes,omitempty"` - Score *float64 `json:"score,omitempty" yaml:"score,omitempty"` -} - -// What we think explains the observation. -type FluidHypothesisExplanation struct { - Claim string `json:"claim" yaml:"claim"` - - // Explanatory reach is a prioritization signal, never a correctness claim - // (FluidAPIStandards.md section 18). - Reach *FluidHypothesisExplanationReach `json:"reach,omitempty" yaml:"reach,omitempty"` -} - -type FluidHypothesisFailureCriteria struct { - Expression string `json:"expression" yaml:"expression"` -} - -type FluidHypothesisFitnessDimensions struct { - Expected *FitnessDimensions `json:"expected,omitempty" yaml:"expected,omitempty"` -} - -type FluidHypothesisLearningValue struct { - Notes string `json:"notes,omitempty" yaml:"notes,omitempty"` - Score *float64 `json:"score,omitempty" yaml:"score,omitempty"` -} - -// What was seen. Not what it means. -type FluidHypothesisObservation struct { - AffectedCohorts []CohortID `json:"affected_cohorts,omitempty" yaml:"affected_cohorts,omitempty"` - EvidenceRefs []EvidenceRef `json:"evidence_refs" yaml:"evidence_refs"` - ObservationWindow *ObservationWindow `json:"observation_window,omitempty" yaml:"observation_window,omitempty"` - Summary string `json:"summary" yaml:"summary"` -} - -type FluidHypothesisOutcomeStatus string - -const ( - FluidHypothesisOutcomeStatusCONFIRMED FluidHypothesisOutcomeStatus = "CONFIRMED" - FluidHypothesisOutcomeStatusREFUTED FluidHypothesisOutcomeStatus = "REFUTED" - FluidHypothesisOutcomeStatusINCONCLUSIVE FluidHypothesisOutcomeStatus = "INCONCLUSIVE" -) - -// Valid reports whether v is a defined FluidHypothesisOutcomeStatus. -func (v FluidHypothesisOutcomeStatus) Valid() bool { - switch v { - case FluidHypothesisOutcomeStatusCONFIRMED, FluidHypothesisOutcomeStatusREFUTED, FluidHypothesisOutcomeStatusINCONCLUSIVE: - return true - } - return false -} - -// What happened afterwards. Null until evaluation completes. -type FluidHypothesisOutcome struct { - EvidenceRefs []EvidenceRef `json:"evidence_refs,omitempty" yaml:"evidence_refs,omitempty"` - Status *FluidHypothesisOutcomeStatus `json:"status,omitempty" yaml:"status,omitempty"` - Summary *string `json:"summary,omitempty" yaml:"summary,omitempty"` -} - -type FluidHypothesisPressure struct { - Classes []PressureClass `json:"classes" yaml:"classes"` - Confidence *UnitInterval `json:"confidence,omitempty" yaml:"confidence,omitempty"` - PressureRefs []PressureID `json:"pressure_refs,omitempty" yaml:"pressure_refs,omitempty"` - Severity *UnitInterval `json:"severity,omitempty" yaml:"severity,omitempty"` -} - -type FluidHypothesisPriority struct { - DecidedBy string `json:"decided_by,omitempty" yaml:"decided_by,omitempty"` - Score *float64 `json:"score,omitempty" yaml:"score,omitempty"` -} - -type FluidHypothesisProposedAdaptationImplementationScope string - -const ( - FluidHypothesisProposedAdaptationImplementationScopeInterfaceOnly FluidHypothesisProposedAdaptationImplementationScope = "interface_only" - FluidHypothesisProposedAdaptationImplementationScopeInterfaceAndBackend FluidHypothesisProposedAdaptationImplementationScope = "interface_and_backend" - FluidHypothesisProposedAdaptationImplementationScopeDocumentationOnly FluidHypothesisProposedAdaptationImplementationScope = "documentation_only" -) - -// Valid reports whether v is a defined FluidHypothesisProposedAdaptationImplementationScope. -func (v FluidHypothesisProposedAdaptationImplementationScope) Valid() bool { - switch v { - case FluidHypothesisProposedAdaptationImplementationScopeInterfaceOnly, FluidHypothesisProposedAdaptationImplementationScopeInterfaceAndBackend, FluidHypothesisProposedAdaptationImplementationScopeDocumentationOnly: - return true - } - return false -} - -// What we propose to change. -type FluidHypothesisProposedAdaptation struct { - // Shape is protocol-specific and deliberately unconstrained. - CandidateContract map[string]any `json:"candidate_contract,omitempty" yaml:"candidate_contract,omitempty"` - Class AdaptationClass `json:"class" yaml:"class"` - ImplementationScope *FluidHypothesisProposedAdaptationImplementationScope `json:"implementation_scope,omitempty" yaml:"implementation_scope,omitempty"` - Summary string `json:"summary" yaml:"summary"` -} - -type FluidHypothesisRiskLevel string - -const ( - FluidHypothesisRiskLevelLOW FluidHypothesisRiskLevel = "LOW" - FluidHypothesisRiskLevelMEDIUM FluidHypothesisRiskLevel = "MEDIUM" - FluidHypothesisRiskLevelHIGH FluidHypothesisRiskLevel = "HIGH" - FluidHypothesisRiskLevelCRITICAL FluidHypothesisRiskLevel = "CRITICAL" -) - -// Valid reports whether v is a defined FluidHypothesisRiskLevel. -func (v FluidHypothesisRiskLevel) Valid() bool { - switch v { - case FluidHypothesisRiskLevelLOW, FluidHypothesisRiskLevelMEDIUM, FluidHypothesisRiskLevelHIGH, FluidHypothesisRiskLevelCRITICAL: - return true - } - return false -} - -type FluidHypothesisRisk struct { - Level FluidHypothesisRiskLevel `json:"level" yaml:"level"` - Reasons []string `json:"reasons,omitempty" yaml:"reasons,omitempty"` -} - -type FluidHypothesisState string - -const ( - FluidHypothesisStateDRAFT FluidHypothesisState = "DRAFT" - FluidHypothesisStateREADY FluidHypothesisState = "READY" - FluidHypothesisStatePRIORITIZED FluidHypothesisState = "PRIORITIZED" - FluidHypothesisStateDESIGNING FluidHypothesisState = "DESIGNING" - FluidHypothesisStateEXPERIMENTING FluidHypothesisState = "EXPERIMENTING" - FluidHypothesisStateEVALUATING FluidHypothesisState = "EVALUATING" - FluidHypothesisStateACCEPTED FluidHypothesisState = "ACCEPTED" - FluidHypothesisStateREJECTED FluidHypothesisState = "REJECTED" - FluidHypothesisStateSUPERSEDED FluidHypothesisState = "SUPERSEDED" - FluidHypothesisStateDEFERRED FluidHypothesisState = "DEFERRED" -) - -// Valid reports whether v is a defined FluidHypothesisState. -func (v FluidHypothesisState) Valid() bool { - switch v { - case FluidHypothesisStateDRAFT, FluidHypothesisStateREADY, FluidHypothesisStatePRIORITIZED, FluidHypothesisStateDESIGNING, FluidHypothesisStateEXPERIMENTING, FluidHypothesisStateEVALUATING, FluidHypothesisStateACCEPTED, FluidHypothesisStateREJECTED, FluidHypothesisStateSUPERSEDED, FluidHypothesisStateDEFERRED: - return true - } - return false -} - -type FluidHypothesisSuccessCriteria struct { - Expression string `json:"expression" yaml:"expression"` -} - -type FluidHypothesis struct { - Audit *FluidHypothesisAudit `json:"audit,omitempty" yaml:"audit,omitempty"` - BackendRequirements *FluidHypothesisBackendRequirements `json:"backend_requirements,omitempty" yaml:"backend_requirements,omitempty"` - CandidateRevisionRefs []RevisionID `json:"candidate_revision_refs,omitempty" yaml:"candidate_revision_refs,omitempty"` - - // Competing hypotheses are legitimate (FluidAPIStandards.md section 19). - Competition *FluidHypothesisCompetition `json:"competition,omitempty" yaml:"competition,omitempty"` - Complexity FluidHypothesisComplexity `json:"complexity" yaml:"complexity"` - CreatedAt *Timestamp `json:"created_at,omitempty" yaml:"created_at,omitempty"` - CreatedBy *Actor `json:"created_by,omitempty" yaml:"created_by,omitempty"` - - // A hypothesis may be valuable but not yet worth exploring - // (ArchitectureBlueprint.md section 29). - Economics *FluidHypothesisEconomics `json:"economics,omitempty" yaml:"economics,omitempty"` - - // A hypothesis must be falsifiable (FluidAPIStandards.md section 17). - ExpectedOutcomes []ExpectedOutcome `json:"expected_outcomes" yaml:"expected_outcomes"` - ExperimentRefs []ExperimentID `json:"experiment_refs,omitempty" yaml:"experiment_refs,omitempty"` - - // What we think explains the observation. - Explanation FluidHypothesisExplanation `json:"explanation" yaml:"explanation"` - FailureCriteria *FluidHypothesisFailureCriteria `json:"failure_criteria,omitempty" yaml:"failure_criteria,omitempty"` - FitnessDimensions *FluidHypothesisFitnessDimensions `json:"fitness_dimensions,omitempty" yaml:"fitness_dimensions,omitempty"` - Guardrails []Guardrail `json:"guardrails" yaml:"guardrails"` - ID HypothesisID `json:"id" yaml:"id"` - InterfaceID InterfaceID `json:"interface_id" yaml:"interface_id"` - LearningValue *FluidHypothesisLearningValue `json:"learning_value,omitempty" yaml:"learning_value,omitempty"` - - // What was seen. Not what it means. - Observation FluidHypothesisObservation `json:"observation" yaml:"observation"` - - // What happened afterwards. Null until evaluation completes. - Outcome *FluidHypothesisOutcome `json:"outcome,omitempty" yaml:"outcome,omitempty"` - Pressure FluidHypothesisPressure `json:"pressure" yaml:"pressure"` - Priority *FluidHypothesisPriority `json:"priority,omitempty" yaml:"priority,omitempty"` - - // What we propose to change. - ProposedAdaptation FluidHypothesisProposedAdaptation `json:"proposed_adaptation" yaml:"proposed_adaptation"` - Risk FluidHypothesisRisk `json:"risk" yaml:"risk"` - SchemaVersion SchemaVersion `json:"schema_version" yaml:"schema_version"` - State FluidHypothesisState `json:"state" yaml:"state"` - SuccessCriteria FluidHypothesisSuccessCriteria `json:"success_criteria" yaml:"success_criteria"` - Title string `json:"title" yaml:"title"` -} - -// Why an interface change should be explored. Observation, explanation, prediction, -// intervention and result stay separate on purpose -// (FluidHypothesisRevisionSchema.md section 18). -type HypothesisDocument struct { - FluidHypothesis FluidHypothesis `json:"fluid_hypothesis" yaml:"fluid_hypothesis"` -} diff --git a/internal/contract/ids.go b/internal/contract/ids.go deleted file mode 100644 index 93d6cbc..0000000 --- a/internal/contract/ids.go +++ /dev/null @@ -1,91 +0,0 @@ -package contract - -import ( - "fmt" - "strings" -) - -// Identifier prefixes from FluidHypothesisRevisionSchema.md section 16. -// -// Implementations may use UUIDs internally; these human-readable prefixes exist -// so that operational tooling and audit trails stay legible to people. -const ( - PrefixHypothesis = "H-" - PrefixRevision = "R-" - PrefixExperiment = "E-" - PrefixPressure = "P-" - PrefixBackendRequirement = "BR-" - PrefixDecision = "D-" - PrefixEvent = "EV-" - PrefixFeedback = "F-" - PrefixCohort = "C-" -) - -// EntityKind names the FLUID artifact an identifier refers to. -type EntityKind string - -const ( - KindHypothesis EntityKind = "hypothesis" - KindRevision EntityKind = "revision" - KindExperiment EntityKind = "experiment" - KindPressure EntityKind = "pressure" - KindBackendRequirement EntityKind = "backend_requirement" - KindDecision EntityKind = "decision" - KindEvent EntityKind = "event" - KindFeedback EntityKind = "feedback" - KindCohort EntityKind = "cohort" -) - -// prefixOrder matters: "BR-" must be tested before "B"-less single letters -// would otherwise mis-claim it, and "EV-" before "E-". -var prefixOrder = []struct { - prefix string - kind EntityKind -}{ - {PrefixBackendRequirement, KindBackendRequirement}, - {PrefixEvent, KindEvent}, - {PrefixHypothesis, KindHypothesis}, - {PrefixRevision, KindRevision}, - {PrefixExperiment, KindExperiment}, - {PrefixPressure, KindPressure}, - {PrefixDecision, KindDecision}, - {PrefixFeedback, KindFeedback}, - {PrefixCohort, KindCohort}, -} - -// KindOf reports which FLUID artifact an identifier names. -// -// Audit trails carry bare identifiers across record boundaries, so being able to -// classify one without knowing where it came from keeps trace reconstruction -// from needing a lookup table at every hop. -func KindOf(id string) (EntityKind, bool) { - for _, p := range prefixOrder { - if strings.HasPrefix(id, p.prefix) { - return p.kind, true - } - } - return "", false -} - -// ErrWrongKind reports an identifier used in the wrong position. -type ErrWrongKind struct { - ID string - Want EntityKind - Got EntityKind -} - -func (e *ErrWrongKind) Error() string { - if e.Got == "" { - return fmt.Sprintf("identifier %q has no recognized FLUID prefix, wanted a %s id", e.ID, e.Want) - } - return fmt.Sprintf("identifier %q is a %s id, wanted a %s id", e.ID, e.Got, e.Want) -} - -// RequireKind checks that id names the expected artifact. -func RequireKind(id string, want EntityKind) error { - got, ok := KindOf(id) - if !ok || got != want { - return &ErrWrongKind{ID: id, Want: want, Got: got} - } - return nil -} diff --git a/internal/contract/pressure_gen.go b/internal/contract/pressure_gen.go deleted file mode 100644 index 6735621..0000000 --- a/internal/contract/pressure_gen.go +++ /dev/null @@ -1,58 +0,0 @@ -// Code generated by tools/schemagen. DO NOT EDIT. -// Source: schemas/. Regenerate with `make generate`. - -package contract - -type FluidPressureFrequency struct { - IndependentConsumers *int64 `json:"independent_consumers,omitempty" yaml:"independent_consumers,omitempty"` - Observations *int64 `json:"observations,omitempty" yaml:"observations,omitempty"` -} - -// ArchitectureBlueprint.md section 9. Pressure may remain unresolved on purpose. -type FluidPressureStatus string - -const ( - FluidPressureStatusOPEN FluidPressureStatus = "OPEN" - FluidPressureStatusANALYZING FluidPressureStatus = "ANALYZING" - FluidPressureStatusEXPLAINED FluidPressureStatus = "EXPLAINED" - FluidPressureStatusADDRESSED FluidPressureStatus = "ADDRESSED" - FluidPressureStatusDISMISSED FluidPressureStatus = "DISMISSED" - FluidPressureStatusOUTOFSCOPE FluidPressureStatus = "OUT_OF_SCOPE" -) - -// Valid reports whether v is a defined FluidPressureStatus. -func (v FluidPressureStatus) Valid() bool { - switch v { - case FluidPressureStatusOPEN, FluidPressureStatusANALYZING, FluidPressureStatusEXPLAINED, FluidPressureStatusADDRESSED, FluidPressureStatusDISMISSED, FluidPressureStatusOUTOFSCOPE: - return true - } - return false -} - -type FluidPressure struct { - AffectedCohorts []CohortID `json:"affected_cohorts,omitempty" yaml:"affected_cohorts,omitempty"` - Class PressureClass `json:"class" yaml:"class"` - Confidence *UnitInterval `json:"confidence,omitempty" yaml:"confidence,omitempty"` - - // A pressure record without evidence references is not auditable and must be - // rejected. - EvidenceRefs []EvidenceRef `json:"evidence_refs" yaml:"evidence_refs"` - FirstSeen Timestamp `json:"first_seen" yaml:"first_seen"` - Frequency *FluidPressureFrequency `json:"frequency,omitempty" yaml:"frequency,omitempty"` - ID PressureID `json:"id" yaml:"id"` - InterfaceID InterfaceID `json:"interface_id" yaml:"interface_id"` - LastSeen Timestamp `json:"last_seen" yaml:"last_seen"` - LinkedHypotheses []HypothesisID `json:"linked_hypotheses,omitempty" yaml:"linked_hypotheses,omitempty"` - SchemaVersion SchemaVersion `json:"schema_version" yaml:"schema_version"` - Severity *UnitInterval `json:"severity,omitempty" yaml:"severity,omitempty"` - - // ArchitectureBlueprint.md section 9. Pressure may remain unresolved on purpose. - Status FluidPressureStatus `json:"status" yaml:"status"` - Summary string `json:"summary" yaml:"summary"` -} - -// Evidence that the interface differs materially from consumer needs. Pressure is -// evidence, not truth (FluidAPIStandards.md section 13). -type PressureDocument struct { - FluidPressure FluidPressure `json:"fluid_pressure" yaml:"fluid_pressure"` -} diff --git a/internal/contract/revision_descriptor_gen.go b/internal/contract/revision_descriptor_gen.go deleted file mode 100644 index 7e3f30d..0000000 --- a/internal/contract/revision_descriptor_gen.go +++ /dev/null @@ -1,215 +0,0 @@ -// Code generated by tools/schemagen. DO NOT EDIT. -// Source: schemas/. Regenerate with `make generate`. - -package contract - -type RevisionContractType string - -const ( - RevisionContractTypeOpenapi RevisionContractType = "openapi" - RevisionContractTypeGraphql RevisionContractType = "graphql" - RevisionContractTypeProtobuf RevisionContractType = "protobuf" - RevisionContractTypeAsyncapi RevisionContractType = "asyncapi" - RevisionContractTypeJsonschema RevisionContractType = "jsonschema" - RevisionContractTypeCustom RevisionContractType = "custom" -) - -// Valid reports whether v is a defined RevisionContractType. -func (v RevisionContractType) Valid() bool { - switch v { - case RevisionContractTypeOpenapi, RevisionContractTypeGraphql, RevisionContractTypeProtobuf, RevisionContractTypeAsyncapi, RevisionContractTypeJsonschema, RevisionContractTypeCustom: - return true - } - return false -} - -type RevisionContract struct { - Digest Digest `json:"digest" yaml:"digest"` - - // Where the contract artifact can be fetched. - Source string `json:"source,omitempty" yaml:"source,omitempty"` - Type RevisionContractType `json:"type" yaml:"type"` -} - -// Which interface evolution intent governs this revision. Required: a revision with -// no governing intent cannot be audited. -type RevisionIntent struct { - Digest *Digest `json:"digest,omitempty" yaml:"digest,omitempty"` - Version string `json:"version" yaml:"version"` -} - -type RevisionPolicyCompatibility string - -const ( - RevisionPolicyCompatibilityBackwardCompatible RevisionPolicyCompatibility = "backward_compatible" - RevisionPolicyCompatibilityForwardCompatible RevisionPolicyCompatibility = "forward_compatible" - RevisionPolicyCompatibilityAdditive RevisionPolicyCompatibility = "additive" - RevisionPolicyCompatibilityBehavioralChange RevisionPolicyCompatibility = "behavioral_change" - RevisionPolicyCompatibilityBreaking RevisionPolicyCompatibility = "breaking" - RevisionPolicyCompatibilityMigrationOnly RevisionPolicyCompatibility = "migration_only" - RevisionPolicyCompatibilityInternalOnly RevisionPolicyCompatibility = "internal_only" -) - -// Valid reports whether v is a defined RevisionPolicyCompatibility. -func (v RevisionPolicyCompatibility) Valid() bool { - switch v { - case RevisionPolicyCompatibilityBackwardCompatible, RevisionPolicyCompatibilityForwardCompatible, RevisionPolicyCompatibilityAdditive, RevisionPolicyCompatibilityBehavioralChange, RevisionPolicyCompatibilityBreaking, RevisionPolicyCompatibilityMigrationOnly, RevisionPolicyCompatibilityInternalOnly: - return true - } - return false -} - -type RevisionPolicyPolicyCheck string - -const ( - RevisionPolicyPolicyCheckPending RevisionPolicyPolicyCheck = "pending" - RevisionPolicyPolicyCheckPassed RevisionPolicyPolicyCheck = "passed" - RevisionPolicyPolicyCheckFailed RevisionPolicyPolicyCheck = "failed" -) - -// Valid reports whether v is a defined RevisionPolicyPolicyCheck. -func (v RevisionPolicyPolicyCheck) Valid() bool { - switch v { - case RevisionPolicyPolicyCheckPending, RevisionPolicyPolicyCheckPassed, RevisionPolicyPolicyCheckFailed: - return true - } - return false -} - -type RevisionPolicySecurityCheck string - -const ( - RevisionPolicySecurityCheckPending RevisionPolicySecurityCheck = "pending" - RevisionPolicySecurityCheckPassed RevisionPolicySecurityCheck = "passed" - RevisionPolicySecurityCheckFailed RevisionPolicySecurityCheck = "failed" -) - -// Valid reports whether v is a defined RevisionPolicySecurityCheck. -func (v RevisionPolicySecurityCheck) Valid() bool { - switch v { - case RevisionPolicySecurityCheckPending, RevisionPolicySecurityCheckPassed, RevisionPolicySecurityCheckFailed: - return true - } - return false -} - -type RevisionPolicy struct { - Compatibility RevisionPolicyCompatibility `json:"compatibility" yaml:"compatibility"` - PolicyCheck *RevisionPolicyPolicyCheck `json:"policy_check,omitempty" yaml:"policy_check,omitempty"` - RollbackTo *RevisionID `json:"rollback_to,omitempty" yaml:"rollback_to,omitempty"` - SecurityCheck RevisionPolicySecurityCheck `json:"security_check" yaml:"security_check"` -} - -type RevisionRouting struct { - // Empty or absent means all cohorts are eligible. - EligibleCohorts []CohortID `json:"eligible_cohorts,omitempty" yaml:"eligible_cohorts,omitempty"` - - // Ceiling the router enforces regardless of what a routing policy asks for. - MaxTrafficShare *UnitInterval `json:"max_traffic_share,omitempty" yaml:"max_traffic_share,omitempty"` -} - -type RevisionRuntimeCircuitBreaker struct { - FailureThreshold *int64 `json:"failure_threshold,omitempty" yaml:"failure_threshold,omitempty"` - ResetAfterMS *int64 `json:"reset_after_ms,omitempty" yaml:"reset_after_ms,omitempty"` -} - -type RevisionRuntimeRetry struct { - BackoffMS *int64 `json:"backoff_ms,omitempty" yaml:"backoff_ms,omitempty"` - MaxAttempts *int64 `json:"max_attempts,omitempty" yaml:"max_attempts,omitempty"` - RetryOn []string `json:"retry_on,omitempty" yaml:"retry_on,omitempty"` -} - -// Out-of-process attachment: the adapter is an upstream reached over the network, -// so it may be implemented in any language. -type RevisionRuntime struct { - CircuitBreaker *RevisionRuntimeCircuitBreaker `json:"circuit_breaker,omitempty" yaml:"circuit_breaker,omitempty"` - Digest *Digest `json:"digest,omitempty" yaml:"digest,omitempty"` - Image string `json:"image,omitempty" yaml:"image,omitempty"` - Retry *RevisionRuntimeRetry `json:"retry,omitempty" yaml:"retry,omitempty"` - TimeoutMS *int64 `json:"timeout_ms,omitempty" yaml:"timeout_ms,omitempty"` - - // Base URL of the adapter process serving this revision. - Upstream string `json:"upstream" yaml:"upstream"` -} - -type RevisionSignatureAlgorithm string - -const ( - RevisionSignatureAlgorithmEd25519 RevisionSignatureAlgorithm = "ed25519" -) - -// Valid reports whether v is a defined RevisionSignatureAlgorithm. -func (v RevisionSignatureAlgorithm) Valid() bool { - switch v { - case RevisionSignatureAlgorithmEd25519: - return true - } - return false -} - -// The router accepts only signed published descriptors (ArchitectureBlueprint.md -// section 35). The signature covers the canonical form of this document with the -// signature member removed. -type RevisionSignature struct { - Algorithm RevisionSignatureAlgorithm `json:"algorithm" yaml:"algorithm"` - KeyID string `json:"key_id" yaml:"key_id"` - SignedAt *Timestamp `json:"signed_at,omitempty" yaml:"signed_at,omitempty"` - Value string `json:"value" yaml:"value"` -} - -// The router refuses to route to created, failed or retired revisions -// (ArchitectureBlueprint.md section 5.3). -type RevisionState string - -const ( - RevisionStateCreated RevisionState = "created" - RevisionStateVerified RevisionState = "verified" - RevisionStateExperiment RevisionState = "experiment" - RevisionStateCandidate RevisionState = "candidate" - RevisionStateStable RevisionState = "stable" - RevisionStateDeprecated RevisionState = "deprecated" - RevisionStateRetired RevisionState = "retired" -) - -// Valid reports whether v is a defined RevisionState. -func (v RevisionState) Valid() bool { - switch v { - case RevisionStateCreated, RevisionStateVerified, RevisionStateExperiment, RevisionStateCandidate, RevisionStateStable, RevisionStateDeprecated, RevisionStateRetired: - return true - } - return false -} - -type Revision struct { - Contract RevisionContract `json:"contract" yaml:"contract"` - ID RevisionID `json:"id" yaml:"id"` - - // Which interface evolution intent governs this revision. Required: a revision - // with no governing intent cannot be audited. - Intent RevisionIntent `json:"intent" yaml:"intent"` - Interface InterfaceID `json:"interface" yaml:"interface"` - Policy RevisionPolicy `json:"policy" yaml:"policy"` - Routing *RevisionRouting `json:"routing,omitempty" yaml:"routing,omitempty"` - - // Out-of-process attachment: the adapter is an upstream reached over the network, - // so it may be implemented in any language. - Runtime RevisionRuntime `json:"runtime" yaml:"runtime"` - SchemaVersion SchemaVersion `json:"schema_version" yaml:"schema_version"` - - // The router accepts only signed published descriptors (ArchitectureBlueprint.md - // section 35). The signature covers the canonical form of this document with the - // signature member removed. - Signature *RevisionSignature `json:"signature,omitempty" yaml:"signature,omitempty"` - - // The router refuses to route to created, failed or retired revisions - // (ArchitectureBlueprint.md section 5.3). - State RevisionState `json:"state" yaml:"state"` -} - -// The runtime-facing projection of a revision, deliberately small enough to be -// consumed deterministically by infrastructure (ArchitectureBlueprint.md section -// 36). This is a wire artifact: the gateway must be able to read it without any -// fluid-core library. -type RevisionDescriptorDocument struct { - Revision Revision `json:"revision" yaml:"revision"` -} diff --git a/internal/contract/revision_gen.go b/internal/contract/revision_gen.go deleted file mode 100644 index d327170..0000000 --- a/internal/contract/revision_gen.go +++ /dev/null @@ -1,314 +0,0 @@ -// Code generated by tools/schemagen. DO NOT EDIT. -// Source: schemas/. Regenerate with `make generate`. - -package contract - -// Adoption is evidence, not proof of quality (FluidAPIStandards.md section 32). -type FluidRevisionAdoption struct { - ActiveConsumers *int64 `json:"active_consumers,omitempty" yaml:"active_consumers,omitempty"` - AdoptionRate *UnitInterval `json:"adoption_rate,omitempty" yaml:"adoption_rate,omitempty"` - EligibleConsumers *int64 `json:"eligible_consumers,omitempty" yaml:"eligible_consumers,omitempty"` - RetainedAdoptionRate *UnitInterval `json:"retained_adoption_rate,omitempty" yaml:"retained_adoption_rate,omitempty"` -} - -type FluidRevisionAudit struct { - ImmutableEventRefs []EventID `json:"immutable_event_refs,omitempty" yaml:"immutable_event_refs,omitempty"` -} - -type FluidRevisionCompatibilityClass string - -const ( - FluidRevisionCompatibilityClassBACKWARDCOMPATIBLE FluidRevisionCompatibilityClass = "BACKWARD_COMPATIBLE" - FluidRevisionCompatibilityClassFORWARDCOMPATIBLE FluidRevisionCompatibilityClass = "FORWARD_COMPATIBLE" - FluidRevisionCompatibilityClassADDITIVE FluidRevisionCompatibilityClass = "ADDITIVE" - FluidRevisionCompatibilityClassBEHAVIORALCHANGE FluidRevisionCompatibilityClass = "BEHAVIORAL_CHANGE" - FluidRevisionCompatibilityClassBREAKING FluidRevisionCompatibilityClass = "BREAKING" - FluidRevisionCompatibilityClassMIGRATIONONLY FluidRevisionCompatibilityClass = "MIGRATION_ONLY" - FluidRevisionCompatibilityClassINTERNALONLY FluidRevisionCompatibilityClass = "INTERNAL_ONLY" -) - -// Valid reports whether v is a defined FluidRevisionCompatibilityClass. -func (v FluidRevisionCompatibilityClass) Valid() bool { - switch v { - case FluidRevisionCompatibilityClassBACKWARDCOMPATIBLE, FluidRevisionCompatibilityClassFORWARDCOMPATIBLE, FluidRevisionCompatibilityClassADDITIVE, FluidRevisionCompatibilityClassBEHAVIORALCHANGE, FluidRevisionCompatibilityClassBREAKING, FluidRevisionCompatibilityClassMIGRATIONONLY, FluidRevisionCompatibilityClassINTERNALONLY: - return true - } - return false -} - -type FluidRevisionCompatibility struct { - BreakingChanges []string `json:"breaking_changes,omitempty" yaml:"breaking_changes,omitempty"` - Class FluidRevisionCompatibilityClass `json:"class" yaml:"class"` - CompatibilityEvidenceRefs []EvidenceRef `json:"compatibility_evidence_refs,omitempty" yaml:"compatibility_evidence_refs,omitempty"` - Deprecations []string `json:"deprecations,omitempty" yaml:"deprecations,omitempty"` - Supersedes []RevisionID `json:"supersedes,omitempty" yaml:"supersedes,omitempty"` -} - -type FluidRevisionComplexityBudgetStatus string - -const ( - FluidRevisionComplexityBudgetStatusWITHINBUDGET FluidRevisionComplexityBudgetStatus = "WITHIN_BUDGET" - FluidRevisionComplexityBudgetStatusATBUDGET FluidRevisionComplexityBudgetStatus = "AT_BUDGET" - FluidRevisionComplexityBudgetStatusOVERBUDGET FluidRevisionComplexityBudgetStatus = "OVER_BUDGET" -) - -// Valid reports whether v is a defined FluidRevisionComplexityBudgetStatus. -func (v FluidRevisionComplexityBudgetStatus) Valid() bool { - switch v { - case FluidRevisionComplexityBudgetStatusWITHINBUDGET, FluidRevisionComplexityBudgetStatusATBUDGET, FluidRevisionComplexityBudgetStatusOVERBUDGET: - return true - } - return false -} - -type FluidRevisionComplexity struct { - After *ComplexityDelta `json:"after,omitempty" yaml:"after,omitempty"` - Before *ComplexityDelta `json:"before,omitempty" yaml:"before,omitempty"` - BudgetStatus *FluidRevisionComplexityBudgetStatus `json:"budget_status,omitempty" yaml:"budget_status,omitempty"` - DeltaScore *float64 `json:"delta_score,omitempty" yaml:"delta_score,omitempty"` -} - -type FluidRevisionContractType string - -const ( - FluidRevisionContractTypeOpenapi FluidRevisionContractType = "openapi" - FluidRevisionContractTypeGraphql FluidRevisionContractType = "graphql" - FluidRevisionContractTypeProtobuf FluidRevisionContractType = "protobuf" - FluidRevisionContractTypeAsyncapi FluidRevisionContractType = "asyncapi" - FluidRevisionContractTypeJsonschema FluidRevisionContractType = "jsonschema" - FluidRevisionContractTypeCustom FluidRevisionContractType = "custom" -) - -// Valid reports whether v is a defined FluidRevisionContractType. -func (v FluidRevisionContractType) Valid() bool { - switch v { - case FluidRevisionContractTypeOpenapi, FluidRevisionContractTypeGraphql, FluidRevisionContractTypeProtobuf, FluidRevisionContractTypeAsyncapi, FluidRevisionContractTypeJsonschema, FluidRevisionContractTypeCustom: - return true - } - return false -} - -type FluidRevisionContract struct { - ArtifactRef ArtifactRef `json:"artifact_ref" yaml:"artifact_ref"` - Digest Digest `json:"digest" yaml:"digest"` - Type FluidRevisionContractType `json:"type" yaml:"type"` - Version string `json:"version,omitempty" yaml:"version,omitempty"` -} - -type FluidRevisionDeploymentCurrentExposure struct { - Cohorts []CohortID `json:"cohorts,omitempty" yaml:"cohorts,omitempty"` - TrafficShare *UnitInterval `json:"traffic_share,omitempty" yaml:"traffic_share,omitempty"` -} - -type FluidRevisionDeploymentEnvironmentsItem struct { - Name string `json:"name" yaml:"name"` - RoutingPolicyRef string `json:"routing_policy_ref,omitempty" yaml:"routing_policy_ref,omitempty"` - StartedAt *Timestamp `json:"started_at,omitempty" yaml:"started_at,omitempty"` -} - -type FluidRevisionDeployment struct { - CurrentExposure *FluidRevisionDeploymentCurrentExposure `json:"current_exposure,omitempty" yaml:"current_exposure,omitempty"` - Environments []FluidRevisionDeploymentEnvironmentsItem `json:"environments,omitempty" yaml:"environments,omitempty"` -} - -type FluidRevisionEconomics struct { - BuildCost *float64 `json:"build_cost,omitempty" yaml:"build_cost,omitempty"` - Currency string `json:"currency,omitempty" yaml:"currency,omitempty"` - ExperimentCostToDate *float64 `json:"experiment_cost_to_date,omitempty" yaml:"experiment_cost_to_date,omitempty"` -} - -type FluidRevisionFitnessMetricsValue struct { - Baseline *float64 `json:"baseline,omitempty" yaml:"baseline,omitempty"` - Current *float64 `json:"current,omitempty" yaml:"current,omitempty"` - Guardrail *float64 `json:"guardrail,omitempty" yaml:"guardrail,omitempty"` - Target *float64 `json:"target,omitempty" yaml:"target,omitempty"` -} - -type FluidRevisionFitness struct { - BaselineRevision *RevisionID `json:"baseline_revision,omitempty" yaml:"baseline_revision,omitempty"` - MeasurementWindow *ObservationWindow `json:"measurement_window,omitempty" yaml:"measurement_window,omitempty"` - Metrics map[string]FluidRevisionFitnessMetricsValue `json:"metrics,omitempty" yaml:"metrics,omitempty"` -} - -type FluidRevisionImplementation struct { - ArtifactRef ArtifactRef `json:"artifact_ref" yaml:"artifact_ref"` - BuildRef string `json:"build_ref,omitempty" yaml:"build_ref,omitempty"` - Digest Digest `json:"digest" yaml:"digest"` - SourceRef string `json:"source_ref,omitempty" yaml:"source_ref,omitempty"` -} - -// Every revision is governed by a specific intent version, so audit can ask whether -// the change was valid under the intent that existed at the time -// (ArchitectureBlueprint.md section 27). -type FluidRevisionInterfaceEvolutionIntent struct { - ArtifactRef *ArtifactRef `json:"artifact_ref,omitempty" yaml:"artifact_ref,omitempty"` - Version string `json:"version" yaml:"version"` -} - -type FluidRevisionPromotionRecommendedState string - -const ( - FluidRevisionPromotionRecommendedStateCREATED FluidRevisionPromotionRecommendedState = "CREATED" - FluidRevisionPromotionRecommendedStateVERIFIED FluidRevisionPromotionRecommendedState = "VERIFIED" - FluidRevisionPromotionRecommendedStateEXPERIMENT FluidRevisionPromotionRecommendedState = "EXPERIMENT" - FluidRevisionPromotionRecommendedStateCANDIDATE FluidRevisionPromotionRecommendedState = "CANDIDATE" - FluidRevisionPromotionRecommendedStateSTABLE FluidRevisionPromotionRecommendedState = "STABLE" - FluidRevisionPromotionRecommendedStateDEPRECATED FluidRevisionPromotionRecommendedState = "DEPRECATED" - FluidRevisionPromotionRecommendedStateRETIRED FluidRevisionPromotionRecommendedState = "RETIRED" -) - -// Valid reports whether v is a defined FluidRevisionPromotionRecommendedState. -func (v FluidRevisionPromotionRecommendedState) Valid() bool { - switch v { - case FluidRevisionPromotionRecommendedStateCREATED, FluidRevisionPromotionRecommendedStateVERIFIED, FluidRevisionPromotionRecommendedStateEXPERIMENT, FluidRevisionPromotionRecommendedStateCANDIDATE, FluidRevisionPromotionRecommendedStateSTABLE, FluidRevisionPromotionRecommendedStateDEPRECATED, FluidRevisionPromotionRecommendedStateRETIRED: - return true - } - return false -} - -type FluidRevisionPromotion struct { - AuthorizedAt *Timestamp `json:"authorized_at,omitempty" yaml:"authorized_at,omitempty"` - AuthorizedBy *Actor `json:"authorized_by,omitempty" yaml:"authorized_by,omitempty"` - Eligible *bool `json:"eligible,omitempty" yaml:"eligible,omitempty"` - RecommendationReason *string `json:"recommendation_reason,omitempty" yaml:"recommendation_reason,omitempty"` - RecommendedState *FluidRevisionPromotionRecommendedState `json:"recommended_state,omitempty" yaml:"recommended_state,omitempty"` -} - -type FluidRevisionProvenance struct { - DecisionRefs []DecisionID `json:"decision_refs,omitempty" yaml:"decision_refs,omitempty"` - ExperimentRefs []ExperimentID `json:"experiment_refs,omitempty" yaml:"experiment_refs,omitempty"` - TelemetryRefs []EvidenceRef `json:"telemetry_refs,omitempty" yaml:"telemetry_refs,omitempty"` -} - -type FluidRevisionRollback struct { - ProcedureRef string `json:"procedure_ref,omitempty" yaml:"procedure_ref,omitempty"` - Supported bool `json:"supported" yaml:"supported"` - TargetRevision *RevisionID `json:"target_revision,omitempty" yaml:"target_revision,omitempty"` -} - -type FluidRevisionState string - -const ( - FluidRevisionStateCREATED FluidRevisionState = "CREATED" - FluidRevisionStateVERIFIED FluidRevisionState = "VERIFIED" - FluidRevisionStateEXPERIMENT FluidRevisionState = "EXPERIMENT" - FluidRevisionStateCANDIDATE FluidRevisionState = "CANDIDATE" - FluidRevisionStateSTABLE FluidRevisionState = "STABLE" - FluidRevisionStateDEPRECATED FluidRevisionState = "DEPRECATED" - FluidRevisionStateRETIRED FluidRevisionState = "RETIRED" -) - -// Valid reports whether v is a defined FluidRevisionState. -func (v FluidRevisionState) Valid() bool { - switch v { - case FluidRevisionStateCREATED, FluidRevisionStateVERIFIED, FluidRevisionStateEXPERIMENT, FluidRevisionStateCANDIDATE, FluidRevisionStateSTABLE, FluidRevisionStateDEPRECATED, FluidRevisionStateRETIRED: - return true - } - return false -} - -type FluidRevisionVerificationPolicyCheck string - -const ( - FluidRevisionVerificationPolicyCheckPENDING FluidRevisionVerificationPolicyCheck = "PENDING" - FluidRevisionVerificationPolicyCheckPASSED FluidRevisionVerificationPolicyCheck = "PASSED" - FluidRevisionVerificationPolicyCheckFAILED FluidRevisionVerificationPolicyCheck = "FAILED" -) - -// Valid reports whether v is a defined FluidRevisionVerificationPolicyCheck. -func (v FluidRevisionVerificationPolicyCheck) Valid() bool { - switch v { - case FluidRevisionVerificationPolicyCheckPENDING, FluidRevisionVerificationPolicyCheckPASSED, FluidRevisionVerificationPolicyCheckFAILED: - return true - } - return false -} - -type FluidRevisionVerificationSecurityCheck string - -const ( - FluidRevisionVerificationSecurityCheckPENDING FluidRevisionVerificationSecurityCheck = "PENDING" - FluidRevisionVerificationSecurityCheckPASSED FluidRevisionVerificationSecurityCheck = "PASSED" - FluidRevisionVerificationSecurityCheckFAILED FluidRevisionVerificationSecurityCheck = "FAILED" -) - -// Valid reports whether v is a defined FluidRevisionVerificationSecurityCheck. -func (v FluidRevisionVerificationSecurityCheck) Valid() bool { - switch v { - case FluidRevisionVerificationSecurityCheckPENDING, FluidRevisionVerificationSecurityCheckPASSED, FluidRevisionVerificationSecurityCheckFAILED: - return true - } - return false -} - -type FluidRevisionVerificationStatus string - -const ( - FluidRevisionVerificationStatusPENDING FluidRevisionVerificationStatus = "PENDING" - FluidRevisionVerificationStatusPASSED FluidRevisionVerificationStatus = "PASSED" - FluidRevisionVerificationStatusFAILED FluidRevisionVerificationStatus = "FAILED" -) - -// Valid reports whether v is a defined FluidRevisionVerificationStatus. -func (v FluidRevisionVerificationStatus) Valid() bool { - switch v { - case FluidRevisionVerificationStatusPENDING, FluidRevisionVerificationStatusPASSED, FluidRevisionVerificationStatusFAILED: - return true - } - return false -} - -// The primary safety barrier between adaptive generation and deterministic runtime -// (ArchitectureBlueprint.md section 15). -type FluidRevisionVerification struct { - PolicyCheck *FluidRevisionVerificationPolicyCheck `json:"policy_check,omitempty" yaml:"policy_check,omitempty"` - SecurityCheck *FluidRevisionVerificationSecurityCheck `json:"security_check,omitempty" yaml:"security_check,omitempty"` - Status FluidRevisionVerificationStatus `json:"status" yaml:"status"` - TestRefs []string `json:"test_refs,omitempty" yaml:"test_refs,omitempty"` -} - -type FluidRevision struct { - AdaptationClasses []AdaptationClass `json:"adaptation_classes,omitempty" yaml:"adaptation_classes,omitempty"` - - // Adoption is evidence, not proof of quality (FluidAPIStandards.md section 32). - Adoption *FluidRevisionAdoption `json:"adoption,omitempty" yaml:"adoption,omitempty"` - Audit *FluidRevisionAudit `json:"audit,omitempty" yaml:"audit,omitempty"` - BackendRequirements []BackendRequirementID `json:"backend_requirements,omitempty" yaml:"backend_requirements,omitempty"` - Compatibility FluidRevisionCompatibility `json:"compatibility" yaml:"compatibility"` - Complexity *FluidRevisionComplexity `json:"complexity,omitempty" yaml:"complexity,omitempty"` - Contract FluidRevisionContract `json:"contract" yaml:"contract"` - CreatedAt *Timestamp `json:"created_at,omitempty" yaml:"created_at,omitempty"` - CreatedBy *Actor `json:"created_by,omitempty" yaml:"created_by,omitempty"` - Deployment *FluidRevisionDeployment `json:"deployment,omitempty" yaml:"deployment,omitempty"` - Economics *FluidRevisionEconomics `json:"economics,omitempty" yaml:"economics,omitempty"` - Fitness *FluidRevisionFitness `json:"fitness,omitempty" yaml:"fitness,omitempty"` - ID RevisionID `json:"id" yaml:"id"` - Implementation FluidRevisionImplementation `json:"implementation" yaml:"implementation"` - - // Every revision is governed by a specific intent version, so audit can ask - // whether the change was valid under the intent that existed at the time - // (ArchitectureBlueprint.md section 27). - InterfaceEvolutionIntent FluidRevisionInterfaceEvolutionIntent `json:"interface_evolution_intent" yaml:"interface_evolution_intent"` - InterfaceID InterfaceID `json:"interface_id" yaml:"interface_id"` - OriginatingHypotheses []HypothesisID `json:"originating_hypotheses,omitempty" yaml:"originating_hypotheses,omitempty"` - - // Null marks genesis. - ParentRevision *RevisionID `json:"parent_revision" yaml:"parent_revision"` - Promotion *FluidRevisionPromotion `json:"promotion,omitempty" yaml:"promotion,omitempty"` - Provenance FluidRevisionProvenance `json:"provenance" yaml:"provenance"` - RevisionNumber int64 `json:"revision_number" yaml:"revision_number"` - Rollback FluidRevisionRollback `json:"rollback" yaml:"rollback"` - SchemaVersion SchemaVersion `json:"schema_version" yaml:"schema_version"` - State FluidRevisionState `json:"state" yaml:"state"` - - // The primary safety barrier between adaptive generation and deterministic runtime - // (ArchitectureBlueprint.md section 15). - Verification FluidRevisionVerification `json:"verification" yaml:"verification"` -} - -// What deterministic interface state was built, verified, exposed and measured. -// Published revisions should be immutable; corrections create successors -// (FluidAPIStandards.md section 8). -type RevisionDocument struct { - FluidRevision FluidRevision `json:"fluid_revision" yaml:"fluid_revision"` -} diff --git a/internal/contract/routing_policy_gen.go b/internal/contract/routing_policy_gen.go deleted file mode 100644 index 35d1e01..0000000 --- a/internal/contract/routing_policy_gen.go +++ /dev/null @@ -1,82 +0,0 @@ -// Code generated by tools/schemagen. DO NOT EDIT. -// Source: schemas/. Regenerate with `make generate`. - -package contract - -// What keeps a long-lived consumer on one side of an experiment. -type RoutingPolicyRulesItemStickyBy string - -const ( - RoutingPolicyRulesItemStickyByConsumerID RoutingPolicyRulesItemStickyBy = "consumer_id" - RoutingPolicyRulesItemStickyByTenant RoutingPolicyRulesItemStickyBy = "tenant" - RoutingPolicyRulesItemStickyByCorrelationID RoutingPolicyRulesItemStickyBy = "correlation_id" - RoutingPolicyRulesItemStickyByNone RoutingPolicyRulesItemStickyBy = "none" -) - -// Valid reports whether v is a defined RoutingPolicyRulesItemStickyBy. -func (v RoutingPolicyRulesItemStickyBy) Valid() bool { - switch v { - case RoutingPolicyRulesItemStickyByConsumerID, RoutingPolicyRulesItemStickyByTenant, RoutingPolicyRulesItemStickyByCorrelationID, RoutingPolicyRulesItemStickyByNone: - return true - } - return false -} - -type RoutingPolicyRulesItem struct { - // Revision id to traffic share. Shares must sum to 1. Assignment of a given - // consumer must be stable across requests. - Allocation map[string]UnitInterval `json:"allocation" yaml:"allocation"` - Cohort *CohortID `json:"cohort,omitempty" yaml:"cohort,omitempty"` - Experiment *ExperimentID `json:"experiment,omitempty" yaml:"experiment,omitempty"` - - // What keeps a long-lived consumer on one side of an experiment. - StickyBy *RoutingPolicyRulesItemStickyBy `json:"sticky_by,omitempty" yaml:"sticky_by,omitempty"` - Tenant string `json:"tenant,omitempty" yaml:"tenant,omitempty"` -} - -type RoutingPolicySignatureAlgorithm string - -const ( - RoutingPolicySignatureAlgorithmEd25519 RoutingPolicySignatureAlgorithm = "ed25519" -) - -// Valid reports whether v is a defined RoutingPolicySignatureAlgorithm. -func (v RoutingPolicySignatureAlgorithm) Valid() bool { - switch v { - case RoutingPolicySignatureAlgorithmEd25519: - return true - } - return false -} - -type RoutingPolicySignature struct { - Algorithm RoutingPolicySignatureAlgorithm `json:"algorithm" yaml:"algorithm"` - KeyID string `json:"key_id" yaml:"key_id"` - Value string `json:"value" yaml:"value"` -} - -type RoutingPolicy struct { - // Where a request lands when no earlier resolution step matched. - DefaultRevision RevisionID `json:"default_revision" yaml:"default_revision"` - - // Monotonic. The router ignores a policy older than the one it holds. - Generation int64 `json:"generation" yaml:"generation"` - ID string `json:"id,omitempty" yaml:"id,omitempty"` - Interface InterfaceID `json:"interface" yaml:"interface"` - IssuedAt *Timestamp `json:"issued_at,omitempty" yaml:"issued_at,omitempty"` - IssuedBy *Actor `json:"issued_by,omitempty" yaml:"issued_by,omitempty"` - - // Evaluated in order; the first matching rule wins. Matching must be a pure - // function of the request and its cohort assignment. - Rules []RoutingPolicyRulesItem `json:"rules" yaml:"rules"` - SchemaVersion SchemaVersion `json:"schema_version" yaml:"schema_version"` - Signature *RoutingPolicySignature `json:"signature,omitempty" yaml:"signature,omitempty"` -} - -// Deterministic routing written by the experiment controller and consumed by the -// revision router. The controller never touches traffic itself; this separation -// keeps experimental intent out of the runtime decision mechanism -// (ArchitectureBlueprint.md section 17). -type RoutingPolicyDocument struct { - RoutingPolicy RoutingPolicy `json:"routing_policy" yaml:"routing_policy"` -} diff --git a/internal/contract/telemetry_envelope_gen.go b/internal/contract/telemetry_envelope_gen.go deleted file mode 100644 index 967bb6c..0000000 --- a/internal/contract/telemetry_envelope_gen.go +++ /dev/null @@ -1,172 +0,0 @@ -// Code generated by tools/schemagen. DO NOT EDIT. -// Source: schemas/. Regenerate with `make generate`. - -package contract - -type FluidTelemetryAdoptionEvent string - -const ( - FluidTelemetryAdoptionEventFirstUse FluidTelemetryAdoptionEvent = "first_use" - FluidTelemetryAdoptionEventContinuedUse FluidTelemetryAdoptionEvent = "continued_use" - FluidTelemetryAdoptionEventMigration FluidTelemetryAdoptionEvent = "migration" - FluidTelemetryAdoptionEventReversion FluidTelemetryAdoptionEvent = "reversion" - FluidTelemetryAdoptionEventDeprecationResponse FluidTelemetryAdoptionEvent = "deprecation_response" -) - -// Valid reports whether v is a defined FluidTelemetryAdoptionEvent. -func (v FluidTelemetryAdoptionEvent) Valid() bool { - switch v { - case FluidTelemetryAdoptionEventFirstUse, FluidTelemetryAdoptionEventContinuedUse, FluidTelemetryAdoptionEventMigration, FluidTelemetryAdoptionEventReversion, FluidTelemetryAdoptionEventDeprecationResponse: - return true - } - return false -} - -type FluidTelemetryAdoption struct { - Event FluidTelemetryAdoptionEvent `json:"event" yaml:"event"` - FromRevision *RevisionID `json:"from_revision,omitempty" yaml:"from_revision,omitempty"` -} - -type FluidTelemetryErrorClass string - -const ( - FluidTelemetryErrorClassValidation FluidTelemetryErrorClass = "validation" - FluidTelemetryErrorClassUnknownPath FluidTelemetryErrorClass = "unknown_path" - FluidTelemetryErrorClassUnknownField FluidTelemetryErrorClass = "unknown_field" - FluidTelemetryErrorClassUnsupportedParameter FluidTelemetryErrorClass = "unsupported_parameter" - FluidTelemetryErrorClassAuthorization FluidTelemetryErrorClass = "authorization" - FluidTelemetryErrorClassBackendFailure FluidTelemetryErrorClass = "backend_failure" - FluidTelemetryErrorClassTimeout FluidTelemetryErrorClass = "timeout" - FluidTelemetryErrorClassPolicyRejection FluidTelemetryErrorClass = "policy_rejection" -) - -// Valid reports whether v is a defined FluidTelemetryErrorClass. -func (v FluidTelemetryErrorClass) Valid() bool { - switch v { - case FluidTelemetryErrorClassValidation, FluidTelemetryErrorClassUnknownPath, FluidTelemetryErrorClassUnknownField, FluidTelemetryErrorClassUnsupportedParameter, FluidTelemetryErrorClassAuthorization, FluidTelemetryErrorClassBackendFailure, FluidTelemetryErrorClassTimeout, FluidTelemetryErrorClassPolicyRejection: - return true - } - return false -} - -type FluidTelemetryError struct { - Class FluidTelemetryErrorClass `json:"class" yaml:"class"` - - // Redacted. Must not carry backend internals or consumer data. - Detail string `json:"detail,omitempty" yaml:"detail,omitempty"` - Field string `json:"field,omitempty" yaml:"field,omitempty"` -} - -// ArchitectureBlueprint.md section 6.1. -type FluidTelemetryKind string - -const ( - FluidTelemetryKindRequest FluidTelemetryKind = "request" - FluidTelemetryKindError FluidTelemetryKind = "error" - FluidTelemetryKindSequence FluidTelemetryKind = "sequence" - FluidTelemetryKindAdoption FluidTelemetryKind = "adoption" - FluidTelemetryKindFeedback FluidTelemetryKind = "feedback" -) - -// Valid reports whether v is a defined FluidTelemetryKind. -func (v FluidTelemetryKind) Valid() bool { - switch v { - case FluidTelemetryKindRequest, FluidTelemetryKindError, FluidTelemetryKindSequence, FluidTelemetryKindAdoption, FluidTelemetryKindFeedback: - return true - } - return false -} - -// What the privacy filter removed. Recorded so analysis knows what it cannot see. -type FluidTelemetryRedaction struct { - Applied bool `json:"applied" yaml:"applied"` - Rules []string `json:"rules,omitempty" yaml:"rules,omitempty"` -} - -type FluidTelemetryRequest struct { - LatencyMS *float64 `json:"latency_ms,omitempty" yaml:"latency_ms,omitempty"` - Method string `json:"method,omitempty" yaml:"method,omitempty"` - Operation string `json:"operation,omitempty" yaml:"operation,omitempty"` - RequestBytes *int64 `json:"request_bytes,omitempty" yaml:"request_bytes,omitempty"` - ResponseBytes *int64 `json:"response_bytes,omitempty" yaml:"response_bytes,omitempty"` - Route string `json:"route,omitempty" yaml:"route,omitempty"` - Status *int64 `json:"status,omitempty" yaml:"status,omitempty"` -} - -type FluidTelemetryResolutionReason string - -const ( - FluidTelemetryResolutionReasonExplicitRevision FluidTelemetryResolutionReason = "explicit_revision" - FluidTelemetryResolutionReasonBoundContract FluidTelemetryResolutionReason = "bound_contract" - FluidTelemetryResolutionReasonExperimentAssignment FluidTelemetryResolutionReason = "experiment_assignment" - FluidTelemetryResolutionReasonCohortRule FluidTelemetryResolutionReason = "cohort_rule" - FluidTelemetryResolutionReasonStableDefault FluidTelemetryResolutionReason = "stable_default" -) - -// Valid reports whether v is a defined FluidTelemetryResolutionReason. -func (v FluidTelemetryResolutionReason) Valid() bool { - switch v { - case FluidTelemetryResolutionReasonExplicitRevision, FluidTelemetryResolutionReasonBoundContract, FluidTelemetryResolutionReasonExperimentAssignment, FluidTelemetryResolutionReasonCohortRule, FluidTelemetryResolutionReasonStableDefault: - return true - } - return false -} - -// Why this revision was chosen. Revision resolution must be deterministic and -// auditable (ArchitectureBlueprint.md section 5.2). -type FluidTelemetryResolution struct { - PolicyGeneration *int64 `json:"policy_generation,omitempty" yaml:"policy_generation,omitempty"` - Reason FluidTelemetryResolutionReason `json:"reason" yaml:"reason"` -} - -// Interaction topology is often more informative than error counts -// (ArchitectureBlueprint.md section 6.4). -type FluidTelemetrySequence struct { - ChainID string `json:"chain_id,omitempty" yaml:"chain_id,omitempty"` - Pattern string `json:"pattern,omitempty" yaml:"pattern,omitempty"` - Position *int64 `json:"position,omitempty" yaml:"position,omitempty"` - RepeatCount *int64 `json:"repeat_count,omitempty" yaml:"repeat_count,omitempty"` -} - -type FluidTelemetry struct { - Adoption *FluidTelemetryAdoption `json:"adoption,omitempty" yaml:"adoption,omitempty"` - Cohort *CohortID `json:"cohort,omitempty" yaml:"cohort,omitempty"` - - // Pseudonymous and stable. Never a raw end-user identifier. - ConsumerRef string `json:"consumer_ref,omitempty" yaml:"consumer_ref,omitempty"` - - // Ties an event to the response the consumer saw, and to the other events in its - // call chain. - CorrelationID string `json:"correlation_id,omitempty" yaml:"correlation_id,omitempty"` - Error *FluidTelemetryError `json:"error,omitempty" yaml:"error,omitempty"` - Experiment *ExperimentID `json:"experiment,omitempty" yaml:"experiment,omitempty"` - FeedbackRef *FeedbackID `json:"feedback_ref,omitempty" yaml:"feedback_ref,omitempty"` - ID string `json:"id" yaml:"id"` - InterfaceID InterfaceID `json:"interface_id" yaml:"interface_id"` - - // ArchitectureBlueprint.md section 6.1. - Kind FluidTelemetryKind `json:"kind" yaml:"kind"` - OccurredAt Timestamp `json:"occurred_at" yaml:"occurred_at"` - - // What the privacy filter removed. Recorded so analysis knows what it cannot see. - Redaction *FluidTelemetryRedaction `json:"redaction,omitempty" yaml:"redaction,omitempty"` - Request *FluidTelemetryRequest `json:"request,omitempty" yaml:"request,omitempty"` - - // Why this revision was chosen. Revision resolution must be deterministic and - // auditable (ArchitectureBlueprint.md section 5.2). - Resolution *FluidTelemetryResolution `json:"resolution,omitempty" yaml:"resolution,omitempty"` - Revision *RevisionID `json:"revision,omitempty" yaml:"revision,omitempty"` - SchemaVersion SchemaVersion `json:"schema_version" yaml:"schema_version"` - - // Interaction topology is often more informative than error counts - // (ArchitectureBlueprint.md section 6.4). - Sequence *FluidTelemetrySequence `json:"sequence,omitempty" yaml:"sequence,omitempty"` -} - -// The normalized interaction event. Designed for interface learning, not -// unrestricted behavioral capture: raw payload capture is never the default and -// redaction happens before an event reaches the store (ArchitectureBlueprint.md -// section 6.2). -type TelemetryEnvelopeDocument struct { - FluidTelemetry FluidTelemetry `json:"fluid_telemetry" yaml:"fluid_telemetry"` -} diff --git a/schemas/backend-requirement.schema.json b/schemas/backend-requirement.schema.json deleted file mode 100644 index 697b17e..0000000 --- a/schemas/backend-requirement.schema.json +++ /dev/null @@ -1,83 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://fluid.dev/schemas/backend-requirement.schema.json", - "title": "FLUID backend capability requirement", - "description": "Capability the interface needs but has no authority to build. FLUID escalates rather than crossing the boundary (FluidAPIStandards.md section 30).", - "type": "object", - "properties": { - "fluid_backend_requirement": { - "type": "object", - "properties": { - "schema_version": { "$ref": "common.schema.json#/$defs/schemaVersion" }, - "id": { "$ref": "common.schema.json#/$defs/backendRequirementId" }, - "originating_interface": { "$ref": "common.schema.json#/$defs/interfaceId" }, - "originating_revision": { "oneOf": [{ "$ref": "common.schema.json#/$defs/revisionId" }, { "type": "null" }] }, - "originating_hypothesis": { "oneOf": [{ "$ref": "common.schema.json#/$defs/hypothesisId" }, { "type": "null" }] }, - "backend_service": { "type": "string", "minLength": 1 }, - - "capability": { - "type": "object", - "properties": { - "title": { "type": "string", "minLength": 1 }, - "description": { "type": "string", "minLength": 1 } - }, - "required": ["title", "description"], - "additionalProperties": false - }, - - "semantics": { - "type": "object", - "properties": { - "consistency": { "type": "string" }, - "required_fields": { "type": "array", "items": { "type": "string" } } - }, - "additionalProperties": true - }, - - "quality": { - "type": "object", - "properties": { - "p95_latency_ms": { "type": "number", "minimum": 0 }, - "availability": { "$ref": "common.schema.json#/$defs/unitInterval" } - }, - "additionalProperties": true - }, - - "security": { - "type": "object", - "properties": { - "authorization_scope": { "type": "string" }, - "tenant_isolation": { "type": "string" } - }, - "additionalProperties": true - }, - - "expected_usage": { - "type": "object", - "properties": { - "requests_per_day": { "type": "integer", "minimum": 0 } - }, - "additionalProperties": true - }, - - "urgency": { "enum": ["LOW", "MEDIUM", "HIGH"] }, - - "disposition": { - "type": "object", - "description": "The backend decides. Repeated OUT_OF_SCOPE feeds boundary learning (ArchitectureBlueprint.md section 22).", - "properties": { - "state": { "enum": ["OPEN", "ACCEPTED", "PLANNED", "AVAILABLE", "PARTIALLY_AVAILABLE", "OUT_OF_SCOPE", "REJECTED", "SUPERSEDED"] }, - "reason": { "type": ["string", "null"] }, - "target_ref": { "type": ["string", "null"] } - }, - "required": ["state"], - "additionalProperties": false - } - }, - "required": ["schema_version", "id", "originating_interface", "backend_service", "capability", "disposition"], - "additionalProperties": false - } - }, - "required": ["fluid_backend_requirement"], - "additionalProperties": false -} diff --git a/schemas/common.schema.json b/schemas/common.schema.json deleted file mode 100644 index 06e67fe..0000000 --- a/schemas/common.schema.json +++ /dev/null @@ -1,161 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://fluid.dev/schemas/common.schema.json", - "title": "FLUID common definitions", - "description": "Shared identifier, reference and measurement types. Identifier prefixes follow FluidHypothesisRevisionSchema.md section 16.", - "$defs": { - "schemaVersion": { - "type": "string", - "const": "0.1" - }, - "interfaceId": { - "type": "string", - "minLength": 1, - "pattern": "^[a-z0-9]([a-z0-9-]*[a-z0-9])?$", - "description": "Stable identifier of the owning interface." - }, - "hypothesisId": { "type": "string", "pattern": "^H-[0-9A-Za-z_-]+$" }, - "revisionId": { "type": "string", "pattern": "^R-[0-9A-Za-z_-]+$" }, - "experimentId": { "type": "string", "pattern": "^E-[0-9A-Za-z_-]+$" }, - "pressureId": { "type": "string", "pattern": "^P-[0-9A-Za-z_-]+$" }, - "backendRequirementId": { "type": "string", "pattern": "^BR-[0-9A-Za-z_-]+$" }, - "decisionId": { "type": "string", "pattern": "^D-[0-9A-Za-z_-]+$" }, - "eventId": { "type": "string", "pattern": "^EV-[0-9A-Za-z_-]+$" }, - "feedbackId": { "type": "string", "pattern": "^F-[0-9A-Za-z_-]+$" }, - "cohortId": { "type": "string", "minLength": 1 }, - - "timestamp": { "type": "string", "format": "date-time" }, - - "evidenceRef": { - "type": "string", - "minLength": 1, - "description": "Opaque reference into the evidence store, conventionally ':' such as 'telemetry:invoice-pattern-2026w36'." - }, - "artifactRef": { - "type": "string", - "minLength": 1, - "description": "Opaque reference into the artifact store." - }, - "digest": { - "type": "string", - "pattern": "^sha256:[a-f0-9]{64}$", - "description": "Content address of an artifact." - }, - - "actor": { - "type": "object", - "description": "Who or what performed an action. FLUID distinguishes these because trust levels differ (ArchitectureBlueprint.md section 47).", - "properties": { - "type": { "enum": ["human", "daimon", "policy", "system", "consumer"] }, - "id": { "type": "string", "minLength": 1 }, - "model_or_agent": { "type": "string" } - }, - "required": ["type", "id"], - "additionalProperties": false - }, - - "observationWindow": { - "type": "object", - "properties": { - "start": { "$ref": "common.schema.json#/$defs/timestamp" }, - "end": { - "oneOf": [ - { "$ref": "common.schema.json#/$defs/timestamp" }, - { "type": "null" } - ], - "description": "Null while the window is still open." - } - }, - "required": ["start"], - "additionalProperties": false - }, - - "unitInterval": { "type": "number", "minimum": 0, "maximum": 1 }, - - "pressureClass": { - "enum": [ - "natural_usage", - "successful_but_inefficient_usage", - "recoverable_misunderstanding", - "repeated_expectation_mismatch", - "poor_discoverability", - "missing_interface_capability", - "missing_backend_capability", - "out_of_scope_demand", - "prohibited_demand", - "implementation_failure" - ], - "description": "FluidAPIStandards.md section 13." - }, - - "adaptationClass": { - "enum": ["presentation", "contract", "composition", "implementation", "requirement_escalation"], - "description": "FluidAPIStandards.md section 16." - }, - - "guardrail": { - "type": "object", - "description": "A constraint that must not regress beyond threshold. Guardrails are evaluated deterministically.", - "properties": { - "metric": { "type": "string", "minLength": 1 }, - "operator": { "enum": ["<", "<=", "==", "!=", ">=", ">"] }, - "threshold": { "type": ["number", "boolean", "string"] } - }, - "required": ["metric", "operator", "threshold"], - "additionalProperties": false - }, - - "expectedOutcome": { - "type": "object", - "properties": { - "metric": { "type": "string", "minLength": 1 }, - "cohort": { "type": "string" }, - "baseline": { "type": "number" }, - "target": { "type": "number" }, - "direction": { "enum": ["lower", "higher", "unchanged"] } - }, - "required": ["metric", "target", "direction"], - "additionalProperties": false - }, - - "complexityDelta": { - "type": "object", - "description": "Standard dimensions, implementation-defined scoring (FluidAPIStandards.md section 22).", - "properties": { - "surface_area": { "type": "number" }, - "concept_count": { "type": "number" }, - "operation_count": { "type": "number" }, - "parameter_dimensionality": { "type": "number" }, - "semantic_overlap": { "type": "number" }, - "alternative_path_count": { "type": "number" }, - "dependency_count": { "type": "number" }, - "backend_composition_count": { "type": "number" }, - "exception_count": { "type": "number" }, - "active_revision_count": { "type": "number" }, - "cognitive_load": { "type": "number" } - }, - "additionalProperties": false - }, - - "fitnessDimensions": { - "type": "object", - "description": "FluidAPIStandards.md section 20. No universal scalar is defined on purpose.", - "properties": { - "client_utility": { "type": "number" }, - "correctness": { "type": "number" }, - "reliability": { "type": "number" }, - "availability": { "type": "number" }, - "performance": { "type": "number" }, - "discoverability": { "type": "number" }, - "simplicity": { "type": "number" }, - "compatibility": { "type": "number" }, - "security": { "type": "number" }, - "maintainability": { "type": "number" }, - "operational_cost": { "type": "number" }, - "implementation_cost": { "type": "number" }, - "resource_consumption": { "type": "number" } - }, - "additionalProperties": false - } - } -} diff --git a/schemas/event.schema.json b/schemas/event.schema.json deleted file mode 100644 index cf2a3a3..0000000 --- a/schemas/event.schema.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://fluid.dev/schemas/event.schema.json", - "title": "FLUID audit event", - "description": "Append-only record of a material lifecycle transition. FLUID evolution is itself a system behavior that must remain reconstructable (FluidHypothesisRevisionSchema.md section 15).", - "type": "object", - "properties": { - "fluid_event": { - "type": "object", - "properties": { - "schema_version": { "$ref": "common.schema.json#/$defs/schemaVersion" }, - "id": { "$ref": "common.schema.json#/$defs/eventId" }, - "occurred_at": { "$ref": "common.schema.json#/$defs/timestamp" }, - "entity_type": { "enum": ["pressure", "hypothesis", "revision", "experiment", "backend_requirement", "intent", "decision", "routing_policy"] }, - "entity_id": { "type": "string", "minLength": 1 }, - "event_type": { "type": "string", "minLength": 1 }, - "actor": { "$ref": "common.schema.json#/$defs/actor" }, - "inputs": { - "type": "array", - "items": { "type": "string" }, - "description": "Identifiers of the records this transition drew on." - }, - "reason": { "type": "string" }, - "evidence_refs": { - "type": "array", - "items": { "$ref": "common.schema.json#/$defs/evidenceRef" } - } - }, - "required": ["schema_version", "id", "occurred_at", "entity_type", "entity_id", "event_type", "actor"], - "additionalProperties": false - } - }, - "required": ["fluid_event"], - "additionalProperties": false -} diff --git a/schemas/experiment.schema.json b/schemas/experiment.schema.json deleted file mode 100644 index 5e673d8..0000000 --- a/schemas/experiment.schema.json +++ /dev/null @@ -1,99 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://fluid.dev/schemas/experiment.schema.json", - "title": "FLUID experiment record", - "description": "Connects hypotheses to revisions under bounded conditions. Experiments must be interruptible (ArchitectureBlueprint.md section 16).", - "type": "object", - "properties": { - "fluid_experiment": { - "type": "object", - "properties": { - "schema_version": { "$ref": "common.schema.json#/$defs/schemaVersion" }, - "id": { "$ref": "common.schema.json#/$defs/experimentId" }, - "interface_id": { "$ref": "common.schema.json#/$defs/interfaceId" }, - "hypothesis_refs": { - "type": "array", - "items": { "$ref": "common.schema.json#/$defs/hypothesisId" }, - "minItems": 1, - "description": "An experiment without a hypothesis measures nothing in particular." - }, - "mechanism": { - "enum": ["sandbox", "shadow", "synthetic", "replay", "canary", "opt_in", "cohort", "tenant", "percentage"] - }, - "control_revision": { "$ref": "common.schema.json#/$defs/revisionId" }, - "candidate_revisions": { - "type": "array", - "items": { "$ref": "common.schema.json#/$defs/revisionId" }, - "minItems": 1 - }, - "cohorts": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/cohortId" } }, - "allocation": { - "type": "object", - "description": "Shares by revision id or by the reserved key 'control'. Allocation is deterministic and enacted by the router, never by this record.", - "additionalProperties": { "$ref": "common.schema.json#/$defs/unitInterval" }, - "minProperties": 2 - }, - "start_at": { "$ref": "common.schema.json#/$defs/timestamp" }, - "planned_end_at": { "oneOf": [{ "$ref": "common.schema.json#/$defs/timestamp" }, { "type": "null" }] }, - "max_duration_hours": { "type": "number", "exclusiveMinimum": 0 }, - - "metrics": { - "type": "object", - "description": "Primary metrics and guardrails are kept distinct so a scalar can never hide a regression (ArchitectureBlueprint.md section 48.5).", - "properties": { - "primary": { "type": "array", "items": { "type": "string" }, "minItems": 1 }, - "guardrails": { "type": "array", "items": { "type": "string" } }, - "secondary": { "type": "array", "items": { "type": "string" } }, - "learning": { "type": "array", "items": { "type": "string" } } - }, - "required": ["primary"], - "additionalProperties": false - }, - - "start_conditions": { "type": "array", "items": { "type": "string" } }, - "stop_conditions": { - "type": "array", - "items": { "type": "string" }, - "minItems": 1, - "description": "Every experiment has a stop condition (ArchitectureBlueprint.md section 55, invariant 7)." - }, - - "amendments": { - "type": "array", - "description": "Success criteria may not be changed after results are visible without recording the amendment (ArchitectureBlueprint.md section 18).", - "items": { - "type": "object", - "properties": { - "at": { "$ref": "common.schema.json#/$defs/timestamp" }, - "actor": { "$ref": "common.schema.json#/$defs/actor" }, - "change": { "type": "string", "minLength": 1 }, - "reason": { "type": "string", "minLength": 1 } - }, - "required": ["at", "actor", "change", "reason"], - "additionalProperties": false - } - }, - - "result": { - "type": "object", - "properties": { - "state": { "enum": ["PLANNED", "RUNNING", "STOPPED", "COMPLETED", "ABANDONED"] }, - "preferred_revision": { "oneOf": [{ "$ref": "common.schema.json#/$defs/revisionId" }, { "type": "null" }] }, - "stopped_reason": { "type": ["string", "null"] }, - "evidence_refs": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/evidenceRef" } } - }, - "required": ["state"], - "additionalProperties": false - } - }, - "required": [ - "schema_version", "id", "interface_id", "hypothesis_refs", - "control_revision", "candidate_revisions", "allocation", - "metrics", "stop_conditions", "result" - ], - "additionalProperties": false - } - }, - "required": ["fluid_experiment"], - "additionalProperties": false -} diff --git a/schemas/feedback.schema.json b/schemas/feedback.schema.json deleted file mode 100644 index 8e7b31d..0000000 --- a/schemas/feedback.schema.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://fluid.dev/schemas/feedback.schema.json", - "title": "FLUID explicit consumer feedback", - "description": "Structured feedback from a consumer. Treated as evidence and never as authority to change anything (FluidAPIStandards.md section 15).", - "type": "object", - "properties": { - "fluid_feedback": { - "type": "object", - "properties": { - "schema_version": { "$ref": "common.schema.json#/$defs/schemaVersion" }, - "id": { "$ref": "common.schema.json#/$defs/feedbackId" }, - "interface_id": { "$ref": "common.schema.json#/$defs/interfaceId" }, - "received_at": { "$ref": "common.schema.json#/$defs/timestamp" }, - "cohort": { "$ref": "common.schema.json#/$defs/cohortId" }, - "revision": { "$ref": "common.schema.json#/$defs/revisionId" }, - "goal": { "type": "string" }, - "attempt": { "type": "string" }, - "attempted_operations": { "type": "array", "items": { "type": "string" } }, - "outcome": { "type": "string" }, - "missing_capability": { "type": "string" }, - "workaround": { "type": ["string", "array"], "items": { "type": "string" } }, - "impact": { - "type": "object", - "properties": { - "description": { "type": "string" }, - "calls": { "type": "integer", "minimum": 0 }, - "latency_ms": { "type": "number", "minimum": 0 } - }, - "additionalProperties": false - }, - "confidence": { "$ref": "common.schema.json#/$defs/unitInterval" } - }, - "required": ["schema_version", "id", "interface_id", "received_at", "goal"], - "additionalProperties": false - } - }, - "required": ["fluid_feedback"], - "additionalProperties": false -} diff --git a/schemas/hypothesis.schema.json b/schemas/hypothesis.schema.json deleted file mode 100644 index e29181e..0000000 --- a/schemas/hypothesis.schema.json +++ /dev/null @@ -1,228 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://fluid.dev/schemas/hypothesis.schema.json", - "title": "FLUID hypothesis record", - "description": "Why an interface change should be explored. Observation, explanation, prediction, intervention and result stay separate on purpose (FluidHypothesisRevisionSchema.md section 18).", - "type": "object", - "properties": { - "fluid_hypothesis": { - "type": "object", - "properties": { - "schema_version": { "$ref": "common.schema.json#/$defs/schemaVersion" }, - "id": { "$ref": "common.schema.json#/$defs/hypothesisId" }, - "interface_id": { "$ref": "common.schema.json#/$defs/interfaceId" }, - "state": { - "enum": ["DRAFT", "READY", "PRIORITIZED", "DESIGNING", "EXPERIMENTING", "EVALUATING", "ACCEPTED", "REJECTED", "SUPERSEDED", "DEFERRED"] - }, - "created_at": { "$ref": "common.schema.json#/$defs/timestamp" }, - "created_by": { "$ref": "common.schema.json#/$defs/actor" }, - "title": { "type": "string", "minLength": 1 }, - - "observation": { - "type": "object", - "description": "What was seen. Not what it means.", - "properties": { - "summary": { "type": "string", "minLength": 1 }, - "evidence_refs": { - "type": "array", - "items": { "$ref": "common.schema.json#/$defs/evidenceRef" }, - "minItems": 1 - }, - "affected_cohorts": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/cohortId" } }, - "observation_window": { "$ref": "common.schema.json#/$defs/observationWindow" } - }, - "required": ["summary", "evidence_refs"], - "additionalProperties": false - }, - - "pressure": { - "type": "object", - "properties": { - "classes": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/pressureClass" }, "minItems": 1 }, - "pressure_refs": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/pressureId" } }, - "severity": { "$ref": "common.schema.json#/$defs/unitInterval" }, - "confidence": { "$ref": "common.schema.json#/$defs/unitInterval" } - }, - "required": ["classes"], - "additionalProperties": false - }, - - "explanation": { - "type": "object", - "description": "What we think explains the observation.", - "properties": { - "claim": { "type": "string", "minLength": 1 }, - "reach": { - "type": "object", - "description": "Explanatory reach is a prioritization signal, never a correctness claim (FluidAPIStandards.md section 18).", - "properties": { - "score": { "type": "number" }, - "explains": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/pressureId" } }, - "notes": { "type": "string" } - }, - "additionalProperties": false - } - }, - "required": ["claim"], - "additionalProperties": false - }, - - "proposed_adaptation": { - "type": "object", - "description": "What we propose to change.", - "properties": { - "class": { "$ref": "common.schema.json#/$defs/adaptationClass" }, - "summary": { "type": "string", "minLength": 1 }, - "candidate_contract": { - "type": "object", - "description": "Shape is protocol-specific and deliberately unconstrained.", - "additionalProperties": true - }, - "implementation_scope": { "enum": ["interface_only", "interface_and_backend", "documentation_only"] } - }, - "required": ["class", "summary"], - "additionalProperties": false - }, - - "backend_requirements": { - "type": "object", - "properties": { - "required": { "type": "boolean" }, - "requirement_refs": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/backendRequirementId" } } - }, - "required": ["required"], - "additionalProperties": false - }, - - "expected_outcomes": { - "type": "array", - "items": { "$ref": "common.schema.json#/$defs/expectedOutcome" }, - "minItems": 1, - "description": "A hypothesis must be falsifiable (FluidAPIStandards.md section 17)." - }, - "guardrails": { - "type": "array", - "items": { "$ref": "common.schema.json#/$defs/guardrail" } - }, - - "fitness_dimensions": { - "type": "object", - "properties": { - "expected": { "$ref": "common.schema.json#/$defs/fitnessDimensions" } - }, - "additionalProperties": false - }, - - "complexity": { - "type": "object", - "properties": { - "expected_delta": { "$ref": "common.schema.json#/$defs/complexityDelta" }, - "score": { "type": "number" } - }, - "required": ["expected_delta"], - "additionalProperties": false - }, - - "risk": { - "type": "object", - "properties": { - "level": { "enum": ["LOW", "MEDIUM", "HIGH", "CRITICAL"] }, - "reasons": { "type": "array", "items": { "type": "string" } } - }, - "required": ["level"], - "additionalProperties": false - }, - - "economics": { - "type": "object", - "description": "A hypothesis may be valuable but not yet worth exploring (ArchitectureBlueprint.md section 29).", - "properties": { - "estimated_experiment_cost": { "type": "number", "minimum": 0 }, - "estimated_implementation_cost": { "type": "number", "minimum": 0 }, - "currency": { "type": "string" }, - "expected_value_class": { "enum": ["LOW", "MEDIUM", "HIGH"] } - }, - "additionalProperties": false - }, - - "learning_value": { - "type": "object", - "properties": { - "score": { "type": "number" }, - "notes": { "type": "string" } - }, - "additionalProperties": false - }, - - "priority": { - "type": "object", - "properties": { - "score": { "type": "number" }, - "decided_by": { "type": "string" } - }, - "additionalProperties": false - }, - - "competition": { - "type": "object", - "description": "Competing hypotheses are legitimate (FluidAPIStandards.md section 19).", - "properties": { - "group_id": { "type": "string", "minLength": 1 }, - "alternatives": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/hypothesisId" } } - }, - "required": ["group_id"], - "additionalProperties": false - }, - - "success_criteria": { - "type": "object", - "properties": { "expression": { "type": "string", "minLength": 1 } }, - "required": ["expression"], - "additionalProperties": false - }, - "failure_criteria": { - "type": "object", - "properties": { "expression": { "type": "string", "minLength": 1 } }, - "required": ["expression"], - "additionalProperties": false - }, - - "candidate_revision_refs": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/revisionId" } }, - "experiment_refs": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/experimentId" } }, - - "outcome": { - "type": "object", - "description": "What happened afterwards. Null until evaluation completes.", - "properties": { - "status": { - "oneOf": [ - { "enum": ["CONFIRMED", "REFUTED", "INCONCLUSIVE"] }, - { "type": "null" } - ] - }, - "summary": { "type": ["string", "null"] }, - "evidence_refs": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/evidenceRef" } } - }, - "additionalProperties": false - }, - - "audit": { - "type": "object", - "properties": { - "decision_refs": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/decisionId" } }, - "immutable_event_refs": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/eventId" } } - }, - "additionalProperties": false - } - }, - "required": [ - "schema_version", "id", "interface_id", "state", "title", - "observation", "pressure", "explanation", "proposed_adaptation", - "expected_outcomes", "guardrails", "complexity", "risk", "success_criteria" - ], - "additionalProperties": false - } - }, - "required": ["fluid_hypothesis"], - "additionalProperties": false -} diff --git a/schemas/pressure.schema.json b/schemas/pressure.schema.json deleted file mode 100644 index 5d5a20f..0000000 --- a/schemas/pressure.schema.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://fluid.dev/schemas/pressure.schema.json", - "title": "FLUID pressure record", - "description": "Evidence that the interface differs materially from consumer needs. Pressure is evidence, not truth (FluidAPIStandards.md section 13).", - "type": "object", - "properties": { - "fluid_pressure": { - "type": "object", - "properties": { - "schema_version": { "$ref": "common.schema.json#/$defs/schemaVersion" }, - "id": { "$ref": "common.schema.json#/$defs/pressureId" }, - "interface_id": { "$ref": "common.schema.json#/$defs/interfaceId" }, - "class": { "$ref": "common.schema.json#/$defs/pressureClass" }, - "first_seen": { "$ref": "common.schema.json#/$defs/timestamp" }, - "last_seen": { "$ref": "common.schema.json#/$defs/timestamp" }, - "affected_cohorts": { - "type": "array", - "items": { "$ref": "common.schema.json#/$defs/cohortId" } - }, - "frequency": { - "type": "object", - "properties": { - "observations": { "type": "integer", "minimum": 0 }, - "independent_consumers": { "type": "integer", "minimum": 0 } - }, - "additionalProperties": false - }, - "severity": { "$ref": "common.schema.json#/$defs/unitInterval" }, - "confidence": { "$ref": "common.schema.json#/$defs/unitInterval" }, - "summary": { "type": "string", "minLength": 1 }, - "evidence_refs": { - "type": "array", - "items": { "$ref": "common.schema.json#/$defs/evidenceRef" }, - "minItems": 1, - "description": "A pressure record without evidence references is not auditable and must be rejected." - }, - "status": { - "enum": ["OPEN", "ANALYZING", "EXPLAINED", "ADDRESSED", "DISMISSED", "OUT_OF_SCOPE"], - "description": "ArchitectureBlueprint.md section 9. Pressure may remain unresolved on purpose." - }, - "linked_hypotheses": { - "type": "array", - "items": { "$ref": "common.schema.json#/$defs/hypothesisId" } - } - }, - "required": ["schema_version", "id", "interface_id", "class", "first_seen", "last_seen", "summary", "evidence_refs", "status"], - "additionalProperties": false - } - }, - "required": ["fluid_pressure"], - "additionalProperties": false -} diff --git a/schemas/revision-descriptor.schema.json b/schemas/revision-descriptor.schema.json deleted file mode 100644 index 3400eac..0000000 --- a/schemas/revision-descriptor.schema.json +++ /dev/null @@ -1,124 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://fluid.dev/schemas/revision-descriptor.schema.json", - "title": "FLUID revision descriptor", - "description": "The runtime-facing projection of a revision, deliberately small enough to be consumed deterministically by infrastructure (ArchitectureBlueprint.md section 36). This is a wire artifact: the gateway must be able to read it without any fluid-core library.", - "type": "object", - "properties": { - "revision": { - "type": "object", - "properties": { - "schema_version": { "$ref": "common.schema.json#/$defs/schemaVersion" }, - "id": { "$ref": "common.schema.json#/$defs/revisionId" }, - "interface": { "$ref": "common.schema.json#/$defs/interfaceId" }, - "state": { - "enum": ["created", "verified", "experiment", "candidate", "stable", "deprecated", "retired"], - "description": "The router refuses to route to created, failed or retired revisions (ArchitectureBlueprint.md section 5.3)." - }, - - "contract": { - "type": "object", - "properties": { - "type": { "enum": ["openapi", "graphql", "protobuf", "asyncapi", "jsonschema", "custom"] }, - "digest": { "$ref": "common.schema.json#/$defs/digest" }, - "source": { "type": "string", "description": "Where the contract artifact can be fetched." } - }, - "required": ["type", "digest"], - "additionalProperties": false - }, - - "runtime": { - "type": "object", - "description": "Out-of-process attachment: the adapter is an upstream reached over the network, so it may be implemented in any language.", - "properties": { - "upstream": { - "type": "string", - "format": "uri", - "description": "Base URL of the adapter process serving this revision." - }, - "image": { "type": "string" }, - "digest": { "$ref": "common.schema.json#/$defs/digest" }, - "timeout_ms": { "type": "integer", "exclusiveMinimum": 0, "default": 5000 }, - "retry": { - "type": "object", - "properties": { - "max_attempts": { "type": "integer", "minimum": 1, "default": 1 }, - "backoff_ms": { "type": "integer", "minimum": 0 }, - "retry_on": { "type": "array", "items": { "type": "string" } } - }, - "additionalProperties": false - }, - "circuit_breaker": { - "type": "object", - "properties": { - "failure_threshold": { "type": "integer", "minimum": 1 }, - "reset_after_ms": { "type": "integer", "exclusiveMinimum": 0 } - }, - "additionalProperties": false - } - }, - "required": ["upstream"], - "additionalProperties": false - }, - - "intent": { - "type": "object", - "description": "Which interface evolution intent governs this revision. Required: a revision with no governing intent cannot be audited.", - "properties": { - "version": { "type": "string", "minLength": 1 }, - "digest": { "$ref": "common.schema.json#/$defs/digest" } - }, - "required": ["version"], - "additionalProperties": false - }, - - "routing": { - "type": "object", - "properties": { - "eligible_cohorts": { - "type": "array", - "items": { "$ref": "common.schema.json#/$defs/cohortId" }, - "description": "Empty or absent means all cohorts are eligible." - }, - "max_traffic_share": { - "$ref": "common.schema.json#/$defs/unitInterval", - "description": "Ceiling the router enforces regardless of what a routing policy asks for." - } - }, - "additionalProperties": false - }, - - "policy": { - "type": "object", - "properties": { - "compatibility": { - "enum": ["backward_compatible", "forward_compatible", "additive", "behavioral_change", "breaking", "migration_only", "internal_only"] - }, - "security_check": { "enum": ["pending", "passed", "failed"] }, - "policy_check": { "enum": ["pending", "passed", "failed"] }, - "rollback_to": { "oneOf": [{ "$ref": "common.schema.json#/$defs/revisionId" }, { "type": "null" }] } - }, - "required": ["compatibility", "security_check"], - "additionalProperties": false - }, - - "signature": { - "type": "object", - "description": "The router accepts only signed published descriptors (ArchitectureBlueprint.md section 35). The signature covers the canonical form of this document with the signature member removed.", - "properties": { - "algorithm": { "enum": ["ed25519"] }, - "key_id": { "type": "string", "minLength": 1 }, - "value": { "type": "string", "contentEncoding": "base64" }, - "signed_at": { "$ref": "common.schema.json#/$defs/timestamp" } - }, - "required": ["algorithm", "key_id", "value"], - "additionalProperties": false - } - }, - "required": ["schema_version", "id", "interface", "state", "contract", "runtime", "intent", "policy"], - "additionalProperties": false - } - }, - "required": ["revision"], - "additionalProperties": false -} diff --git a/schemas/revision.schema.json b/schemas/revision.schema.json deleted file mode 100644 index 9a9d7ab..0000000 --- a/schemas/revision.schema.json +++ /dev/null @@ -1,235 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://fluid.dev/schemas/revision.schema.json", - "title": "FLUID revision record", - "description": "What deterministic interface state was built, verified, exposed and measured. Published revisions should be immutable; corrections create successors (FluidAPIStandards.md section 8).", - "type": "object", - "properties": { - "fluid_revision": { - "type": "object", - "properties": { - "schema_version": { "$ref": "common.schema.json#/$defs/schemaVersion" }, - "id": { "$ref": "common.schema.json#/$defs/revisionId" }, - "interface_id": { "$ref": "common.schema.json#/$defs/interfaceId" }, - "revision_number": { "type": "integer", "minimum": 0 }, - "parent_revision": { - "oneOf": [ - { "$ref": "common.schema.json#/$defs/revisionId" }, - { "type": "null" } - ], - "description": "Null marks genesis." - }, - "state": { - "enum": ["CREATED", "VERIFIED", "EXPERIMENT", "CANDIDATE", "STABLE", "DEPRECATED", "RETIRED"] - }, - "created_at": { "$ref": "common.schema.json#/$defs/timestamp" }, - "created_by": { "$ref": "common.schema.json#/$defs/actor" }, - - "contract": { - "type": "object", - "properties": { - "type": { "enum": ["openapi", "graphql", "protobuf", "asyncapi", "jsonschema", "custom"] }, - "version": { "type": "string" }, - "artifact_ref": { "$ref": "common.schema.json#/$defs/artifactRef" }, - "digest": { "$ref": "common.schema.json#/$defs/digest" } - }, - "required": ["type", "artifact_ref", "digest"], - "additionalProperties": false - }, - - "implementation": { - "type": "object", - "properties": { - "artifact_ref": { "$ref": "common.schema.json#/$defs/artifactRef" }, - "source_ref": { "type": "string" }, - "build_ref": { "type": "string" }, - "digest": { "$ref": "common.schema.json#/$defs/digest" } - }, - "required": ["artifact_ref", "digest"], - "additionalProperties": false - }, - - "interface_evolution_intent": { - "type": "object", - "description": "Every revision is governed by a specific intent version, so audit can ask whether the change was valid under the intent that existed at the time (ArchitectureBlueprint.md section 27).", - "properties": { - "version": { "type": "string", "minLength": 1 }, - "artifact_ref": { "$ref": "common.schema.json#/$defs/artifactRef" } - }, - "required": ["version"], - "additionalProperties": false - }, - - "originating_hypotheses": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/hypothesisId" } }, - "backend_requirements": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/backendRequirementId" } }, - - "compatibility": { - "type": "object", - "properties": { - "class": { - "enum": ["BACKWARD_COMPATIBLE", "FORWARD_COMPATIBLE", "ADDITIVE", "BEHAVIORAL_CHANGE", "BREAKING", "MIGRATION_ONLY", "INTERNAL_ONLY"] - }, - "breaking_changes": { "type": "array", "items": { "type": "string" } }, - "deprecations": { "type": "array", "items": { "type": "string" } }, - "supersedes": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/revisionId" } }, - "compatibility_evidence_refs": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/evidenceRef" } } - }, - "required": ["class"], - "additionalProperties": false - }, - - "adaptation_classes": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/adaptationClass" } }, - - "verification": { - "type": "object", - "description": "The primary safety barrier between adaptive generation and deterministic runtime (ArchitectureBlueprint.md section 15).", - "properties": { - "status": { "enum": ["PENDING", "PASSED", "FAILED"] }, - "test_refs": { "type": "array", "items": { "type": "string" } }, - "security_check": { "enum": ["PENDING", "PASSED", "FAILED"] }, - "policy_check": { "enum": ["PENDING", "PASSED", "FAILED"] } - }, - "required": ["status"], - "additionalProperties": false - }, - - "complexity": { - "type": "object", - "properties": { - "before": { "$ref": "common.schema.json#/$defs/complexityDelta" }, - "after": { "$ref": "common.schema.json#/$defs/complexityDelta" }, - "delta_score": { "type": "number" }, - "budget_status": { "enum": ["WITHIN_BUDGET", "AT_BUDGET", "OVER_BUDGET"] } - }, - "additionalProperties": false - }, - - "deployment": { - "type": "object", - "properties": { - "environments": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { "type": "string" }, - "started_at": { "$ref": "common.schema.json#/$defs/timestamp" }, - "routing_policy_ref": { "type": "string" } - }, - "required": ["name"], - "additionalProperties": false - } - }, - "current_exposure": { - "type": "object", - "properties": { - "traffic_share": { "$ref": "common.schema.json#/$defs/unitInterval" }, - "cohorts": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/cohortId" } } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - - "fitness": { - "type": "object", - "properties": { - "baseline_revision": { "oneOf": [{ "$ref": "common.schema.json#/$defs/revisionId" }, { "type": "null" }] }, - "measurement_window": { "$ref": "common.schema.json#/$defs/observationWindow" }, - "metrics": { - "type": "object", - "additionalProperties": { - "type": "object", - "properties": { - "baseline": { "type": ["number", "null"] }, - "current": { "type": ["number", "null"] }, - "target": { "type": ["number", "null"] }, - "guardrail": { "type": ["number", "null"] } - }, - "additionalProperties": false - } - } - }, - "additionalProperties": false - }, - - "adoption": { - "type": "object", - "description": "Adoption is evidence, not proof of quality (FluidAPIStandards.md section 32).", - "properties": { - "eligible_consumers": { "type": ["integer", "null"], "minimum": 0 }, - "active_consumers": { "type": ["integer", "null"], "minimum": 0 }, - "adoption_rate": { "oneOf": [{ "$ref": "common.schema.json#/$defs/unitInterval" }, { "type": "null" }] }, - "retained_adoption_rate": { "oneOf": [{ "$ref": "common.schema.json#/$defs/unitInterval" }, { "type": "null" }] } - }, - "additionalProperties": false - }, - - "economics": { - "type": "object", - "properties": { - "build_cost": { "type": "number", "minimum": 0 }, - "experiment_cost_to_date": { "type": "number", "minimum": 0 }, - "currency": { "type": "string" } - }, - "additionalProperties": false - }, - - "promotion": { - "type": "object", - "properties": { - "eligible": { "type": "boolean" }, - "recommended_state": { - "oneOf": [ - { "enum": ["CREATED", "VERIFIED", "EXPERIMENT", "CANDIDATE", "STABLE", "DEPRECATED", "RETIRED"] }, - { "type": "null" } - ] - }, - "recommendation_reason": { "type": ["string", "null"] }, - "authorized_by": { "oneOf": [{ "$ref": "common.schema.json#/$defs/actor" }, { "type": "null" }] }, - "authorized_at": { "oneOf": [{ "$ref": "common.schema.json#/$defs/timestamp" }, { "type": "null" }] } - }, - "additionalProperties": false - }, - - "rollback": { - "type": "object", - "properties": { - "supported": { "type": "boolean" }, - "target_revision": { "oneOf": [{ "$ref": "common.schema.json#/$defs/revisionId" }, { "type": "null" }] }, - "procedure_ref": { "type": "string" } - }, - "required": ["supported"], - "additionalProperties": false - }, - - "provenance": { - "type": "object", - "properties": { - "decision_refs": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/decisionId" } }, - "experiment_refs": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/experimentId" } }, - "telemetry_refs": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/evidenceRef" } } - }, - "additionalProperties": false - }, - - "audit": { - "type": "object", - "properties": { - "immutable_event_refs": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/eventId" } } - }, - "additionalProperties": false - } - }, - "required": [ - "schema_version", "id", "interface_id", "revision_number", "parent_revision", - "state", "contract", "implementation", "interface_evolution_intent", - "compatibility", "verification", "rollback", "provenance" - ], - "additionalProperties": false - } - }, - "required": ["fluid_revision"], - "additionalProperties": false -} diff --git a/schemas/routing-policy.schema.json b/schemas/routing-policy.schema.json deleted file mode 100644 index 7717197..0000000 --- a/schemas/routing-policy.schema.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://fluid.dev/schemas/routing-policy.schema.json", - "title": "FLUID routing policy", - "description": "Deterministic routing written by the experiment controller and consumed by the revision router. The controller never touches traffic itself; this separation keeps experimental intent out of the runtime decision mechanism (ArchitectureBlueprint.md section 17).", - "type": "object", - "properties": { - "routing_policy": { - "type": "object", - "properties": { - "schema_version": { "$ref": "common.schema.json#/$defs/schemaVersion" }, - "id": { "type": "string", "minLength": 1 }, - "interface": { "$ref": "common.schema.json#/$defs/interfaceId" }, - "generation": { - "type": "integer", - "minimum": 0, - "description": "Monotonic. The router ignores a policy older than the one it holds." - }, - "issued_at": { "$ref": "common.schema.json#/$defs/timestamp" }, - "issued_by": { "$ref": "common.schema.json#/$defs/actor" }, - - "default_revision": { - "$ref": "common.schema.json#/$defs/revisionId", - "description": "Where a request lands when no earlier resolution step matched." - }, - - "rules": { - "type": "array", - "description": "Evaluated in order; the first matching rule wins. Matching must be a pure function of the request and its cohort assignment.", - "items": { - "type": "object", - "properties": { - "cohort": { "$ref": "common.schema.json#/$defs/cohortId" }, - "tenant": { "type": "string" }, - "experiment": { "$ref": "common.schema.json#/$defs/experimentId" }, - "allocation": { - "type": "object", - "description": "Revision id to traffic share. Shares must sum to 1. Assignment of a given consumer must be stable across requests.", - "propertyNames": { "$ref": "common.schema.json#/$defs/revisionId" }, - "additionalProperties": { "$ref": "common.schema.json#/$defs/unitInterval" }, - "minProperties": 1 - }, - "sticky_by": { - "enum": ["consumer_id", "tenant", "correlation_id", "none"], - "default": "consumer_id", - "description": "What keeps a long-lived consumer on one side of an experiment." - } - }, - "required": ["allocation"], - "additionalProperties": false - } - }, - - "signature": { - "type": "object", - "properties": { - "algorithm": { "enum": ["ed25519"] }, - "key_id": { "type": "string", "minLength": 1 }, - "value": { "type": "string", "contentEncoding": "base64" } - }, - "required": ["algorithm", "key_id", "value"], - "additionalProperties": false - } - }, - "required": ["schema_version", "interface", "generation", "default_revision", "rules"], - "additionalProperties": false - } - }, - "required": ["routing_policy"], - "additionalProperties": false -} diff --git a/schemas/telemetry-envelope.schema.json b/schemas/telemetry-envelope.schema.json deleted file mode 100644 index 2a55b7f..0000000 --- a/schemas/telemetry-envelope.schema.json +++ /dev/null @@ -1,113 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://fluid.dev/schemas/telemetry-envelope.schema.json", - "title": "FLUID telemetry envelope", - "description": "The normalized interaction event. Designed for interface learning, not unrestricted behavioral capture: raw payload capture is never the default and redaction happens before an event reaches the store (ArchitectureBlueprint.md section 6.2).", - "type": "object", - "properties": { - "fluid_telemetry": { - "type": "object", - "properties": { - "schema_version": { "$ref": "common.schema.json#/$defs/schemaVersion" }, - "id": { "type": "string", "minLength": 1 }, - "occurred_at": { "$ref": "common.schema.json#/$defs/timestamp" }, - "interface_id": { "$ref": "common.schema.json#/$defs/interfaceId" }, - "kind": { - "enum": ["request", "error", "sequence", "adoption", "feedback"], - "description": "ArchitectureBlueprint.md section 6.1." - }, - - "correlation_id": { - "type": "string", - "description": "Ties an event to the response the consumer saw, and to the other events in its call chain." - }, - "consumer_ref": { - "type": "string", - "description": "Pseudonymous and stable. Never a raw end-user identifier." - }, - "cohort": { "$ref": "common.schema.json#/$defs/cohortId" }, - "revision": { "$ref": "common.schema.json#/$defs/revisionId" }, - "experiment": { "oneOf": [{ "$ref": "common.schema.json#/$defs/experimentId" }, { "type": "null" }] }, - - "resolution": { - "type": "object", - "description": "Why this revision was chosen. Revision resolution must be deterministic and auditable (ArchitectureBlueprint.md section 5.2).", - "properties": { - "reason": { - "enum": ["explicit_revision", "bound_contract", "experiment_assignment", "cohort_rule", "stable_default"] - }, - "policy_generation": { "type": "integer", "minimum": 0 } - }, - "required": ["reason"], - "additionalProperties": false - }, - - "request": { - "type": "object", - "properties": { - "operation": { "type": "string" }, - "route": { "type": "string" }, - "method": { "type": "string" }, - "status": { "type": "integer" }, - "latency_ms": { "type": "number", "minimum": 0 }, - "request_bytes": { "type": "integer", "minimum": 0 }, - "response_bytes": { "type": "integer", "minimum": 0 } - }, - "additionalProperties": false - }, - - "error": { - "type": "object", - "properties": { - "class": { - "enum": ["validation", "unknown_path", "unknown_field", "unsupported_parameter", "authorization", "backend_failure", "timeout", "policy_rejection"] - }, - "detail": { "type": "string", "description": "Redacted. Must not carry backend internals or consumer data." }, - "field": { "type": "string" } - }, - "required": ["class"], - "additionalProperties": false - }, - - "sequence": { - "type": "object", - "description": "Interaction topology is often more informative than error counts (ArchitectureBlueprint.md section 6.4).", - "properties": { - "chain_id": { "type": "string" }, - "position": { "type": "integer", "minimum": 0 }, - "pattern": { "type": "string" }, - "repeat_count": { "type": "integer", "minimum": 0 } - }, - "additionalProperties": false - }, - - "adoption": { - "type": "object", - "properties": { - "event": { "enum": ["first_use", "continued_use", "migration", "reversion", "deprecation_response"] }, - "from_revision": { "oneOf": [{ "$ref": "common.schema.json#/$defs/revisionId" }, { "type": "null" }] } - }, - "required": ["event"], - "additionalProperties": false - }, - - "feedback_ref": { "$ref": "common.schema.json#/$defs/feedbackId" }, - - "redaction": { - "type": "object", - "description": "What the privacy filter removed. Recorded so analysis knows what it cannot see.", - "properties": { - "applied": { "type": "boolean" }, - "rules": { "type": "array", "items": { "type": "string" } } - }, - "required": ["applied"], - "additionalProperties": false - } - }, - "required": ["schema_version", "id", "occurred_at", "interface_id", "kind"], - "additionalProperties": false - } - }, - "required": ["fluid_telemetry"], - "additionalProperties": false -} diff --git a/tools/schemagen/main.go b/tools/schemagen/main.go deleted file mode 100644 index cb56160..0000000 --- a/tools/schemagen/main.go +++ /dev/null @@ -1,495 +0,0 @@ -// Command schemagen generates Go types from the FLUID wire-contract schemas. -// -// The record types are never hand-written: schemas/ is the source of truth, and -// drift between the specification and the implementation must fail the build -// rather than survive as a quietly diverging struct. -package main - -import ( - "bytes" - "encoding/json" - "fmt" - "go/format" - "os" - "path/filepath" - "sort" - "strings" -) - -// schema is the subset of JSON Schema the FLUID contract actually uses. -type schema struct { - ID string `json:"$id"` - Ref string `json:"$ref"` - Title string `json:"title"` - Description string `json:"description"` - Type any `json:"type"` - Const any `json:"const"` - Enum []string `json:"enum"` - Format string `json:"format"` - Properties map[string]*schema `json:"properties"` - Required []string `json:"required"` - Items *schema `json:"items"` - Defs map[string]*schema `json:"$defs"` - OneOf []*schema `json:"oneOf"` - AdditionalProperties json.RawMessage `json:"additionalProperties"` - PropertyNames *schema `json:"propertyNames"` -} - -// additional reports how additionalProperties was written: as a schema -// (a map value type), as false, or absent. -func (s *schema) additional() (*schema, bool) { - if len(s.AdditionalProperties) == 0 { - return nil, false - } - var b bool - if err := json.Unmarshal(s.AdditionalProperties, &b); err == nil { - return nil, b // true means "anything"; false means closed - } - var sub schema - if err := json.Unmarshal(s.AdditionalProperties, &sub); err == nil { - return &sub, true - } - return nil, false -} - -func (s *schema) typeIs(want string) bool { - switch t := s.Type.(type) { - case string: - return t == want - case []any: - for _, v := range t { - if v == want { - return true - } - } - } - return false -} - -func (s *schema) nullable() bool { - if s.typeIs("null") { - return true - } - for _, o := range s.OneOf { - if o.typeIs("null") { - return true - } - } - return false -} - -var initialisms = map[string]string{ - "id": "ID", "ids": "IDs", "url": "URL", "uri": "URI", "api": "API", - "ms": "MS", "http": "HTTP", "json": "JSON", "yaml": "YAML", "sha": "SHA", - "p95": "P95", "iei": "IEI", "sla": "SLA", "ai": "AI", -} - -// splitCamel inserts a separator at lowercase-to-uppercase boundaries so that -// camelCase schema keys such as "cohortId" split into words the initialism map -// can then correct. -func splitCamel(s string) string { - var b strings.Builder - runes := []rune(s) - for i, r := range runes { - if i > 0 && r >= 'A' && r <= 'Z' && runes[i-1] >= 'a' && runes[i-1] <= 'z' { - b.WriteRune('_') - } - b.WriteRune(r) - } - return b.String() -} - -func goName(s string) string { - s = splitCamel(s) - parts := strings.FieldsFunc(s, func(r rune) bool { return r == '_' || r == '-' || r == '.' }) - var b strings.Builder - for _, p := range parts { - if up, ok := initialisms[strings.ToLower(p)]; ok { - b.WriteString(up) - continue - } - if p == "" { - continue - } - b.WriteString(strings.ToUpper(p[:1])) - b.WriteString(p[1:]) - } - return b.String() -} - -// generator accumulates named types while walking the schemas. -type generator struct { - commonDefs map[string]string // $defs key -> Go type name - decls []string - seen map[string]bool -} - -func (g *generator) emit(decl string) { - g.decls = append(g.decls, decl) -} - -func comment(indent, text string) string { - text = strings.Join(strings.Fields(text), " ") - if text == "" { - return "" - } - var out []string - line := indent + "//" - for _, w := range strings.Fields(text) { - if len(line)+1+len(w) > 84 && line != indent+"//" { - out = append(out, line) - line = indent + "//" - } - line += " " + w - } - out = append(out, line) - return strings.Join(out, "\n") + "\n" -} - -// resolveRef maps "common.schema.json#/$defs/foo" to its generated Go type. -func (g *generator) resolveRef(ref string) (string, bool) { - const marker = "#/$defs/" - i := strings.Index(ref, marker) - if i < 0 { - return "", false - } - key := ref[i+len(marker):] - name, ok := g.commonDefs[key] - return name, ok -} - -// goType returns the Go type for s, generating nested named types as needed. -// parent seeds the name of any anonymous struct or enum encountered. -func (g *generator) goType(parent, field string, s *schema, required bool) string { - if s.Ref != "" { - if name, ok := g.resolveRef(s.Ref); ok { - if !required { - return "*" + name - } - return name - } - return "any" - } - - nullable := s.nullable() - - // oneOf that is just "X or null" collapses to a pointer to X. - if len(s.OneOf) > 0 { - for _, o := range s.OneOf { - if o.typeIs("null") { - continue - } - inner := g.goType(parent, field, o, true) - return "*" + strings.TrimPrefix(inner, "*") - } - return "any" - } - - // A union of several concrete types (guardrail thresholds may be number, - // boolean or string) has no faithful Go equivalent but `any`. - if ts, ok := s.Type.([]any); ok { - concrete := 0 - for _, t := range ts { - if t != "null" { - concrete++ - } - } - if concrete > 1 { - return "any" - } - } - - name := parent + goName(field) - - switch { - case len(s.Enum) > 0: - g.emitEnum(name, s) - if !required || nullable { - return "*" + name - } - return name - - case s.typeIs("object"): - if len(s.Properties) > 0 { - g.emitStruct(name, s) - if !required || nullable { - return "*" + name - } - return name - } - if sub, ok := s.additional(); ok && sub != nil { - return "map[string]" + g.goType(name, "Value", sub, true) - } - return "map[string]any" - - case s.typeIs("array"): - if s.Items == nil { - return "[]any" - } - return "[]" + strings.TrimPrefix(g.goType(name, "Item", s.Items, true), "*") - - case s.typeIs("string"): - base := "string" - if s.Format == "date-time" { - base = "time.Time" - } - if nullable || (!required && base == "time.Time") { - return "*" + base - } - return base - - case s.typeIs("integer"): - if nullable || !required { - return "*int64" - } - return "int64" - - case s.typeIs("number"): - if nullable || !required { - return "*float64" - } - return "float64" - - case s.typeIs("boolean"): - if nullable || !required { - return "*bool" - } - return "bool" - } - - return "any" -} - -// symbolNames covers enum values that are operators rather than words, so the -// generated constants stay valid Go identifiers. -var symbolNames = map[string]string{ - "<": "Lt", "<=": "Lte", "==": "Eq", "!=": "Neq", ">=": "Gte", ">": "Gt", -} - -// enumConstName builds an identifier suffix for one enum value. -func enumConstName(v string, index int) string { - if sym, ok := symbolNames[v]; ok { - return sym - } - name := goName(v) - cleaned := make([]rune, 0, len(name)) - for _, r := range name { - if r == '_' || r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' || r >= '0' && r <= '9' { - cleaned = append(cleaned, r) - } - } - name = string(cleaned) - if name == "" || name[0] >= '0' && name[0] <= '9' { - name = fmt.Sprintf("Value%d", index) - } - return name -} - -func (g *generator) emitEnum(name string, s *schema) { - if g.seen[name] { - return - } - g.seen[name] = true - - var b strings.Builder - b.WriteString(comment("", s.Description)) - fmt.Fprintf(&b, "type %s string\n\nconst (\n", name) - for i, v := range s.Enum { - fmt.Fprintf(&b, "\t%s%s %s = %q\n", name, enumConstName(v, i), name, v) - } - b.WriteString(")\n\n") - - // A generated Valid method keeps enum checking in one place. - fmt.Fprintf(&b, "// Valid reports whether v is a defined %s.\n", name) - fmt.Fprintf(&b, "func (v %s) Valid() bool {\n\tswitch v {\n\tcase ", name) - quoted := make([]string, 0, len(s.Enum)) - for i, v := range s.Enum { - quoted = append(quoted, name+enumConstName(v, i)) - } - b.WriteString(strings.Join(quoted, ", ")) - b.WriteString(":\n\t\treturn true\n\t}\n\treturn false\n}\n") - - g.emit(b.String()) -} - -func (g *generator) emitStruct(name string, s *schema) { - if g.seen[name] { - return - } - g.seen[name] = true - - req := map[string]bool{} - for _, r := range s.Required { - req[r] = true - } - - keys := make([]string, 0, len(s.Properties)) - for k := range s.Properties { - keys = append(keys, k) - } - sort.Strings(keys) - - // Fields are resolved before the struct is written so nested types are - // declared in dependency order. - type field struct{ name, typ, tag, doc string } - fields := make([]field, 0, len(keys)) - for _, k := range keys { - p := s.Properties[k] - typ := g.goType(name, k, p, req[k]) - tag := k - if !req[k] { - tag += ",omitempty" - } - fields = append(fields, field{goName(k), typ, tag, p.Description}) - } - - var b strings.Builder - b.WriteString(comment("", s.Description)) - fmt.Fprintf(&b, "type %s struct {\n", name) - for i, f := range fields { - if f.doc != "" { - if i > 0 { - b.WriteString("\n") - } - b.WriteString(comment("\t", f.doc)) - } - fmt.Fprintf(&b, "\t%s %s `json:%q yaml:%q`\n", f.name, f.typ, f.tag, f.tag) - } - b.WriteString("}\n") - - g.emit(b.String()) -} - -func load(path string) (*schema, error) { - data, err := os.ReadFile(path) - if err != nil { - return nil, err - } - var s schema - if err := json.Unmarshal(data, &s); err != nil { - return nil, fmt.Errorf("%s: %w", filepath.Base(path), err) - } - return &s, nil -} - -func write(path, pkg string, decls []string) error { - var b bytes.Buffer - b.WriteString("// Code generated by tools/schemagen. DO NOT EDIT.\n") - b.WriteString("// Source: schemas/. Regenerate with `make generate`.\n\n") - fmt.Fprintf(&b, "package %s\n\n", pkg) - body := strings.Join(decls, "\n") - if strings.Contains(body, "time.Time") { - b.WriteString("import \"time\"\n\n") - } - b.WriteString(body) - - src, err := format.Source(b.Bytes()) - if err != nil { - // Write the unformatted source so the error is diagnosable. - _ = os.WriteFile(path+".broken", b.Bytes(), 0o644) - return fmt.Errorf("format %s: %w", path, err) - } - return os.WriteFile(path, src, 0o644) -} - -func main() { - schemaDir := "schemas" - outDir := filepath.Join("internal", "contract") - - if err := os.MkdirAll(outDir, 0o755); err != nil { - fatal(err) - } - - common, err := load(filepath.Join(schemaDir, "common.schema.json")) - if err != nil { - fatal(err) - } - - g := &generator{commonDefs: map[string]string{}, seen: map[string]bool{}} - - // Pass one: name every common definition so record schemas can reference them. - defKeys := make([]string, 0, len(common.Defs)) - for k := range common.Defs { - defKeys = append(defKeys, k) - } - sort.Strings(defKeys) - for _, k := range defKeys { - g.commonDefs[k] = goName(k) - } - - // Pass two: declare them. - for _, k := range defKeys { - d := common.Defs[k] - name := g.commonDefs[k] - switch { - case len(d.Enum) > 0: - g.emitEnum(name, d) - case d.typeIs("object") && len(d.Properties) > 0: - g.emitStruct(name, d) - default: - if g.seen[name] { - continue - } - g.seen[name] = true - underlying := g.goType("", k, d, true) - // Scalar definitions become distinct types rather than aliases: - // passing a hypothesis id where a revision id belongs should not - // compile. Everything else stays an alias to keep call sites plain. - form := "type %s = %s\n" - switch underlying { - case "string", "float64", "int64": - form = "type %s %s\n" - } - g.emit(comment("", d.Description) + fmt.Sprintf(form, name, underlying)) - } - } - - if err := write(filepath.Join(outDir, "common_gen.go"), "contract", g.decls); err != nil { - fatal(err) - } - fmt.Printf("generated %s (%d declarations)\n", filepath.Join(outDir, "common_gen.go"), len(g.decls)) - - records := []string{ - "pressure", "hypothesis", "revision", "experiment", "event", - "feedback", "backend-requirement", "revision-descriptor", - "routing-policy", "telemetry-envelope", - } - - for _, rec := range records { - s, err := load(filepath.Join(schemaDir, rec+".schema.json")) - if err != nil { - fatal(err) - } - rg := &generator{commonDefs: g.commonDefs, seen: map[string]bool{}} - - // Each record schema is a single-property wrapper; generate the - // wrapper plus the payload type it names. - for key, prop := range s.Properties { - rg.emitStruct(goName(key), prop) - } - var keys []string - for k := range s.Properties { - keys = append(keys, k) - } - sort.Strings(keys) - var doc strings.Builder - doc.WriteString(comment("", s.Description)) - fmt.Fprintf(&doc, "type %sDocument struct {\n", goName(rec)) - for _, k := range keys { - fmt.Fprintf(&doc, "\t%s %s `json:%q yaml:%q`\n", goName(k), goName(k), k, k) - } - doc.WriteString("}\n") - rg.emit(doc.String()) - - out := filepath.Join(outDir, strings.ReplaceAll(rec, "-", "_")+"_gen.go") - if err := write(out, "contract", rg.decls); err != nil { - fatal(err) - } - fmt.Printf("generated %s\n", out) - } -} - -func fatal(err error) { - fmt.Fprintln(os.Stderr, "schemagen:", err) - os.Exit(1) -} diff --git a/workplans/FLUID-WP-0002-foundations-and-wire-contract.md b/workplans/FLUID-WP-0002-foundations-and-wire-contract.md index 0a86bce..e67ff76 100644 --- a/workplans/FLUID-WP-0002-foundations-and-wire-contract.md +++ b/workplans/FLUID-WP-0002-foundations-and-wire-contract.md @@ -4,7 +4,7 @@ type: workplan title: "FLUID foundations and the wire contract" domain: infotech repo: fluid-core -status: done +status: active owner: worsch topic_slug: fluid-core created: "2026-09-04" @@ -43,7 +43,7 @@ boundary and the four invariants this repo defends hardest. ```task id: FLUID-WP-0002-T02 -status: done +status: todo priority: high state_hub_task_id: "5cf83eeb-819f-5972-a660-aa9f5345355f" ``` @@ -57,7 +57,7 @@ the interop surface, per its §1. ```task id: FLUID-WP-0002-T03 -status: done +status: todo priority: high state_hub_task_id: "da22bde0-5138-56db-a26d-f63a3195d6ed" ``` @@ -70,7 +70,7 @@ readable without any fluid-core code. ```task id: FLUID-WP-0002-T04 -status: done +status: todo priority: high state_hub_task_id: "ba9933b2-0b92-55c4-96b0-b46c955e25a6" ``` @@ -82,7 +82,7 @@ types are prohibited — drift between spec and implementation must fail CI. ```task id: FLUID-WP-0002-T05 -status: done +status: todo priority: high state_hub_task_id: "f835faf7-98f4-514f-937d-83b86b4b44a5" ``` @@ -95,7 +95,7 @@ build breaks. ```task id: FLUID-WP-0002-T06 -status: done +status: todo priority: medium state_hub_task_id: "2b0e42e0-c6ff-534e-8de4-3ff333af9e78" ``` @@ -107,7 +107,7 @@ identifier prefix helpers from schema doc §16: `H- R- E- P- BR- D- EV- F- C-`. ```task id: FLUID-WP-0002-T07 -status: done +status: todo priority: medium state_hub_task_id: "83b10a34-08e3-5ceb-81f4-f148a3d87183" ```