Files
zeekayandHanzo Dev 1b7a28d31f planes: one assembly, one identity for an offer, and a bound nobody can spend twice
The defining failure of the last review was not in a plane. It was that there
were TWO wirings: cmd/amld assembled a Handler by hand and the tests assembled a
different one, so the record fingerprint could be a struct field the suite
exercised and no column stored — every retry a permanent 409 in production while
every test passed. api.Wire is now the only assembly. It creates every
collection, opens every shelf, makes every join and registers what has to run on
a cadence; cmd/amld supplies only what an installation answers differently, the
tests call the same function, and internal/source.NoLiteral reads cmd/amld to
refuse a second copy. Handler completeness is checked by reflection, because a
hand-written field list would be the third copy.

A retry is not a second transaction. pkg/receipt resolves the identity of an
OFFER once, at the top of ingest, before anything is read or written, and returns
the prior answer byte for byte — so the ledger, the aggregates, the history, the
alerts and the case plane all agree on one transaction instead of the ledger
holding one record while the aggregates hold three. Two different facts under one
reference is 409 and never 503. Because the answer is kept AFTER the work, a
process that dies in between leaves no receipt, so every plane past it also
recognises the transaction for itself: history does not append a second row,
alerts keep the first judgement, and a judged transaction opens no second case.

A bound over a COUNT of caller-sized values is not a bound. types.MaxIdent bounds
every identifier at the door, which is what turns the counts downstream into
figures in bytes; dictionary.MaxName does the same for a payload key, and
dictionary.Ceiling is the product a test WEIGHS a full accumulator against. The
field catalog's accumulator moves into roster, which admits and never removes, so
per-tenant bounds multiply by ONE number an operator can hold against a pod.

engine.Evaluator now keeps no compiled rules at all. A candidate's text arrives on
the wire at /v1/aml/rules/test, so a compiled-rule table there was keyed by a
string a caller wrote, sized by how many distinct ones a caller sends, with no cap
and no removal, in the memory every institution's ingest runs in — an OOM from one
authenticated caller, which on this deployment is every control off at once. A
rule's compiled form is a value with an owner: the installed library, or one
replay. Nothing shared, nothing to grow. Found here, not reported.

Handler.history is the one function that materialises a whole retained history, so
it now REQUIRES a *topology.Grant, and a Grant comes from Budget.Admit and nowhere
else. An ungated whole-history read is something a caller has to write on purpose
rather than something a new door can forget. The rule replay and the activation
rate fold take the same one budget the model studies take.

Reaching a bound is allowed; reaching it quietly is not. GET /v1/aml/load is the
one door for "is anything of mine quietly degraded": the tenant's aggregates
against its own ceiling in bytes with a grade, whether it has a behavioural model
at all, the field catalog's pressure, and the process's counts of institutions
held and refused. It names no other institution.

Closing R1-R8 from the review plus R11, the two recurring classes, and the silent
degradation the load door answers. Twenty-four mutations, twenty-four killed.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-02 11:32:15 -07:00

126 lines
4.5 KiB
Go

