Commit graph

17 commits

Author SHA1 Message Date
7e0de9e5b7 Add telemetry ingest, feedback collector, pressure API and insight CLI
Some checks failed
ci / build (push) Failing after 3h11m37s
Completes FLUID-WP-0005. Normalization and redaction live on one path,
shared by the in-process emitter and the ingest endpoint: two paths with
two normalizations would eventually disagree, and the disagreement would
surface as a pressure finding that is really a pipeline bug.

Telemetry kind is inferred from event shape rather than defaulting to
"request", since an error filed as a request understates the interface's
failure rate. A malformed event in a batch does not discard the rest.

Feedback is stored as evidence and creates no pressure and no hypothesis
on its own, per API Standards 15, with the consumer recorded as the
actor so their untrusted status stays visible in the audit trail. The
feedback endpoint is the only consumer-reachable part of the control
plane.

The observation endpoints are not served at all when no pseudonymization
salt is configured, rather than served with a generated one: a salt that
changed per run would make the same consumer look new every time and
every cohort count wrong.

Adds an end-to-end test driving real traffic through the gateway and
confirming it becomes a classified pressure record, with no raw consumer
identity reaching the store.

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 03:19:06 +02:00
6e705aa0af Add fitness evaluator and telemetry measurer
FLUID-WP-0005 T07. Primary metrics, guardrails, secondary observations
and learning signals stay in separate roles, and there is no universal
scalar: the standards decline to define one, and inventing one would
make the interface's most important trade-offs on the operator's behalf.

Three verdict rules carry the weight. A guardrail breach dominates
primary success, because a candidate that hit its target while
regressing a guardrail traded something it was told not to. Apparent
success on too few samples is inconclusive rather than successful, since
promoting on three requests is promoting on noise. A metric missing from
one side is reported absent rather than defaulted to zero, which would
read as a dramatic change that never happened.

Specs are an input, never derived from the data, so that changing
success criteria after seeing results requires a recorded amendment
rather than being indistinguishable from normal operation.

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 03:14:25 +02:00
0ac35892a5 Add pressure classifier and pressure registry
FLUID-WP-0005 T05-T06. The classifier is deterministic: its output is
what a hypothesis will later cite as its observation, and a first
implementation that reached for a model could not be audited.

Two mappings are deliberate. An authorization failure is not pressure --
the interface working as designed is not a defect, and classifying it as
one would send the Daimon after a change that must not happen. Backend
failures and timeouts are reported at any rate, while consumer-side
errors must be a meaningful share of traffic first, since a handful says
more about one confused client than about the interface.

Pressure ids are derived from a finding fingerprint rather than
allocated, which makes ingest idempotent: re-analysing a window updates
one record instead of minting another. first_seen is never overwritten,
because how long the interface has had a problem is the evidence that
makes an old pressure worth prioritizing. A dismissed pressure is not
silently reopened, or the operator's decision would mean nothing.

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 03:11:49 +02:00
e779d1f8b9 Add redaction filter, cohort engine and topology analyzer
FLUID-WP-0005 T02-T04. Redaction is deterministic configuration rather
than a heuristic: the evidence store is append-only, so anything it
accepts cannot be taken back out. Consumer identities are pseudonymized
with HMAC rather than a bare hash, since a plain digest of a short
identifier is reversible by enumeration.

Applied rules are recorded on each event, so later analysis knows what
it cannot see instead of mistaking an absence of evidence for evidence
of absence. Redacted query parameters keep their key: which parameters a
consumer sent is itself interface evidence.

The topology analyzer requires several independent consumers before
calling a shape a pattern. One client looping is a client bug; several
converging on the same workaround is interface pressure. It also
distinguishes a recovered error from a hard failure, because those two
deserve different hypotheses.

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 03:08:05 +02:00
03ff7a8ad7 Add control APIs, control-plane binary, and close the CLI gate bypass
Some checks failed
ci / build (push) Has been cancelled
Completes FLUID-WP-0004. The Revision and Intent APIs (Blueprint 44.1
and 44.5) are served by fluid-control, which is deliberately off the
request path and must never be reachable by interface consumers: it is
the mechanism that evolves the interface in response to their behaviour.

Intent amendment is a proposal, never an edit. Rewriting a recorded
version returns 409, because changing what a version says would change
what already-published revisions were governed by.

A rejected candidate comes back as 422 with its full stage report rather
than as a server fault. Rejection is a normal outcome (invariant 14) and
the reasons are the evidence a later hypothesis needs.

Also closes a real hole this workplan opened: `fluid revision publish`
previously wrote straight into the evidence store, which was a way
around the deterministic policy gate for anyone with shell access. It
now runs the same pipeline the control plane does and requires a signing
key.

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 03:00:17 +02:00
a2d561eae5 Add signing, trust types, policy gate and publication pipeline
FLUID-WP-0004 T01, T02, T05, T06. The pipeline is the only path from
Candidate to Verified: no other code constructs a Verified value, and
Publish takes one, so "AI-generated artifacts are untrusted until
verified" is a property of the type signatures rather than a rule people
are asked to remember.

