Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/missionmatch / internal/modules/ai/mission.go |
| Date | Not specified in source |
| Repository | Janady13/missionmatch |
| Revision | 6c56e29231ec05735927fa265c97d517499dfeaf |
| Branch | ws6-retrieval |
| Path or archive member | internal/modules/ai/mission.go |
| Original SHA-256 | 6a4347a342ee7c5090817dff1eb05161d858e1761ed456f957e9df91e6b4105b |
| Git blob SHA | fa0ab5d1044e27781eff2cece00fe391e43f2210 |
| Reading-copy SHA-256 | 6a4347a342ee7c5090817dff1eb05161d858e1761ed456f957e9df91e6b4105b |
Recovered source transcript
Download complete source text ↓67 lines · 3.4 KB
package ai
import (
"regexp"
"strings"
)
// Mission scoping (WS6 Option A). A RAG question can name a mission area (SIGINT, GEOINT, ...). When it
// does, the retrieval is constrained to candidates whose full_name, headline, or location text matches
// that mission keyword, using the existing full text index (search_tsv). This is the ONLY mission filter
// available without structured mission tags on candidates and requirements, which are WS7 work: it filters
// on that candidate TEXT, not on a semantic mission alignment, and not on the resume body. The limit is
// documented in docs/AI_RESEARCH.md.
//
// The vocabulary mirrors the curated mission_spine seed (migration 0021_ontology.sql), which is the
// source of truth. The ten terms change rarely and are controlled IC vocabulary, so mirroring them in
// code (rather than a database round trip on every question) keeps retrieval deterministic and avoids a
// dependency from the AI path onto a reference query. If mission_spine grows, extend this list to match.
// missionPattern pairs the canonical full text keyword fed to websearch_to_tsquery with the natural
// language forms a user might type. Acronyms and short tokens are matched on a word boundary so a bare
// acronym never matches inside another word.
type missionPattern struct {
canonical string // the keyword passed to the mission scoped query ($mission_query)
forms []*regexp.Regexp // question phrasings that select this mission
}
// wb compiles a case insensitive, word boundary anchored matcher for a mission form. Any single space in a
// multi word form becomes \s+, so a line break or a double space between phrase tokens still matches (for
// example "satellite command and control"). The forms contain no regex metacharacters, so the literal is
// safe to embed directly.
func wb(s string) *regexp.Regexp {
return regexp.MustCompile(`(?i)\b` + strings.ReplaceAll(s, " ", `\s+`) + `\b`)
}
// missionVocabulary is iterated in mission_spine order, so when a question names more than one mission the
// result is deterministic (the earliest spine term wins). Order MUST match migration 0021.
var missionVocabulary = []missionPattern{
{"SIGINT", []*regexp.Regexp{wb("sigint"), wb("signals intelligence")}},
{"GEOINT", []*regexp.Regexp{wb("geoint"), wb("geospatial intelligence")}},
{"HUMINT", []*regexp.Regexp{wb("humint"), wb("human intelligence")}},
{"MASINT", []*regexp.Regexp{wb("masint"), wb("measurement and signature intelligence")}},
{"OSINT", []*regexp.Regexp{wb("osint"), wb("open source intelligence")}},
{"Satellite C2", []*regexp.Regexp{wb("satellite c2"), wb("satellite command and control")}},
{"Cross Domain Solution", []*regexp.Regexp{wb("cross domain solution"), wb("cross domain solutions")}},
{"COMSEC", []*regexp.Regexp{wb("comsec"), wb("communications security")}},
{"TEMPEST", []*regexp.Regexp{wb("tempest")}},
{"Electronic Warfare", []*regexp.Regexp{wb("electronic warfare")}},
}
// MissionFromQuestion returns the canonical mission keyword named in the question, or "" if none is named.
// The returned value is drawn only from the fixed mission vocabulary above, never from free user text, so
// it can never carry a demographic reference into the retrieval path.
func MissionFromQuestion(question string) string {
if question == "" {
return ""
}
for _, m := range missionVocabulary {
for _, form := range m.forms {
if form.MatchString(question) {
return m.canonical
}
}
}
return ""
}