// Package aioemodule is the HTTP surface of the Adaptive Intelligence Optimization Engine: outcome // capture from every module, and the admin status and audit trail reads. The engine itself (the // batch updater) lives in internal/aioe and never runs on a request path. package aioemodule import ( "encoding/json" "time" "github.com/google/uuid" "github.com/shopspring/decimal" ) // validModules and validEventTypes mirror the CHECK constraints on outcome_events exactly, so a bad // payload fails fast with a clear 400 instead of a database error. var validModules = map[string]bool{"screen": true, "match": true, "slate": true, "command": true} var validEventTypes = map[string]bool{ "screening_decision": true, "interview_result": true, "offer_result": true, "placement_result": true, "slate_result": true, "match_feedback": true, } // PostOutcomeRequest records one outcome event. SignalValue is a json number or numeric string in // [-1, 1] (for example advance 1, hold 0, reject -1). RequirementID is optional; when present with // a candidate subject the service denormalizes the served per dimension scores into the event // metadata so the batch updater can consume outcome_events alone. type PostOutcomeRequest struct { Module string `json:"module"` SubjectType string `json:"subjectType"` SubjectID uuid.UUID `json:"subjectId"` RequirementID *uuid.UUID `json:"requirementId,omitempty"` EventType string `json:"eventType"` SignalValue json.Number `json:"signalValue"` ReasonCode string `json:"reasonCode"` OccurredAt *time.Time `json:"occurredAt,omitempty"` Metadata json.RawMessage `json:"metadata,omitempty"` } // PostOutcomeResponse acknowledges the recorded event. type PostOutcomeResponse struct { ID uuid.UUID `json:"id"` OccurredAt time.Time `json:"occurredAt"` } // StatusResponse is the admin proof of life: how many outcomes the org has recorded, the last // engine run, the active profile, and this month's adjustment count. type StatusResponse struct { OutcomesRecorded int64 `json:"outcomesRecorded"` LastRunAt *time.Time `json:"lastRunAt,omitempty"` LastRunOutcomesSeen *int `json:"lastRunOutcomesSeen,omitempty"` LastRunProfilesMoved *int `json:"lastRunProfilesMoved,omitempty"` ActiveProfileVersion string `json:"activeProfileVersion"` AdjustmentsThisMonth int64 `json:"adjustmentsThisMonth"` UpdaterConfigured bool `json:"updaterConfigured"` // ShadowMode is true when the updater proposes candidate profiles a human must promote rather // than activating them automatically. ShadowMode bool `json:"shadowMode"` // PendingCandidateVersion is the profile version of a candidate awaiting promotion, or empty. PendingCandidateVersion string `json:"pendingCandidateVersion,omitempty"` } // AdjustmentView is one audit trail row for the admin history endpoint. type AdjustmentView struct { Dimension string `json:"dimension"` OldValue string `json:"oldValue"` NewValue string `json:"newValue"` ProducedVersion int `json:"producedVersion"` WindowStart time.Time `json:"windowStart"` WindowEnd time.Time `json:"windowEnd"` OutcomesUsed int `json:"outcomesUsed"` Rationale string `json:"rationale"` CreatedAt time.Time `json:"createdAt"` } // parseSignal validates the signal into a decimal in [-1, 1]. func parseSignal(n json.Number) (decimal.Decimal, bool) { d, err := decimal.NewFromString(n.String()) if err != nil { return decimal.Zero, false } one := decimal.NewFromInt(1) if d.GreaterThan(one) || d.LessThan(one.Neg()) { return decimal.Zero, false } return d, true }