fluid-core/docs/integration-guide.md

138 lines
5.3 KiB
Markdown
Raw Normal View History

Add the conformance suite, echo fixture and integration guide Completes FLUID-WP-0007. The seven minimal-conformance requirements and the mechanically checkable architectural invariants are asserted as tests rather than claimed in a README, because a conformance claim nobody re-checks is one that quietly stops being true. Only the checkable subset of the invariants is asserted; pretending a test can settle the rest would be worse than leaving them to review. TestFirstVerticalSlice runs all eleven steps of Blueprint 50 with no human steps: two revisions, explicit routing, telemetry, a cohort dimension, detected pressure, a hypothesis, a candidate, a 90/10 experiment, fitness comparison, promotion, and a complete audit trail. Requests per completed task fall from 5.65 to 1.00 against a 1.20 target. A companion test runs the loop twice and requires the same verdict, since a loop whose conclusion depended on run order would be measuring the harness rather than the interface. The failure-containment matrix covers Blueprint 34 directly: the data plane keeps serving with the evidence store closed, with telemetry wedged against a sink that never returns, after a failed build, after an experiment rollback, and with the adaptive concurrency limit saturated. Fixes a real bug the suite exposed. Drain closed the emitter outright, so every request after the first flush emitted into a dead emitter and was silently lost -- the kind of fault that makes a later measurement quietly wrong rather than loudly broken. Emitter.Flush now waits for delivery without stopping it. 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
2026-09-04 08:21:49 +02:00
# Putting an existing API behind fluid-core
fluid-core attaches **out of process** (ADR-0002). Your API contributes no code,
imports no library, and may be written in any stack. Integration is over a wire
contract, so the fact that fluid-core is written in Go is not something you need
to care about.
## What you need
1. **A running service.** Anything that speaks HTTP. This becomes an *adapter*.
2. **A contract.** OpenAPI 3.1 today; the validator interface admits others.
3. **An interface evolution intent.** The governance document that bounds how
the interface may change.
Nothing else. In particular you do not need a Kubernetes cluster, a message
broker, or Postgres — SQLite is the default evidence store.
## The five minutes version
```bash
export FLUID_INTERFACE=my-api
export FLUID_STORE=$PWD/fluid.db
# 1. Record the intent that governs this interface.
fluid intent put --version IEI-1 --file InterfaceEvolutionIntent.md --activate
# 2. Publish a revision pointing at your existing service.
fluid revision publish --file r1.yaml --ephemeral-key \
--adaptation-classes presentation --approved-by "$USER" --traffic-share 1.0
# 3. Route traffic to it.
fluid policy put --file routing-policy.yaml
```
A revision descriptor is small:
```yaml
revision:
schema_version: "0.1"
id: "R-1"
interface: "my-api"
state: "stable"
contract:
type: "openapi"
digest: "sha256:<digest of your OpenAPI document>"
runtime:
upstream: "http://my-existing-service:8080" # your service, unchanged
timeout_ms: 5000
intent:
version: "IEI-1"
policy:
compatibility: "additive"
security_check: "passed"
```
`runtime.upstream` is the whole integration. Everything else is metadata about
what that upstream is allowed to be.
## What you get immediately
- **Revision identity.** Every response names the revision that served it.
- **Telemetry.** Requests, errors, call sequences and adoption, redacted before
storage.
- **Contract enforcement.** Undeclared paths, methods and fields are refused
rather than proxied.
- **An audit trail.** `fluid audit trace R-1` reconstructs how a revision came
to exist and what happened to it.
That is FLUID-0 (Instrumented) conformance, and it needs no AI and no change to
your service.
## What you should decide deliberately
**The pseudonymization salt.** Consumer identities are pseudonymized with HMAC.
The salt must be stable for the life of the interface — rotating it makes the
same consumer look like a new one and breaks every longitudinal comparison.
Store it where you store secrets, not in the repository.
**The authority mode.** Your intent document declares one, `FLUID-0` through
`FLUID-6`. This is a statement about authority, not maturity: a high-assurance
interface may deliberately stay at FLUID-2 forever. The unfilled template is
refused rather than defaulted, precisely so nobody inherits a permissive mode by
accident.
**Your complexity budget.** `policy.DefaultLimits()` permits only presentation
and implementation adaptations, refuses breaking changes, and caps exposure at
25%. If your interface needs more, say so explicitly in the intent rather than
widening the default.
## Adding a second revision
This is where FLUID earns its keep. Publish a second revision pointing at a
second adapter — a different build, a different service, a different language —
and let the experiment controller split traffic between them:
```bash
fluid revision publish --file r2.yaml --ephemeral-key ...
fluid experiment design --file experiment.yaml
fluid experiment start E-1 --generation 2 --default-revision R-1 --policy-out rp.json
fluid policy put --file rp.json
```
The controller does not touch traffic. It emits a routing policy for you to
install, which is what makes an experiment interruptible: stopping one replaces
a document rather than unwinding anything.
## Signing
`--ephemeral-key` is for development. It generates a throwaway key and warns
that revisions signed with it will not verify after a restart, which is exactly
what you want to hear before it happens in production.
In production, generate a key pair, keep the private half in your secret store,
and configure the gateway's registry with the public half. The router accepts
only descriptors that verify — an unsigned or tampered descriptor is refused,
including one that was signed and then edited.
## What fluid-core will not do for you
- **It will not modify your backend.** Where a candidate needs capability the
backend does not have, FLUID emits a structured requirement and stops. The
backend owner accepts, plans, or declares it out of scope.
- **It will not decide what "better" means.** There is no universal fitness
scalar. You declare primary metrics and guardrails; the evaluator compares
against what you declared and refuses to infer criteria from the data.
- **It will not promote on thin evidence.** A comparison below the sample floor
is inconclusive, not successful. You can override that with an acknowledged
flag; the override is recorded as one.
## Reference
- `examples/echo-interface/` — the two-revision fixture the conformance suite
drives, and the smallest complete example.
- `conformance/suite/` — every conformance requirement asserted as a test.
`TestFirstVerticalSlice` is the whole loop in one function.
- `spec/` — the normative documents. When this guide and the spec disagree, the
spec is right and this guide is a bug.