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
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
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