The policy gate is a pure function of the candidate, the governing
intent and configured limits. It cannot consult a model or take an
opinion as input, because a gate that can be argued with is not a gate.
Two behaviours it enforces are worth naming: the tighter of the
descriptor's own traffic ceiling and the gate's wins, so a descriptor
can restrict itself but never widen; and a daimon cannot authorize its
own promotion below FLUID-5, since generation authority is not promotion
authority.

Signatures cover the canonical document with the signature member
removed, so a signed descriptor round-trips and a tampered one does not.
The registry now refuses anything that does not verify, which is what
makes the pipeline's signature mean something at the router.

An unchecked pipeline stage is recorded as unchecked rather than
omitted, so a pipeline with no security check cannot look identical to
one that passed.

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 02:56:30 +02:00
d52dcc92a9 Add evidence store, intent store and the fluid CLI
Some checks failed
ci / build (push) Has been cancelled
Completes FLUID-WP-0003. The evidence store is append-only at the
database, not by convention in Go: a trigger blocks UPDATE and DELETE on
fluid_events in both SQLite and Postgres, so the guarantee binds a psql
session and the CLI equally, not just callers who go through the Go API.
Records are derived summary state and may be superseded; the event log
stays the authority.

The intent store makes a recorded version immutable and content
addressed, since reassigning what governed a revision after the fact
would break the one audit question Blueprint 27 exists to answer. The
unfilled InterfaceEvolutionIntent template is rejected rather than
defaulted, because a template still listing every FLUID-N mode has not
been completed and defaulting it would pick a permissive authority by
accident.

The CLI reads the evidence store directly rather than through the
control-plane API, so an operator can reconstruct history when the
control plane is down.

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 02:26:23 +02:00
791e419973 Add connector, response policy, telemetry emitter and gateway
Some checks failed
ci / build (push) Failing after 23s
Completes the request path for FLUID-WP-0003 T01-T03 and T05-T07. The
gateway resolves a revision, routes to the adapter process, and records
what happened, without ever depending on the control plane to serve.

Three behaviours carry tests because the architecture rests on them:
Emit never blocks against a stalled sink (Blueprint 34.2), the gateway
keeps serving after control-plane loss (invariant 2), and backend
internals do not leak into error responses (5.7).

The connector retries only idempotent methods, so a slow adapter cannot
cause a hall-of-helix entry to be published twice, and breakers are
per-revision so a broken candidate does not take the stable revision
down with 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 02:09:49 +02:00
42891e08a2 Add revision registry and deterministic resolver
The registry is a cached snapshot rather than a control-plane client, so
the data plane keeps serving when the control plane dies (Blueprint
34.6). Routing policy generations are monotonic: a delayed older policy
is refused rather than silently rolling back an in-flight experiment's
allocation.

The resolver implements the Blueprint 5.2 precedence chain and records
why each revision was chosen. Experiment allocation is a deterministic
function of a sticky key namespaced by experiment id, so a consumer
stays in one arm for the experiment's duration and does not land in the
same arm of every concurrent experiment.

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 02:06:19 +02:00
76912adef8 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
2026-09-04 02:03:12 +02:00
fbbf56df7a Add FLUID wire contract schemas
Nine JSON Schemas covering the five record types from the schema spec,
plus backend requirements, feedback, the revision descriptor, routing
policy and the telemetry envelope. These are the integration boundary:
readable without any fluid-core code, so adapters may be written in any
language.

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 01:55:47 +02:00
7d2fc2af5d Add generated WORK-RECORDS
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 01:52:53 +02:00
repo-manager
0aab793a3a repo.work.assign_missing_identifiers
source: repo-manager
reason: deterministic projection registration

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 1116572@bnt-lap001
Assistant-Session: 8ba9bb93-a72a-4883-b189-2499cce5c400
2026-09-04 01:52:06 +02:00
custodian-sync
813fee41b0 chore(consistency): sync task status from DB [auto]
Updated by fix-consistency on 2026-09-04:
  - update .custodian-brief.md for fluid-core

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 1116572@bnt-lap001
Assistant-Session: 8ba9bb93-a72a-4883-b189-2499cce5c400
2026-09-04 01:51:58 +02:00
82becd0fda Register with State Hub and align workplan IDs to FLUID-WP prefix
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 01:50:34 +02:00
c2cfe86d88 Establish fluid-core scaffold, intent, and phased workplans
Adds the repository structure, INTENT.md declaring the wire-contract
boundary, and seven workplans covering Blueprint Phases A-C plus the
fluid-telegram handover.

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 01:49:12 +02:00
af4bf798c4 Initial commit 2026-09-02 22:51:13 +00:00