// Package scrub is the contents scrubbed teaching boundary between a tenant and the shared, stateless // MEGAMIND inference plane. It exists to enforce one rule from the architecture: MEGAMIND may learn only // the CRAFT of the work (abstract patterns), never any client facts, and nothing one client learns may // ever come from another client's contents. // // The boundary has two halves, both enforced here so the rule holds by construction rather than by // reviewer vigilance: // // 1. TeachingSignal is a closed struct of ONLY abstract, contents free fields (counts, ratios, enum // categories, booleans). It carries no id, no name, no email, no free text, no org, and no raw // value. A craft pattern such as "reviewers approved an adjacent skill substitution N of M times at // similarity band X" fits; a candidate, an org, or a resume does not. // 2. Validate is a defense in depth scan that REJECTS any serialized payload still carrying a client // fact marker (a UUID, an email, or an overlong free text run). Both the tenant side (before send) // and the gateway side (on receive) call it, so a leak is refused at two independent layers. // // If a future change adds a free text field to TeachingSignal, Validate will start failing the tests, // which is the intended tripwire. package scrub import ( "bytes" "encoding/json" "errors" "fmt" "regexp" ) // ErrClientFactLeak is returned when a teaching payload still contains a client fact marker. var ErrClientFactLeak = errors.New("scrub: payload contains a client fact and cannot cross the teaching boundary") // SimilarityBand is a coarse, contents free bucket of a semantic similarity, so a craft signal can speak // about "a near match" without carrying the underlying text or score precision that could fingerprint a // tenant's data. type SimilarityBand string const ( BandExact SimilarityBand = "exact" // 1.00 BandHigh SimilarityBand = "high" // 0.90 to 0.99 BandMedium SimilarityBand = "medium" // 0.80 to 0.89 (at or above the engine threshold) BandLow SimilarityBand = "low" // below 0.80 (unmet) BandUnknown SimilarityBand = "unknown" // no signal ) // Outcome is the human decision a craft signal generalizes over, taken verbatim from the existing // slate_members.decision vocabulary so the signal is interpretable without any candidate context. type Outcome string const ( OutcomeApproved Outcome = "approved" OutcomeRejected Outcome = "rejected" OutcomePending Outcome = "pending" ) // TeachingSignal is the ONLY thing the tenant side may send to MEGAMIND's teaching channel. Every field // is an abstraction of the work, never a fact about a person, an org, or a document. There is // deliberately no string field that can hold free text, no identifier, and no raw score. type TeachingSignal struct { // Pattern names the craft observation in a fixed, enumerated vocabulary (not free text). Example: // "adjacent_skill_substitution_outcome". Callers must use a constant from Patterns; Validate rejects // anything outside it. Pattern string `json:"pattern"` // Band buckets the semantic similarity involved (no raw score, no text). Band SimilarityBand `json:"band"` // Outcome is the human decision being generalized. Outcome Outcome `json:"outcome"` // Observations is the count of events this signal aggregates (an integer, never a row id). Observations int `json:"observations"` // Successes is how many of Observations matched the outcome (an integer). Successes int `json:"successes"` // ModelVersion is the deterministic engine model version the signal sits on top of (public, not a // client fact), so a craft pattern is always tied to the scoring contract that produced it. ModelVersion string `json:"model_version"` } // Patterns is the closed vocabulary of craft observations. A signal whose Pattern is not here is refused, // so the teaching channel cannot be repurposed to smuggle a free text field. var Patterns = map[string]bool{ "adjacent_skill_substitution_outcome": true, "clearance_gap_override_outcome": true, "experience_shortfall_outcome": true, "reranker_label_agreement": true, } // uuidRe and emailRe are the unambiguous client fact markers. A teaching payload that still contains // either has not been scrubbed and must not cross the boundary. var ( uuidRe = regexp.MustCompile(`[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}`) emailRe = regexp.MustCompile(`[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}`) ) // maxFieldLen caps any string field so a craft enum can never be used as a free text smuggling channel. const maxFieldLen = 64 // Validate is the boundary check. It runs on the tenant side before send AND on the gateway side on // receive. It refuses a payload that is not a well formed TeachingSignal, that uses a Pattern outside the // closed vocabulary, that carries a count that does not add up, or whose serialized form still contains a // UUID or an email. It returns ErrClientFactLeak (wrapped) on any client fact marker. func Validate(raw []byte) error { // Marker scan first: this catches a leak even if the struct shape changes underneath us. if uuidRe.Match(raw) { return fmt.Errorf("%w: uuid", ErrClientFactLeak) } if emailRe.Match(raw) { return fmt.Errorf("%w: email", ErrClientFactLeak) } dec := json.NewDecoder(bytes.NewReader(raw)) dec.DisallowUnknownFields() // a stray extra field (a smuggled fact) is refused var s TeachingSignal if err := dec.Decode(&s); err != nil { return fmt.Errorf("scrub: not a teaching signal: %w", err) } if !Patterns[s.Pattern] { return fmt.Errorf("scrub: pattern %q is not in the craft vocabulary", clip(s.Pattern)) } if len(s.Pattern) > maxFieldLen || len(s.Band) > maxFieldLen || len(s.Outcome) > maxFieldLen || len(s.ModelVersion) > maxFieldLen { return errors.New("scrub: a field exceeds the abstract length cap") } if s.Observations < 0 || s.Successes < 0 || s.Successes > s.Observations { return errors.New("scrub: counts are not a valid aggregate") } switch s.Band { case BandExact, BandHigh, BandMedium, BandLow, BandUnknown: default: return fmt.Errorf("scrub: band %q is not a valid bucket", clip(string(s.Band))) } switch s.Outcome { case OutcomeApproved, OutcomeRejected, OutcomePending: default: return fmt.Errorf("scrub: outcome %q is not a valid decision", clip(string(s.Outcome))) } return nil } // Marshal serializes a TeachingSignal and refuses to emit one that would fail the boundary, so a caller // cannot accidentally hand raw bytes to the transport. func Marshal(s TeachingSignal) ([]byte, error) { b, err := json.Marshal(s) if err != nil { return nil, err } if err := Validate(b); err != nil { return nil, err } return b, nil } func clip(s string) string { if len(s) > maxFieldLen { return s[:maxFieldLen] } return s }