Generate contract types, pin fixtures to spec, add build and ADRs
Some checks failed
ci / build (push) Failing after 1m6s

Completes FLUID-WP-0002. The record types in internal/contract are
generated from schemas/ by a dependency-free generator; fixtures
transcribed from the spec's worked examples validate against those
schemas and round-trip through the generated types with
DisallowUnknownFields, so a spec change that misses the schemas fails
CI rather than drifting silently.

Adds identifier prefix helpers, Makefile, GitHub Actions, and five ADRs
recording the Go choice, out-of-process attachment, the wire contract as
boundary, the evidence store, and revision identity.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014KmVxhJ35tCo7rE7UnLwWu

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 1116572@bnt-lap001
Assistant-Session: 8ba9bb93-a72a-4883-b189-2499cce5c400
This commit is contained in:
tegwick 2026-09-04 02:03:12 +02:00
parent fbbf56df7a
commit 76912adef8
44 changed files with 4010 additions and 7 deletions

42
.github/workflows/ci.yml vendored Normal file
View file

@ -0,0 +1,42 @@
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

54
Makefile Normal file
View file

@ -0,0 +1,54 @@
# 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}'

View file

@ -0,0 +1,28 @@
# 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:<digest>`. 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 |

View file

@ -0,0 +1,37 @@
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"

View file

@ -0,0 +1,24 @@
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"

View file

@ -0,0 +1,41 @@
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: []

View file

@ -0,0 +1,22 @@
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

View file

@ -0,0 +1,156 @@
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"

View file

@ -0,0 +1,38 @@
{
"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"
]
}
}
}

View file

@ -0,0 +1,22 @@
{
"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"
}
}

View file

@ -0,0 +1,44 @@
{
"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"
]
}
}

View file

@ -0,0 +1,26 @@
{
"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"
]
}
}

View file

@ -0,0 +1,164 @@
{
"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"
}
}

View file

@ -0,0 +1,25 @@
{
"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"
}
}

View file

@ -0,0 +1,35 @@
{
"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"
}
}

View file

@ -0,0 +1,150 @@
{
"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"
]
}
}
}

View file

@ -0,0 +1,25 @@
{
"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"
}
}

View file

@ -0,0 +1,34 @@
{
"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"
}
}

View file

@ -0,0 +1,29 @@
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"

View file

@ -0,0 +1,32 @@
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"

View file

@ -0,0 +1,134 @@
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"

View file

@ -0,0 +1,20 @@
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"

View file

@ -0,0 +1,32 @@
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"

193
conformance/validate_schemas.py Executable file
View file

@ -0,0 +1,193 @@
#!/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 "<root>"
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())

View file

@ -0,0 +1,46 @@
# 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` §4243) 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.

View file

@ -0,0 +1,43 @@
# 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.

View file

@ -0,0 +1,43 @@
# 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.

View file

@ -0,0 +1,37 @@
# 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.

View file

@ -0,0 +1,47 @@
# 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.

View file

@ -0,0 +1,100 @@
// 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"`
}

View file

@ -0,0 +1,207 @@
// 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 '<kind>:<locator>' 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

View file

@ -0,0 +1,203 @@
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
}

View file

@ -0,0 +1,48 @@
// 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"`
}

View file

@ -0,0 +1,108 @@
// 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"`
}

View file

@ -0,0 +1,33 @@
// 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"`
}

View file

@ -0,0 +1,254 @@
// 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"`
}

91
internal/contract/ids.go Normal file
View file

@ -0,0 +1,91 @@
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
}

View file

@ -0,0 +1,58 @@
// 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"`
}

View file

@ -0,0 +1,215 @@
// 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"`
}

View file

@ -0,0 +1,314 @@
// 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"`
}

View file

@ -0,0 +1,82 @@
// 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"`
}

View file

@ -0,0 +1,172 @@
// 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"`
}

495
tools/schemagen/main.go Normal file
View file

@ -0,0 +1,495 @@
// 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)
}

View file

@ -4,7 +4,7 @@ type: workplan
title: "FLUID foundations and the wire contract"
domain: infotech
repo: fluid-core
status: active
status: done
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: todo
status: done
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: todo
status: done
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: todo
status: done
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: todo
status: done
priority: high
state_hub_task_id: "f835faf7-98f4-514f-937d-83b86b4b44a5"
```
@ -95,7 +95,7 @@ build breaks.
```task
id: FLUID-WP-0002-T06
status: todo
status: done
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: todo
status: done
priority: medium
state_hub_task_id: "83b10a34-08e3-5ceb-81f4-f148a3d87183"
```