Add the conformance suite, echo fixture and integration guide
Some checks failed
ci / build (push) Has been cancelled
Some checks failed
ci / build (push) Has been cancelled
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
This commit is contained in:
parent
61d8d8cabe
commit
55363905bc
16 changed files with 1885 additions and 23 deletions
137
docs/integration-guide.md
Normal file
137
docs/integration-guide.md
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
# 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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue