// Package aioe is the Adaptive Intelligence Optimization Engine (MWT-2212, FSM-002): the closed // loop that measures recorded outcomes and adjusts the scoring dimension weights, per tenant, on a // deterministic nightly batch. It NEVER runs per request, it consumes outcome_events ONLY, and it // runs under the dedicated missionmatch_aioe database role whose grants reach the three aioe tables // and nothing else, so it is physically unable to read a candidate row, an EEO table, or any other // tenant data. Customers see improved recommendations, never the mechanism. package aioe import ( "context" "errors" "fmt" "strconv" "github.com/jackc/pgx/v5" "github.com/shopspring/decimal" "github.com/missionworkforce/missionmatch/internal/modules/matching/engine" ) // Dimensions is the canonical ordered dimension list. Every iteration in this package walks this // slice, never a Go map, so identical inputs always produce identical outputs. var Dimensions = []string{ "clearance", "skills", "certifications", "customer_experience", "experience", "location", } // DefaultWeightsMap returns the frozen global default weights keyed by canonical dimension name. func DefaultWeightsMap() map[string]decimal.Decimal { d := engine.DefaultWeights() return map[string]decimal.Decimal{ "clearance": d.Clearance, "skills": d.Skills, "certifications": d.Certifications, "customer_experience": d.CustomerExperience, "experience": d.Experience, "location": d.Location, } } // WeightsFromMap shapes a canonical map into engine.Weights. Missing keys become zero, which // Validate then rejects, so a malformed profile can only fall back, never mis-score. func WeightsFromMap(m map[string]decimal.Decimal) engine.Weights { return engine.Weights{ Clearance: m["clearance"], Skills: m["skills"], Certifications: m["certifications"], CustomerExperience: m["customer_experience"], Experience: m["experience"], Location: m["location"], } } // ProfileLabel renders the stamped profile version string a result carries, for example // "tenant.v3". Version zero or below means the global default. func ProfileLabel(version int) string { if version <= 0 { return engine.GlobalDefaultProfileVersion } return "tenant.v" + strconv.Itoa(version) } // ErrActiveProfileUnreadable marks that an ACTIVE weight profile EXISTS for the caller's org but could // not be turned into usable weights. It is deliberately distinct from the no profile case: the caller // must fail the request rather than score on defaults, because the two outcomes are not interchangeable // in an automated employment decision tool. Scoring on defaults when a tenant profile was active // produces the wrong score AND stamps the audit trail with the global default label, which is a false // statement that no tenant profile was in effect. A compliance reviewer reading that trail could not // reconstruct what actually happened. var ErrActiveProfileUnreadable = errors.New("aioe: an active weight profile exists but could not be read") // LoadActiveWeightsOnTx loads the caller org's ACTIVE weight profile on an RLS scoped tenant tx and // returns the engine weights plus the stamped profile label. One indexed read per request transaction. // // It draws a hard line between two cases that used to be collapsed into one silent fallback: // // - NO active profile exists (pgx.ErrNoRows, the normal state for a tenant the updater has not // produced a profile for yet). That legitimately returns the frozen global default weights and the // global.default label, and the audit trail then states exactly the truth. // - An active profile EXISTS but is unusable (the query itself failed, a dimension is missing, a // dimension is not a decimal, or the assembled weights fail engine validation). That returns // ErrActiveProfileUnreadable so the caller fails the request. Swallowing it scored every candidate // in the request on the wrong weights while claiming in the audit that no profile was active. func LoadActiveWeightsOnTx(ctx context.Context, tx pgx.Tx) (engine.Weights, string, error) { var raw map[string]string var version int err := tx.QueryRow(ctx, ` SELECT dimension_weights, profile_version FROM aioe_weight_profiles WHERE status = 'active' LIMIT 1`).Scan(&raw, &version) if errors.Is(err, pgx.ErrNoRows) { return engine.DefaultWeights(), engine.GlobalDefaultProfileVersion, nil } if err != nil { // Anything other than no rows is a real failure: a malformed dimension_weights jsonb fails client // side here, a dropped connection fails here, and neither is evidence that the tenant has no // profile. Note that a scan error does NOT abort the transaction, which is precisely why this had // to be surfaced explicitly: nothing else would have noticed. return engine.Weights{}, "", fmt.Errorf("%w: %v", ErrActiveProfileUnreadable, err) } m := make(map[string]decimal.Decimal, len(Dimensions)) for _, dim := range Dimensions { s, ok := raw[dim] if !ok { return engine.Weights{}, "", fmt.Errorf("%w: missing dimension %s", ErrActiveProfileUnreadable, dim) } d, perr := decimal.NewFromString(s) if perr != nil { return engine.Weights{}, "", fmt.Errorf("%w: dimension %s: %v", ErrActiveProfileUnreadable, dim, perr) } m[dim] = d } w := WeightsFromMap(m) if verr := w.Validate(); verr != nil { return engine.Weights{}, "", fmt.Errorf("%w: %v", ErrActiveProfileUnreadable, verr) } return w, ProfileLabel(version), nil }