package anomaly
import (
"fmt"
"testing"
"github.com/luxfi/aml/internal/source"
"github.com/luxfi/aml/pkg/types"
"github.com/luxfi/aml/pkg/velocity"
)
// A tenant's model is a control, and no other tenant may switch it off.
//
// The model held here IS the behavioural half of this engine's monitoring. A
// tenant whose model went back to warming reports nothing, and reporting nothing
// is what a clean institution also does — so a model that resets on its own is a
// control that turned itself off and told nobody. The store used to hold every
// tenant's model in one map under one cap and drop the least recently used one to
// make room, which made that reset something ANOTHER institution's ordinary
// traffic could cause, for free, at any time.
// planted runs enough transactions through one tenant to plant and warm nothing
// in particular — it only has to exist and have learned something.
func planted(t *testing.T, s *Store, org string, n int) {
t.Helper()
for i := range n {
s.Learn(types.Transaction{
ID: fmt.Sprintf("%s-tx-%d", org, i), OrgID: org,
UserID: "acct-1", AccountID: "acct-1", Notional: 1000, USD: 1000,
Currency: "USD", Timestamp: start,
}, types.Entity{})
}
}
// TestOneTenantsVolumeCannotResetAnothersModel is red's repro, as a property.
func TestOneTenantsVolumeCannotResetAnothersModel(t *testing.T) {
vel := velocity.New(velocity.Config{})
s, err := New(Config{Orgs: 2, Seed: 1}, vel)
if err != nil {
t.Fatal(err)
}
const victim = "hanzo/victim"
planted(t, s, victim, 40)
before := s.State(victim)
if before.Learned != 40 {
t.Fatalf("the victim's own model did not learn: %+v", before.Learned)
}
for _, other := range []string{"hanzo/loud", "hanzo/louder"} {
planted(t, s, other, 5)
}
after := s.State(victim)
if after.Learned != before.Learned {
t.Errorf("two other tenants' traffic took this tenant's model from learned=%d to learned=%d, with no error and nothing in the state to say so",
before.Learned, after.Learned)
}
if !after.Planted.Equal(before.Planted) {
t.Errorf("the victim's model was replanted at %s (was %s) by another tenant's traffic", after.Planted, before.Planted)
}
}
// TestATenantWithNoRoomIsRefusedLoudly: the roster is full and a new institution
// arrives. It gets no model — which is a real gap in a real control — so the
// transaction is refused by name and counted, exactly as warming and unusable
// coordinates are. Silence is the one answer that is not allowed.
func TestATenantWithNoRoomIsRefusedLoudly(t *testing.T) {
vel := velocity.New(velocity.Config{})
s, err := New(Config{Orgs: 1, Seed: 1}, vel)
if err != nil {
t.Fatal(err)
}
planted(t, s, "hanzo/first", 5)
const late = "hanzo/late"
a := s.Learn(types.Transaction{
ID: "tx-1", OrgID: late, UserID: "acct-1", AccountID: "acct-1",
Notional: 1000, USD: 1000, Currency: "USD", Timestamp: start,
}, types.Entity{})
if a.Reason != ReasonCrowded {
t.Errorf("a tenant this process has no room for was refused with %q, want %q", a.Reason, ReasonCrowded)
}
if a.Scored {
t.Error("a tenant with no model was scored")
}
st := s.State(late)
if !st.Crowded {
t.Error("state does not say the process had no room for this tenant's model")
}
if p := s.Pressure(); p.Crowded == 0 {
t.Errorf("the refusal is nowhere in the process's own reading: %+v", p)
}
// And the institution that WAS admitted is untouched.
if s.State("hanzo/first").Learned != 5 {
t.Error("the admitted tenant lost its model to an arrival")
}
}
// TestTheAdoptedReloadIsAskedOncePerTenant: the reload put a store read and a
// whole learned-state unmarshal on the ingest path. It is right that it happens
// — an adopted control must come back after a rollout — but it must happen once
// per tenant and not once per transaction.
func TestTheAdoptedReloadIsAskedOncePerTenant(t *testing.T) {
vel := velocity.New(velocity.Config{})
s, err := New(Config{Seed: 1}, vel)
if err != nil {
t.Fatal(err)
}
asked := 0
s.SetAdopted(func(string) (Snapshot, bool) { asked++; return Snapshot{}, false })
planted(t, s, "hanzo/acme", 25)
if asked != 1 {
t.Errorf("the durable adoption was read %d times for one tenant's 25 transactions, want 1", asked)
}
}
// TestNoTenantMapIsEvictedFrom reads this package's own source. See the same
// test in pkg/velocity, and pkg/roster.
func TestNoTenantMapIsEvictedFrom(t *testing.T) {
source.NoTable(t, "anomaly.go", "Store",
"One map of every tenant's state under one cap is how one institution's traffic evicts another's; per-tenant state goes in roster.Roster, which cannot remove.")
}