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
21
examples/echo-interface/README.md
Normal file
21
examples/echo-interface/README.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# echo-interface
|
||||
|
||||
The smallest honest reproduction of the `ArchitectureBlueprint.md` §33 worked
|
||||
example, used as the conformance fixture.
|
||||
|
||||
Two revisions of one interface over the same backend data:
|
||||
|
||||
| Revision | Shape | Requests per completed task |
|
||||
|---|---|---|
|
||||
| **R-1** | `GET /v1/entries` only — consumers list everything and filter locally | ~3 |
|
||||
| **R-2** | adds `GET /v1/entries/latest` — the concept consumers actually wanted | 1 |
|
||||
|
||||
R-1 is not a strawman. It is the interface a careful designer produces before
|
||||
they have seen how it is used: a clean collection resource with no special
|
||||
cases. The pressure it generates is the point — consumers repeatedly fetching
|
||||
a collection to discard all but one item is the evidence that "latest" is a
|
||||
first-class concept the contract failed to name.
|
||||
|
||||
The fixture has no external dependencies and runs in CI. It exists so the
|
||||
revision–experiment–fitness loop is proven mechanically before a real workload
|
||||
depends on it.
|
||||
67
examples/echo-interface/adapter.go
Normal file
67
examples/echo-interface/adapter.go
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
// Package echo provides the conformance fixture adapters.
|
||||
//
|
||||
// Two revisions over the same data: R-1 exposes only a collection, R-2 adds the
|
||||
// convenience resource. Both are ordinary deterministic HTTP handlers, which is
|
||||
// the point — an adapter is a normal service, and fluid-core sits in front of
|
||||
// it without asking anything of it.
|
||||
package echo
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Entry is one record the fixture serves.
|
||||
type Entry struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
// Data returns a deterministic set of entries.
|
||||
//
|
||||
// Fixed timestamps rather than time.Now: a conformance suite whose fixture
|
||||
// changes between runs cannot distinguish a regression from the clock.
|
||||
func Data() []Entry {
|
||||
base := time.Date(2026, 9, 1, 12, 0, 0, 0, time.UTC)
|
||||
return []Entry{
|
||||
{ID: "e-1", Title: "the river reached the forge", CreatedAt: base},
|
||||
{ID: "e-2", Title: "nine doors stayed honest", CreatedAt: base.Add(24 * time.Hour)},
|
||||
{ID: "e-3", Title: "the reviewing side", CreatedAt: base.Add(48 * time.Hour)},
|
||||
}
|
||||
}
|
||||
|
||||
// NewR1 returns the collection-only adapter.
|
||||
//
|
||||
// A consumer wanting the newest entry must fetch the whole collection, sort it
|
||||
// and discard the rest. That is the interface pressure the fixture generates.
|
||||
func NewR1() http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/v1/entries", func(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, Data())
|
||||
})
|
||||
return mux
|
||||
}
|
||||
|
||||
// NewR2 returns the adapter with the convenience resource added.
|
||||
func NewR2() http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/v1/entries", func(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, Data())
|
||||
})
|
||||
mux.HandleFunc("/v1/entries/latest", func(w http.ResponseWriter, r *http.Request) {
|
||||
entries := Data()
|
||||
sort.Slice(entries, func(i, j int) bool {
|
||||
return entries[i].CreatedAt.After(entries[j].CreatedAt)
|
||||
})
|
||||
writeJSON(w, entries[0])
|
||||
})
|
||||
return mux
|
||||
}
|
||||
|
||||
func writeJSON(w http.ResponseWriter, v any) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(v)
|
||||
}
|
||||
12
examples/echo-interface/r1.openapi.yaml
Normal file
12
examples/echo-interface/r1.openapi.yaml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
openapi: "3.1.0"
|
||||
info:
|
||||
title: echo-interface
|
||||
version: "1"
|
||||
paths:
|
||||
/v1/entries:
|
||||
get:
|
||||
operationId: listEntries
|
||||
parameters:
|
||||
- name: limit
|
||||
in: query
|
||||
schema: {type: integer, minimum: 1, maximum: 100}
|
||||
15
examples/echo-interface/r2.openapi.yaml
Normal file
15
examples/echo-interface/r2.openapi.yaml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
openapi: "3.1.0"
|
||||
info:
|
||||
title: echo-interface
|
||||
version: "2"
|
||||
paths:
|
||||
/v1/entries:
|
||||
get:
|
||||
operationId: listEntries
|
||||
parameters:
|
||||
- name: limit
|
||||
in: query
|
||||
schema: {type: integer, minimum: 1, maximum: 100}
|
||||
/v1/entries/latest:
|
||||
get:
|
||||
operationId: latestEntry
|
||||
Loading…
Add table
Add a link
Reference in a new issue