diff --git a/schemas/backend-requirement.schema.json b/schemas/backend-requirement.schema.json new file mode 100644 index 0000000..697b17e --- /dev/null +++ b/schemas/backend-requirement.schema.json @@ -0,0 +1,83 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://fluid.dev/schemas/backend-requirement.schema.json", + "title": "FLUID backend capability requirement", + "description": "Capability the interface needs but has no authority to build. FLUID escalates rather than crossing the boundary (FluidAPIStandards.md section 30).", + "type": "object", + "properties": { + "fluid_backend_requirement": { + "type": "object", + "properties": { + "schema_version": { "$ref": "common.schema.json#/$defs/schemaVersion" }, + "id": { "$ref": "common.schema.json#/$defs/backendRequirementId" }, + "originating_interface": { "$ref": "common.schema.json#/$defs/interfaceId" }, + "originating_revision": { "oneOf": [{ "$ref": "common.schema.json#/$defs/revisionId" }, { "type": "null" }] }, + "originating_hypothesis": { "oneOf": [{ "$ref": "common.schema.json#/$defs/hypothesisId" }, { "type": "null" }] }, + "backend_service": { "type": "string", "minLength": 1 }, + + "capability": { + "type": "object", + "properties": { + "title": { "type": "string", "minLength": 1 }, + "description": { "type": "string", "minLength": 1 } + }, + "required": ["title", "description"], + "additionalProperties": false + }, + + "semantics": { + "type": "object", + "properties": { + "consistency": { "type": "string" }, + "required_fields": { "type": "array", "items": { "type": "string" } } + }, + "additionalProperties": true + }, + + "quality": { + "type": "object", + "properties": { + "p95_latency_ms": { "type": "number", "minimum": 0 }, + "availability": { "$ref": "common.schema.json#/$defs/unitInterval" } + }, + "additionalProperties": true + }, + + "security": { + "type": "object", + "properties": { + "authorization_scope": { "type": "string" }, + "tenant_isolation": { "type": "string" } + }, + "additionalProperties": true + }, + + "expected_usage": { + "type": "object", + "properties": { + "requests_per_day": { "type": "integer", "minimum": 0 } + }, + "additionalProperties": true + }, + + "urgency": { "enum": ["LOW", "MEDIUM", "HIGH"] }, + + "disposition": { + "type": "object", + "description": "The backend decides. Repeated OUT_OF_SCOPE feeds boundary learning (ArchitectureBlueprint.md section 22).", + "properties": { + "state": { "enum": ["OPEN", "ACCEPTED", "PLANNED", "AVAILABLE", "PARTIALLY_AVAILABLE", "OUT_OF_SCOPE", "REJECTED", "SUPERSEDED"] }, + "reason": { "type": ["string", "null"] }, + "target_ref": { "type": ["string", "null"] } + }, + "required": ["state"], + "additionalProperties": false + } + }, + "required": ["schema_version", "id", "originating_interface", "backend_service", "capability", "disposition"], + "additionalProperties": false + } + }, + "required": ["fluid_backend_requirement"], + "additionalProperties": false +} diff --git a/schemas/common.schema.json b/schemas/common.schema.json new file mode 100644 index 0000000..06e67fe --- /dev/null +++ b/schemas/common.schema.json @@ -0,0 +1,161 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://fluid.dev/schemas/common.schema.json", + "title": "FLUID common definitions", + "description": "Shared identifier, reference and measurement types. Identifier prefixes follow FluidHypothesisRevisionSchema.md section 16.", + "$defs": { + "schemaVersion": { + "type": "string", + "const": "0.1" + }, + "interfaceId": { + "type": "string", + "minLength": 1, + "pattern": "^[a-z0-9]([a-z0-9-]*[a-z0-9])?$", + "description": "Stable identifier of the owning interface." + }, + "hypothesisId": { "type": "string", "pattern": "^H-[0-9A-Za-z_-]+$" }, + "revisionId": { "type": "string", "pattern": "^R-[0-9A-Za-z_-]+$" }, + "experimentId": { "type": "string", "pattern": "^E-[0-9A-Za-z_-]+$" }, + "pressureId": { "type": "string", "pattern": "^P-[0-9A-Za-z_-]+$" }, + "backendRequirementId": { "type": "string", "pattern": "^BR-[0-9A-Za-z_-]+$" }, + "decisionId": { "type": "string", "pattern": "^D-[0-9A-Za-z_-]+$" }, + "eventId": { "type": "string", "pattern": "^EV-[0-9A-Za-z_-]+$" }, + "feedbackId": { "type": "string", "pattern": "^F-[0-9A-Za-z_-]+$" }, + "cohortId": { "type": "string", "minLength": 1 }, + + "timestamp": { "type": "string", "format": "date-time" }, + + "evidenceRef": { + "type": "string", + "minLength": 1, + "description": "Opaque reference into the evidence store, conventionally ':' such as 'telemetry:invoice-pattern-2026w36'." + }, + "artifactRef": { + "type": "string", + "minLength": 1, + "description": "Opaque reference into the artifact store." + }, + "digest": { + "type": "string", + "pattern": "^sha256:[a-f0-9]{64}$", + "description": "Content address of an artifact." + }, + + "actor": { + "type": "object", + "description": "Who or what performed an action. FLUID distinguishes these because trust levels differ (ArchitectureBlueprint.md section 47).", + "properties": { + "type": { "enum": ["human", "daimon", "policy", "system", "consumer"] }, + "id": { "type": "string", "minLength": 1 }, + "model_or_agent": { "type": "string" } + }, + "required": ["type", "id"], + "additionalProperties": false + }, + + "observationWindow": { + "type": "object", + "properties": { + "start": { "$ref": "common.schema.json#/$defs/timestamp" }, + "end": { + "oneOf": [ + { "$ref": "common.schema.json#/$defs/timestamp" }, + { "type": "null" } + ], + "description": "Null while the window is still open." + } + }, + "required": ["start"], + "additionalProperties": false + }, + + "unitInterval": { "type": "number", "minimum": 0, "maximum": 1 }, + + "pressureClass": { + "enum": [ + "natural_usage", + "successful_but_inefficient_usage", + "recoverable_misunderstanding", + "repeated_expectation_mismatch", + "poor_discoverability", + "missing_interface_capability", + "missing_backend_capability", + "out_of_scope_demand", + "prohibited_demand", + "implementation_failure" + ], + "description": "FluidAPIStandards.md section 13." + }, + + "adaptationClass": { + "enum": ["presentation", "contract", "composition", "implementation", "requirement_escalation"], + "description": "FluidAPIStandards.md section 16." + }, + + "guardrail": { + "type": "object", + "description": "A constraint that must not regress beyond threshold. Guardrails are evaluated deterministically.", + "properties": { + "metric": { "type": "string", "minLength": 1 }, + "operator": { "enum": ["<", "<=", "==", "!=", ">=", ">"] }, + "threshold": { "type": ["number", "boolean", "string"] } + }, + "required": ["metric", "operator", "threshold"], + "additionalProperties": false + }, + + "expectedOutcome": { + "type": "object", + "properties": { + "metric": { "type": "string", "minLength": 1 }, + "cohort": { "type": "string" }, + "baseline": { "type": "number" }, + "target": { "type": "number" }, + "direction": { "enum": ["lower", "higher", "unchanged"] } + }, + "required": ["metric", "target", "direction"], + "additionalProperties": false + }, + + "complexityDelta": { + "type": "object", + "description": "Standard dimensions, implementation-defined scoring (FluidAPIStandards.md section 22).", + "properties": { + "surface_area": { "type": "number" }, + "concept_count": { "type": "number" }, + "operation_count": { "type": "number" }, + "parameter_dimensionality": { "type": "number" }, + "semantic_overlap": { "type": "number" }, + "alternative_path_count": { "type": "number" }, + "dependency_count": { "type": "number" }, + "backend_composition_count": { "type": "number" }, + "exception_count": { "type": "number" }, + "active_revision_count": { "type": "number" }, + "cognitive_load": { "type": "number" } + }, + "additionalProperties": false + }, + + "fitnessDimensions": { + "type": "object", + "description": "FluidAPIStandards.md section 20. No universal scalar is defined on purpose.", + "properties": { + "client_utility": { "type": "number" }, + "correctness": { "type": "number" }, + "reliability": { "type": "number" }, + "availability": { "type": "number" }, + "performance": { "type": "number" }, + "discoverability": { "type": "number" }, + "simplicity": { "type": "number" }, + "compatibility": { "type": "number" }, + "security": { "type": "number" }, + "maintainability": { "type": "number" }, + "operational_cost": { "type": "number" }, + "implementation_cost": { "type": "number" }, + "resource_consumption": { "type": "number" } + }, + "additionalProperties": false + } + } +} diff --git a/schemas/event.schema.json b/schemas/event.schema.json new file mode 100644 index 0000000..cf2a3a3 --- /dev/null +++ b/schemas/event.schema.json @@ -0,0 +1,35 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://fluid.dev/schemas/event.schema.json", + "title": "FLUID audit event", + "description": "Append-only record of a material lifecycle transition. FLUID evolution is itself a system behavior that must remain reconstructable (FluidHypothesisRevisionSchema.md section 15).", + "type": "object", + "properties": { + "fluid_event": { + "type": "object", + "properties": { + "schema_version": { "$ref": "common.schema.json#/$defs/schemaVersion" }, + "id": { "$ref": "common.schema.json#/$defs/eventId" }, + "occurred_at": { "$ref": "common.schema.json#/$defs/timestamp" }, + "entity_type": { "enum": ["pressure", "hypothesis", "revision", "experiment", "backend_requirement", "intent", "decision", "routing_policy"] }, + "entity_id": { "type": "string", "minLength": 1 }, + "event_type": { "type": "string", "minLength": 1 }, + "actor": { "$ref": "common.schema.json#/$defs/actor" }, + "inputs": { + "type": "array", + "items": { "type": "string" }, + "description": "Identifiers of the records this transition drew on." + }, + "reason": { "type": "string" }, + "evidence_refs": { + "type": "array", + "items": { "$ref": "common.schema.json#/$defs/evidenceRef" } + } + }, + "required": ["schema_version", "id", "occurred_at", "entity_type", "entity_id", "event_type", "actor"], + "additionalProperties": false + } + }, + "required": ["fluid_event"], + "additionalProperties": false +} diff --git a/schemas/experiment.schema.json b/schemas/experiment.schema.json new file mode 100644 index 0000000..5e673d8 --- /dev/null +++ b/schemas/experiment.schema.json @@ -0,0 +1,99 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://fluid.dev/schemas/experiment.schema.json", + "title": "FLUID experiment record", + "description": "Connects hypotheses to revisions under bounded conditions. Experiments must be interruptible (ArchitectureBlueprint.md section 16).", + "type": "object", + "properties": { + "fluid_experiment": { + "type": "object", + "properties": { + "schema_version": { "$ref": "common.schema.json#/$defs/schemaVersion" }, + "id": { "$ref": "common.schema.json#/$defs/experimentId" }, + "interface_id": { "$ref": "common.schema.json#/$defs/interfaceId" }, + "hypothesis_refs": { + "type": "array", + "items": { "$ref": "common.schema.json#/$defs/hypothesisId" }, + "minItems": 1, + "description": "An experiment without a hypothesis measures nothing in particular." + }, + "mechanism": { + "enum": ["sandbox", "shadow", "synthetic", "replay", "canary", "opt_in", "cohort", "tenant", "percentage"] + }, + "control_revision": { "$ref": "common.schema.json#/$defs/revisionId" }, + "candidate_revisions": { + "type": "array", + "items": { "$ref": "common.schema.json#/$defs/revisionId" }, + "minItems": 1 + }, + "cohorts": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/cohortId" } }, + "allocation": { + "type": "object", + "description": "Shares by revision id or by the reserved key 'control'. Allocation is deterministic and enacted by the router, never by this record.", + "additionalProperties": { "$ref": "common.schema.json#/$defs/unitInterval" }, + "minProperties": 2 + }, + "start_at": { "$ref": "common.schema.json#/$defs/timestamp" }, + "planned_end_at": { "oneOf": [{ "$ref": "common.schema.json#/$defs/timestamp" }, { "type": "null" }] }, + "max_duration_hours": { "type": "number", "exclusiveMinimum": 0 }, + + "metrics": { + "type": "object", + "description": "Primary metrics and guardrails are kept distinct so a scalar can never hide a regression (ArchitectureBlueprint.md section 48.5).", + "properties": { + "primary": { "type": "array", "items": { "type": "string" }, "minItems": 1 }, + "guardrails": { "type": "array", "items": { "type": "string" } }, + "secondary": { "type": "array", "items": { "type": "string" } }, + "learning": { "type": "array", "items": { "type": "string" } } + }, + "required": ["primary"], + "additionalProperties": false + }, + + "start_conditions": { "type": "array", "items": { "type": "string" } }, + "stop_conditions": { + "type": "array", + "items": { "type": "string" }, + "minItems": 1, + "description": "Every experiment has a stop condition (ArchitectureBlueprint.md section 55, invariant 7)." + }, + + "amendments": { + "type": "array", + "description": "Success criteria may not be changed after results are visible without recording the amendment (ArchitectureBlueprint.md section 18).", + "items": { + "type": "object", + "properties": { + "at": { "$ref": "common.schema.json#/$defs/timestamp" }, + "actor": { "$ref": "common.schema.json#/$defs/actor" }, + "change": { "type": "string", "minLength": 1 }, + "reason": { "type": "string", "minLength": 1 } + }, + "required": ["at", "actor", "change", "reason"], + "additionalProperties": false + } + }, + + "result": { + "type": "object", + "properties": { + "state": { "enum": ["PLANNED", "RUNNING", "STOPPED", "COMPLETED", "ABANDONED"] }, + "preferred_revision": { "oneOf": [{ "$ref": "common.schema.json#/$defs/revisionId" }, { "type": "null" }] }, + "stopped_reason": { "type": ["string", "null"] }, + "evidence_refs": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/evidenceRef" } } + }, + "required": ["state"], + "additionalProperties": false + } + }, + "required": [ + "schema_version", "id", "interface_id", "hypothesis_refs", + "control_revision", "candidate_revisions", "allocation", + "metrics", "stop_conditions", "result" + ], + "additionalProperties": false + } + }, + "required": ["fluid_experiment"], + "additionalProperties": false +} diff --git a/schemas/feedback.schema.json b/schemas/feedback.schema.json new file mode 100644 index 0000000..8e7b31d --- /dev/null +++ b/schemas/feedback.schema.json @@ -0,0 +1,40 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://fluid.dev/schemas/feedback.schema.json", + "title": "FLUID explicit consumer feedback", + "description": "Structured feedback from a consumer. Treated as evidence and never as authority to change anything (FluidAPIStandards.md section 15).", + "type": "object", + "properties": { + "fluid_feedback": { + "type": "object", + "properties": { + "schema_version": { "$ref": "common.schema.json#/$defs/schemaVersion" }, + "id": { "$ref": "common.schema.json#/$defs/feedbackId" }, + "interface_id": { "$ref": "common.schema.json#/$defs/interfaceId" }, + "received_at": { "$ref": "common.schema.json#/$defs/timestamp" }, + "cohort": { "$ref": "common.schema.json#/$defs/cohortId" }, + "revision": { "$ref": "common.schema.json#/$defs/revisionId" }, + "goal": { "type": "string" }, + "attempt": { "type": "string" }, + "attempted_operations": { "type": "array", "items": { "type": "string" } }, + "outcome": { "type": "string" }, + "missing_capability": { "type": "string" }, + "workaround": { "type": ["string", "array"], "items": { "type": "string" } }, + "impact": { + "type": "object", + "properties": { + "description": { "type": "string" }, + "calls": { "type": "integer", "minimum": 0 }, + "latency_ms": { "type": "number", "minimum": 0 } + }, + "additionalProperties": false + }, + "confidence": { "$ref": "common.schema.json#/$defs/unitInterval" } + }, + "required": ["schema_version", "id", "interface_id", "received_at", "goal"], + "additionalProperties": false + } + }, + "required": ["fluid_feedback"], + "additionalProperties": false +} diff --git a/schemas/hypothesis.schema.json b/schemas/hypothesis.schema.json new file mode 100644 index 0000000..e29181e --- /dev/null +++ b/schemas/hypothesis.schema.json @@ -0,0 +1,228 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://fluid.dev/schemas/hypothesis.schema.json", + "title": "FLUID hypothesis record", + "description": "Why an interface change should be explored. Observation, explanation, prediction, intervention and result stay separate on purpose (FluidHypothesisRevisionSchema.md section 18).", + "type": "object", + "properties": { + "fluid_hypothesis": { + "type": "object", + "properties": { + "schema_version": { "$ref": "common.schema.json#/$defs/schemaVersion" }, + "id": { "$ref": "common.schema.json#/$defs/hypothesisId" }, + "interface_id": { "$ref": "common.schema.json#/$defs/interfaceId" }, + "state": { + "enum": ["DRAFT", "READY", "PRIORITIZED", "DESIGNING", "EXPERIMENTING", "EVALUATING", "ACCEPTED", "REJECTED", "SUPERSEDED", "DEFERRED"] + }, + "created_at": { "$ref": "common.schema.json#/$defs/timestamp" }, + "created_by": { "$ref": "common.schema.json#/$defs/actor" }, + "title": { "type": "string", "minLength": 1 }, + + "observation": { + "type": "object", + "description": "What was seen. Not what it means.", + "properties": { + "summary": { "type": "string", "minLength": 1 }, + "evidence_refs": { + "type": "array", + "items": { "$ref": "common.schema.json#/$defs/evidenceRef" }, + "minItems": 1 + }, + "affected_cohorts": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/cohortId" } }, + "observation_window": { "$ref": "common.schema.json#/$defs/observationWindow" } + }, + "required": ["summary", "evidence_refs"], + "additionalProperties": false + }, + + "pressure": { + "type": "object", + "properties": { + "classes": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/pressureClass" }, "minItems": 1 }, + "pressure_refs": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/pressureId" } }, + "severity": { "$ref": "common.schema.json#/$defs/unitInterval" }, + "confidence": { "$ref": "common.schema.json#/$defs/unitInterval" } + }, + "required": ["classes"], + "additionalProperties": false + }, + + "explanation": { + "type": "object", + "description": "What we think explains the observation.", + "properties": { + "claim": { "type": "string", "minLength": 1 }, + "reach": { + "type": "object", + "description": "Explanatory reach is a prioritization signal, never a correctness claim (FluidAPIStandards.md section 18).", + "properties": { + "score": { "type": "number" }, + "explains": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/pressureId" } }, + "notes": { "type": "string" } + }, + "additionalProperties": false + } + }, + "required": ["claim"], + "additionalProperties": false + }, + + "proposed_adaptation": { + "type": "object", + "description": "What we propose to change.", + "properties": { + "class": { "$ref": "common.schema.json#/$defs/adaptationClass" }, + "summary": { "type": "string", "minLength": 1 }, + "candidate_contract": { + "type": "object", + "description": "Shape is protocol-specific and deliberately unconstrained.", + "additionalProperties": true + }, + "implementation_scope": { "enum": ["interface_only", "interface_and_backend", "documentation_only"] } + }, + "required": ["class", "summary"], + "additionalProperties": false + }, + + "backend_requirements": { + "type": "object", + "properties": { + "required": { "type": "boolean" }, + "requirement_refs": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/backendRequirementId" } } + }, + "required": ["required"], + "additionalProperties": false + }, + + "expected_outcomes": { + "type": "array", + "items": { "$ref": "common.schema.json#/$defs/expectedOutcome" }, + "minItems": 1, + "description": "A hypothesis must be falsifiable (FluidAPIStandards.md section 17)." + }, + "guardrails": { + "type": "array", + "items": { "$ref": "common.schema.json#/$defs/guardrail" } + }, + + "fitness_dimensions": { + "type": "object", + "properties": { + "expected": { "$ref": "common.schema.json#/$defs/fitnessDimensions" } + }, + "additionalProperties": false + }, + + "complexity": { + "type": "object", + "properties": { + "expected_delta": { "$ref": "common.schema.json#/$defs/complexityDelta" }, + "score": { "type": "number" } + }, + "required": ["expected_delta"], + "additionalProperties": false + }, + + "risk": { + "type": "object", + "properties": { + "level": { "enum": ["LOW", "MEDIUM", "HIGH", "CRITICAL"] }, + "reasons": { "type": "array", "items": { "type": "string" } } + }, + "required": ["level"], + "additionalProperties": false + }, + + "economics": { + "type": "object", + "description": "A hypothesis may be valuable but not yet worth exploring (ArchitectureBlueprint.md section 29).", + "properties": { + "estimated_experiment_cost": { "type": "number", "minimum": 0 }, + "estimated_implementation_cost": { "type": "number", "minimum": 0 }, + "currency": { "type": "string" }, + "expected_value_class": { "enum": ["LOW", "MEDIUM", "HIGH"] } + }, + "additionalProperties": false + }, + + "learning_value": { + "type": "object", + "properties": { + "score": { "type": "number" }, + "notes": { "type": "string" } + }, + "additionalProperties": false + }, + + "priority": { + "type": "object", + "properties": { + "score": { "type": "number" }, + "decided_by": { "type": "string" } + }, + "additionalProperties": false + }, + + "competition": { + "type": "object", + "description": "Competing hypotheses are legitimate (FluidAPIStandards.md section 19).", + "properties": { + "group_id": { "type": "string", "minLength": 1 }, + "alternatives": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/hypothesisId" } } + }, + "required": ["group_id"], + "additionalProperties": false + }, + + "success_criteria": { + "type": "object", + "properties": { "expression": { "type": "string", "minLength": 1 } }, + "required": ["expression"], + "additionalProperties": false + }, + "failure_criteria": { + "type": "object", + "properties": { "expression": { "type": "string", "minLength": 1 } }, + "required": ["expression"], + "additionalProperties": false + }, + + "candidate_revision_refs": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/revisionId" } }, + "experiment_refs": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/experimentId" } }, + + "outcome": { + "type": "object", + "description": "What happened afterwards. Null until evaluation completes.", + "properties": { + "status": { + "oneOf": [ + { "enum": ["CONFIRMED", "REFUTED", "INCONCLUSIVE"] }, + { "type": "null" } + ] + }, + "summary": { "type": ["string", "null"] }, + "evidence_refs": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/evidenceRef" } } + }, + "additionalProperties": false + }, + + "audit": { + "type": "object", + "properties": { + "decision_refs": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/decisionId" } }, + "immutable_event_refs": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/eventId" } } + }, + "additionalProperties": false + } + }, + "required": [ + "schema_version", "id", "interface_id", "state", "title", + "observation", "pressure", "explanation", "proposed_adaptation", + "expected_outcomes", "guardrails", "complexity", "risk", "success_criteria" + ], + "additionalProperties": false + } + }, + "required": ["fluid_hypothesis"], + "additionalProperties": false +} diff --git a/schemas/pressure.schema.json b/schemas/pressure.schema.json new file mode 100644 index 0000000..5d5a20f --- /dev/null +++ b/schemas/pressure.schema.json @@ -0,0 +1,53 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://fluid.dev/schemas/pressure.schema.json", + "title": "FLUID pressure record", + "description": "Evidence that the interface differs materially from consumer needs. Pressure is evidence, not truth (FluidAPIStandards.md section 13).", + "type": "object", + "properties": { + "fluid_pressure": { + "type": "object", + "properties": { + "schema_version": { "$ref": "common.schema.json#/$defs/schemaVersion" }, + "id": { "$ref": "common.schema.json#/$defs/pressureId" }, + "interface_id": { "$ref": "common.schema.json#/$defs/interfaceId" }, + "class": { "$ref": "common.schema.json#/$defs/pressureClass" }, + "first_seen": { "$ref": "common.schema.json#/$defs/timestamp" }, + "last_seen": { "$ref": "common.schema.json#/$defs/timestamp" }, + "affected_cohorts": { + "type": "array", + "items": { "$ref": "common.schema.json#/$defs/cohortId" } + }, + "frequency": { + "type": "object", + "properties": { + "observations": { "type": "integer", "minimum": 0 }, + "independent_consumers": { "type": "integer", "minimum": 0 } + }, + "additionalProperties": false + }, + "severity": { "$ref": "common.schema.json#/$defs/unitInterval" }, + "confidence": { "$ref": "common.schema.json#/$defs/unitInterval" }, + "summary": { "type": "string", "minLength": 1 }, + "evidence_refs": { + "type": "array", + "items": { "$ref": "common.schema.json#/$defs/evidenceRef" }, + "minItems": 1, + "description": "A pressure record without evidence references is not auditable and must be rejected." + }, + "status": { + "enum": ["OPEN", "ANALYZING", "EXPLAINED", "ADDRESSED", "DISMISSED", "OUT_OF_SCOPE"], + "description": "ArchitectureBlueprint.md section 9. Pressure may remain unresolved on purpose." + }, + "linked_hypotheses": { + "type": "array", + "items": { "$ref": "common.schema.json#/$defs/hypothesisId" } + } + }, + "required": ["schema_version", "id", "interface_id", "class", "first_seen", "last_seen", "summary", "evidence_refs", "status"], + "additionalProperties": false + } + }, + "required": ["fluid_pressure"], + "additionalProperties": false +} diff --git a/schemas/revision-descriptor.schema.json b/schemas/revision-descriptor.schema.json new file mode 100644 index 0000000..3400eac --- /dev/null +++ b/schemas/revision-descriptor.schema.json @@ -0,0 +1,124 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://fluid.dev/schemas/revision-descriptor.schema.json", + "title": "FLUID revision descriptor", + "description": "The runtime-facing projection of a revision, deliberately small enough to be consumed deterministically by infrastructure (ArchitectureBlueprint.md section 36). This is a wire artifact: the gateway must be able to read it without any fluid-core library.", + "type": "object", + "properties": { + "revision": { + "type": "object", + "properties": { + "schema_version": { "$ref": "common.schema.json#/$defs/schemaVersion" }, + "id": { "$ref": "common.schema.json#/$defs/revisionId" }, + "interface": { "$ref": "common.schema.json#/$defs/interfaceId" }, + "state": { + "enum": ["created", "verified", "experiment", "candidate", "stable", "deprecated", "retired"], + "description": "The router refuses to route to created, failed or retired revisions (ArchitectureBlueprint.md section 5.3)." + }, + + "contract": { + "type": "object", + "properties": { + "type": { "enum": ["openapi", "graphql", "protobuf", "asyncapi", "jsonschema", "custom"] }, + "digest": { "$ref": "common.schema.json#/$defs/digest" }, + "source": { "type": "string", "description": "Where the contract artifact can be fetched." } + }, + "required": ["type", "digest"], + "additionalProperties": false + }, + + "runtime": { + "type": "object", + "description": "Out-of-process attachment: the adapter is an upstream reached over the network, so it may be implemented in any language.", + "properties": { + "upstream": { + "type": "string", + "format": "uri", + "description": "Base URL of the adapter process serving this revision." + }, + "image": { "type": "string" }, + "digest": { "$ref": "common.schema.json#/$defs/digest" }, + "timeout_ms": { "type": "integer", "exclusiveMinimum": 0, "default": 5000 }, + "retry": { + "type": "object", + "properties": { + "max_attempts": { "type": "integer", "minimum": 1, "default": 1 }, + "backoff_ms": { "type": "integer", "minimum": 0 }, + "retry_on": { "type": "array", "items": { "type": "string" } } + }, + "additionalProperties": false + }, + "circuit_breaker": { + "type": "object", + "properties": { + "failure_threshold": { "type": "integer", "minimum": 1 }, + "reset_after_ms": { "type": "integer", "exclusiveMinimum": 0 } + }, + "additionalProperties": false + } + }, + "required": ["upstream"], + "additionalProperties": false + }, + + "intent": { + "type": "object", + "description": "Which interface evolution intent governs this revision. Required: a revision with no governing intent cannot be audited.", + "properties": { + "version": { "type": "string", "minLength": 1 }, + "digest": { "$ref": "common.schema.json#/$defs/digest" } + }, + "required": ["version"], + "additionalProperties": false + }, + + "routing": { + "type": "object", + "properties": { + "eligible_cohorts": { + "type": "array", + "items": { "$ref": "common.schema.json#/$defs/cohortId" }, + "description": "Empty or absent means all cohorts are eligible." + }, + "max_traffic_share": { + "$ref": "common.schema.json#/$defs/unitInterval", + "description": "Ceiling the router enforces regardless of what a routing policy asks for." + } + }, + "additionalProperties": false + }, + + "policy": { + "type": "object", + "properties": { + "compatibility": { + "enum": ["backward_compatible", "forward_compatible", "additive", "behavioral_change", "breaking", "migration_only", "internal_only"] + }, + "security_check": { "enum": ["pending", "passed", "failed"] }, + "policy_check": { "enum": ["pending", "passed", "failed"] }, + "rollback_to": { "oneOf": [{ "$ref": "common.schema.json#/$defs/revisionId" }, { "type": "null" }] } + }, + "required": ["compatibility", "security_check"], + "additionalProperties": false + }, + + "signature": { + "type": "object", + "description": "The router accepts only signed published descriptors (ArchitectureBlueprint.md section 35). The signature covers the canonical form of this document with the signature member removed.", + "properties": { + "algorithm": { "enum": ["ed25519"] }, + "key_id": { "type": "string", "minLength": 1 }, + "value": { "type": "string", "contentEncoding": "base64" }, + "signed_at": { "$ref": "common.schema.json#/$defs/timestamp" } + }, + "required": ["algorithm", "key_id", "value"], + "additionalProperties": false + } + }, + "required": ["schema_version", "id", "interface", "state", "contract", "runtime", "intent", "policy"], + "additionalProperties": false + } + }, + "required": ["revision"], + "additionalProperties": false +} diff --git a/schemas/revision.schema.json b/schemas/revision.schema.json new file mode 100644 index 0000000..9a9d7ab --- /dev/null +++ b/schemas/revision.schema.json @@ -0,0 +1,235 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://fluid.dev/schemas/revision.schema.json", + "title": "FLUID revision record", + "description": "What deterministic interface state was built, verified, exposed and measured. Published revisions should be immutable; corrections create successors (FluidAPIStandards.md section 8).", + "type": "object", + "properties": { + "fluid_revision": { + "type": "object", + "properties": { + "schema_version": { "$ref": "common.schema.json#/$defs/schemaVersion" }, + "id": { "$ref": "common.schema.json#/$defs/revisionId" }, + "interface_id": { "$ref": "common.schema.json#/$defs/interfaceId" }, + "revision_number": { "type": "integer", "minimum": 0 }, + "parent_revision": { + "oneOf": [ + { "$ref": "common.schema.json#/$defs/revisionId" }, + { "type": "null" } + ], + "description": "Null marks genesis." + }, + "state": { + "enum": ["CREATED", "VERIFIED", "EXPERIMENT", "CANDIDATE", "STABLE", "DEPRECATED", "RETIRED"] + }, + "created_at": { "$ref": "common.schema.json#/$defs/timestamp" }, + "created_by": { "$ref": "common.schema.json#/$defs/actor" }, + + "contract": { + "type": "object", + "properties": { + "type": { "enum": ["openapi", "graphql", "protobuf", "asyncapi", "jsonschema", "custom"] }, + "version": { "type": "string" }, + "artifact_ref": { "$ref": "common.schema.json#/$defs/artifactRef" }, + "digest": { "$ref": "common.schema.json#/$defs/digest" } + }, + "required": ["type", "artifact_ref", "digest"], + "additionalProperties": false + }, + + "implementation": { + "type": "object", + "properties": { + "artifact_ref": { "$ref": "common.schema.json#/$defs/artifactRef" }, + "source_ref": { "type": "string" }, + "build_ref": { "type": "string" }, + "digest": { "$ref": "common.schema.json#/$defs/digest" } + }, + "required": ["artifact_ref", "digest"], + "additionalProperties": false + }, + + "interface_evolution_intent": { + "type": "object", + "description": "Every revision is governed by a specific intent version, so audit can ask whether the change was valid under the intent that existed at the time (ArchitectureBlueprint.md section 27).", + "properties": { + "version": { "type": "string", "minLength": 1 }, + "artifact_ref": { "$ref": "common.schema.json#/$defs/artifactRef" } + }, + "required": ["version"], + "additionalProperties": false + }, + + "originating_hypotheses": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/hypothesisId" } }, + "backend_requirements": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/backendRequirementId" } }, + + "compatibility": { + "type": "object", + "properties": { + "class": { + "enum": ["BACKWARD_COMPATIBLE", "FORWARD_COMPATIBLE", "ADDITIVE", "BEHAVIORAL_CHANGE", "BREAKING", "MIGRATION_ONLY", "INTERNAL_ONLY"] + }, + "breaking_changes": { "type": "array", "items": { "type": "string" } }, + "deprecations": { "type": "array", "items": { "type": "string" } }, + "supersedes": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/revisionId" } }, + "compatibility_evidence_refs": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/evidenceRef" } } + }, + "required": ["class"], + "additionalProperties": false + }, + + "adaptation_classes": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/adaptationClass" } }, + + "verification": { + "type": "object", + "description": "The primary safety barrier between adaptive generation and deterministic runtime (ArchitectureBlueprint.md section 15).", + "properties": { + "status": { "enum": ["PENDING", "PASSED", "FAILED"] }, + "test_refs": { "type": "array", "items": { "type": "string" } }, + "security_check": { "enum": ["PENDING", "PASSED", "FAILED"] }, + "policy_check": { "enum": ["PENDING", "PASSED", "FAILED"] } + }, + "required": ["status"], + "additionalProperties": false + }, + + "complexity": { + "type": "object", + "properties": { + "before": { "$ref": "common.schema.json#/$defs/complexityDelta" }, + "after": { "$ref": "common.schema.json#/$defs/complexityDelta" }, + "delta_score": { "type": "number" }, + "budget_status": { "enum": ["WITHIN_BUDGET", "AT_BUDGET", "OVER_BUDGET"] } + }, + "additionalProperties": false + }, + + "deployment": { + "type": "object", + "properties": { + "environments": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "started_at": { "$ref": "common.schema.json#/$defs/timestamp" }, + "routing_policy_ref": { "type": "string" } + }, + "required": ["name"], + "additionalProperties": false + } + }, + "current_exposure": { + "type": "object", + "properties": { + "traffic_share": { "$ref": "common.schema.json#/$defs/unitInterval" }, + "cohorts": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/cohortId" } } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + + "fitness": { + "type": "object", + "properties": { + "baseline_revision": { "oneOf": [{ "$ref": "common.schema.json#/$defs/revisionId" }, { "type": "null" }] }, + "measurement_window": { "$ref": "common.schema.json#/$defs/observationWindow" }, + "metrics": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "baseline": { "type": ["number", "null"] }, + "current": { "type": ["number", "null"] }, + "target": { "type": ["number", "null"] }, + "guardrail": { "type": ["number", "null"] } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + }, + + "adoption": { + "type": "object", + "description": "Adoption is evidence, not proof of quality (FluidAPIStandards.md section 32).", + "properties": { + "eligible_consumers": { "type": ["integer", "null"], "minimum": 0 }, + "active_consumers": { "type": ["integer", "null"], "minimum": 0 }, + "adoption_rate": { "oneOf": [{ "$ref": "common.schema.json#/$defs/unitInterval" }, { "type": "null" }] }, + "retained_adoption_rate": { "oneOf": [{ "$ref": "common.schema.json#/$defs/unitInterval" }, { "type": "null" }] } + }, + "additionalProperties": false + }, + + "economics": { + "type": "object", + "properties": { + "build_cost": { "type": "number", "minimum": 0 }, + "experiment_cost_to_date": { "type": "number", "minimum": 0 }, + "currency": { "type": "string" } + }, + "additionalProperties": false + }, + + "promotion": { + "type": "object", + "properties": { + "eligible": { "type": "boolean" }, + "recommended_state": { + "oneOf": [ + { "enum": ["CREATED", "VERIFIED", "EXPERIMENT", "CANDIDATE", "STABLE", "DEPRECATED", "RETIRED"] }, + { "type": "null" } + ] + }, + "recommendation_reason": { "type": ["string", "null"] }, + "authorized_by": { "oneOf": [{ "$ref": "common.schema.json#/$defs/actor" }, { "type": "null" }] }, + "authorized_at": { "oneOf": [{ "$ref": "common.schema.json#/$defs/timestamp" }, { "type": "null" }] } + }, + "additionalProperties": false + }, + + "rollback": { + "type": "object", + "properties": { + "supported": { "type": "boolean" }, + "target_revision": { "oneOf": [{ "$ref": "common.schema.json#/$defs/revisionId" }, { "type": "null" }] }, + "procedure_ref": { "type": "string" } + }, + "required": ["supported"], + "additionalProperties": false + }, + + "provenance": { + "type": "object", + "properties": { + "decision_refs": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/decisionId" } }, + "experiment_refs": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/experimentId" } }, + "telemetry_refs": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/evidenceRef" } } + }, + "additionalProperties": false + }, + + "audit": { + "type": "object", + "properties": { + "immutable_event_refs": { "type": "array", "items": { "$ref": "common.schema.json#/$defs/eventId" } } + }, + "additionalProperties": false + } + }, + "required": [ + "schema_version", "id", "interface_id", "revision_number", "parent_revision", + "state", "contract", "implementation", "interface_evolution_intent", + "compatibility", "verification", "rollback", "provenance" + ], + "additionalProperties": false + } + }, + "required": ["fluid_revision"], + "additionalProperties": false +} diff --git a/schemas/routing-policy.schema.json b/schemas/routing-policy.schema.json new file mode 100644 index 0000000..7717197 --- /dev/null +++ b/schemas/routing-policy.schema.json @@ -0,0 +1,71 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://fluid.dev/schemas/routing-policy.schema.json", + "title": "FLUID routing policy", + "description": "Deterministic routing written by the experiment controller and consumed by the revision router. The controller never touches traffic itself; this separation keeps experimental intent out of the runtime decision mechanism (ArchitectureBlueprint.md section 17).", + "type": "object", + "properties": { + "routing_policy": { + "type": "object", + "properties": { + "schema_version": { "$ref": "common.schema.json#/$defs/schemaVersion" }, + "id": { "type": "string", "minLength": 1 }, + "interface": { "$ref": "common.schema.json#/$defs/interfaceId" }, + "generation": { + "type": "integer", + "minimum": 0, + "description": "Monotonic. The router ignores a policy older than the one it holds." + }, + "issued_at": { "$ref": "common.schema.json#/$defs/timestamp" }, + "issued_by": { "$ref": "common.schema.json#/$defs/actor" }, + + "default_revision": { + "$ref": "common.schema.json#/$defs/revisionId", + "description": "Where a request lands when no earlier resolution step matched." + }, + + "rules": { + "type": "array", + "description": "Evaluated in order; the first matching rule wins. Matching must be a pure function of the request and its cohort assignment.", + "items": { + "type": "object", + "properties": { + "cohort": { "$ref": "common.schema.json#/$defs/cohortId" }, + "tenant": { "type": "string" }, + "experiment": { "$ref": "common.schema.json#/$defs/experimentId" }, + "allocation": { + "type": "object", + "description": "Revision id to traffic share. Shares must sum to 1. Assignment of a given consumer must be stable across requests.", + "propertyNames": { "$ref": "common.schema.json#/$defs/revisionId" }, + "additionalProperties": { "$ref": "common.schema.json#/$defs/unitInterval" }, + "minProperties": 1 + }, + "sticky_by": { + "enum": ["consumer_id", "tenant", "correlation_id", "none"], + "default": "consumer_id", + "description": "What keeps a long-lived consumer on one side of an experiment." + } + }, + "required": ["allocation"], + "additionalProperties": false + } + }, + + "signature": { + "type": "object", + "properties": { + "algorithm": { "enum": ["ed25519"] }, + "key_id": { "type": "string", "minLength": 1 }, + "value": { "type": "string", "contentEncoding": "base64" } + }, + "required": ["algorithm", "key_id", "value"], + "additionalProperties": false + } + }, + "required": ["schema_version", "interface", "generation", "default_revision", "rules"], + "additionalProperties": false + } + }, + "required": ["routing_policy"], + "additionalProperties": false +} diff --git a/schemas/telemetry-envelope.schema.json b/schemas/telemetry-envelope.schema.json new file mode 100644 index 0000000..2a55b7f --- /dev/null +++ b/schemas/telemetry-envelope.schema.json @@ -0,0 +1,113 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://fluid.dev/schemas/telemetry-envelope.schema.json", + "title": "FLUID telemetry envelope", + "description": "The normalized interaction event. Designed for interface learning, not unrestricted behavioral capture: raw payload capture is never the default and redaction happens before an event reaches the store (ArchitectureBlueprint.md section 6.2).", + "type": "object", + "properties": { + "fluid_telemetry": { + "type": "object", + "properties": { + "schema_version": { "$ref": "common.schema.json#/$defs/schemaVersion" }, + "id": { "type": "string", "minLength": 1 }, + "occurred_at": { "$ref": "common.schema.json#/$defs/timestamp" }, + "interface_id": { "$ref": "common.schema.json#/$defs/interfaceId" }, + "kind": { + "enum": ["request", "error", "sequence", "adoption", "feedback"], + "description": "ArchitectureBlueprint.md section 6.1." + }, + + "correlation_id": { + "type": "string", + "description": "Ties an event to the response the consumer saw, and to the other events in its call chain." + }, + "consumer_ref": { + "type": "string", + "description": "Pseudonymous and stable. Never a raw end-user identifier." + }, + "cohort": { "$ref": "common.schema.json#/$defs/cohortId" }, + "revision": { "$ref": "common.schema.json#/$defs/revisionId" }, + "experiment": { "oneOf": [{ "$ref": "common.schema.json#/$defs/experimentId" }, { "type": "null" }] }, + + "resolution": { + "type": "object", + "description": "Why this revision was chosen. Revision resolution must be deterministic and auditable (ArchitectureBlueprint.md section 5.2).", + "properties": { + "reason": { + "enum": ["explicit_revision", "bound_contract", "experiment_assignment", "cohort_rule", "stable_default"] + }, + "policy_generation": { "type": "integer", "minimum": 0 } + }, + "required": ["reason"], + "additionalProperties": false + }, + + "request": { + "type": "object", + "properties": { + "operation": { "type": "string" }, + "route": { "type": "string" }, + "method": { "type": "string" }, + "status": { "type": "integer" }, + "latency_ms": { "type": "number", "minimum": 0 }, + "request_bytes": { "type": "integer", "minimum": 0 }, + "response_bytes": { "type": "integer", "minimum": 0 } + }, + "additionalProperties": false + }, + + "error": { + "type": "object", + "properties": { + "class": { + "enum": ["validation", "unknown_path", "unknown_field", "unsupported_parameter", "authorization", "backend_failure", "timeout", "policy_rejection"] + }, + "detail": { "type": "string", "description": "Redacted. Must not carry backend internals or consumer data." }, + "field": { "type": "string" } + }, + "required": ["class"], + "additionalProperties": false + }, + + "sequence": { + "type": "object", + "description": "Interaction topology is often more informative than error counts (ArchitectureBlueprint.md section 6.4).", + "properties": { + "chain_id": { "type": "string" }, + "position": { "type": "integer", "minimum": 0 }, + "pattern": { "type": "string" }, + "repeat_count": { "type": "integer", "minimum": 0 } + }, + "additionalProperties": false + }, + + "adoption": { + "type": "object", + "properties": { + "event": { "enum": ["first_use", "continued_use", "migration", "reversion", "deprecation_response"] }, + "from_revision": { "oneOf": [{ "$ref": "common.schema.json#/$defs/revisionId" }, { "type": "null" }] } + }, + "required": ["event"], + "additionalProperties": false + }, + + "feedback_ref": { "$ref": "common.schema.json#/$defs/feedbackId" }, + + "redaction": { + "type": "object", + "description": "What the privacy filter removed. Recorded so analysis knows what it cannot see.", + "properties": { + "applied": { "type": "boolean" }, + "rules": { "type": "array", "items": { "type": "string" } } + }, + "required": ["applied"], + "additionalProperties": false + } + }, + "required": ["schema_version", "id", "occurred_at", "interface_id", "kind"], + "additionalProperties": false + } + }, + "required": ["fluid_telemetry"], + "additionalProperties": false +}