Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/missionmatch / internal/modules/matching/convert.go |
| Date | Not specified in source |
| Repository | Janady13/missionmatch |
| Revision | 1675031db74316e100782d063e9eda1a597fe3a1 |
| Branch | workstream-a-opportunity-intelligence |
| Path or archive member | internal/modules/matching/convert.go |
| Original SHA-256 | 6db4bef4410661c50e27a82628302560b76b1792e3b0b109e63344d8fa46a21c |
| Git blob SHA | df27c3e94c79a7196ccfa7aef09f8b2adc27c107 |
| Reading-copy SHA-256 | 6db4bef4410661c50e27a82628302560b76b1792e3b0b109e63344d8fa46a21c |
Every recorded occurrence
These 9 records contain identical original source bytes. Their paths and revisions remain part of the archive.
| Original record | Branch or archive | Revision / identifier | Original location |
|---|---|---|---|
| Janady13/missionmatch / internal/modules/matching/convert.go | workstream-a-opportunity-intelligence | 1675031db74316e100782d063e9eda1a597fe3a1 | Open original |
| Janady13/missionmatch / internal/modules/matching/convert.go | workstream-d-command-center | 255e7d1d420abf5e277d0cd8c9b14f6f72b6acb9 | Open original |
| Janady13/missionmatch / internal/modules/matching/convert.go | workstream-e-commercial-stubs | 4437da660539284a400ee380b9b427a300c369ee | Open original |
| Janady13/missionmatch / internal/modules/matching/convert.go | ruthless-debug-sweep | b7d742f8f032984be3817efffa3eb8b13b651c1f | Open original |
| Janady13/missionmatch / internal/modules/matching/convert.go | workstream-b-sam-contract-awards | d5101cceb98e70000cebb31d3e7ac73d747d3a6d | Open original |
| Janady13/missionmatch / internal/modules/matching/convert.go | workstream-d-command-center | 255e7d1d420abf5e277d0cd8c9b14f6f72b6acb9 | Open original |
| Janady13/missionmatch / internal/modules/matching/convert.go | ruthless-debug-sweep | b7d742f8f032984be3817efffa3eb8b13b651c1f | Open original |
| Janady13/missionmatch / internal/modules/matching/convert.go | workstream-a-opportunity-intelligence | 1675031db74316e100782d063e9eda1a597fe3a1 | Open original |
| Janady13/missionmatch / internal/modules/matching/convert.go | workstream-e-commercial-stubs | 4437da660539284a400ee380b9b427a300c369ee | Open original |
Recovered source transcript
Download complete source text ↓172 lines · 6.5 KB
// Package matching is the slate-loop application layer: it loads candidates and requirements from
// the DB, runs the pure engine, and persists the explainable result as a slate. The engine stays
// pure; THIS package owns the DB<->engine boundary, including enum encode/decode (engine.Code/Parse)
// and the clearance-expiry-as-of-date rule.
package matching
import (
"encoding/json"
"fmt"
"time"
"github.com/google/uuid"
"github.com/jackc/pgx/v5/pgtype"
"github.com/missionworkforce/missionmatch/internal/modules/matching/engine"
"github.com/missionworkforce/missionmatch/internal/modules/matching/intelligence"
"github.com/missionworkforce/missionmatch/internal/platform/database/dbgen"
)
// ClearanceFactsFrom builds the intelligence ClearanceFacts (with the clearance dates the engine
// omits) from a DB candidate row plus its loaded customer agencies. Codes are CHECK-constrained at
// the DB so parsing always succeeds for a stored row.
func ClearanceFactsFrom(c dbgen.Candidate, customerAgencies []string) intelligence.ClearanceFacts {
level, _ := engine.ParseClearanceLevel(c.ClearanceLevel)
poly, _ := engine.ParsePolygraph(c.ClearancePoly)
status, _ := engine.ParseClearanceStatus(c.ClearanceStatus)
return intelligence.ClearanceFacts{
Level: level,
Poly: poly,
Status: status,
GrantedAt: datePtr(c.ClearanceGrantedAt),
ExpiresAt: datePtr(c.ClearanceExpiresAt),
ReinvestigationDueAt: datePtr(c.ReinvestigationDueAt),
CustomerAgencies: customerAgencies,
Provenance: c.ClearanceProvenance,
VerifiedAt: c.ClearanceVerifiedAt,
}
}
func datePtr(d pgtype.Date) *time.Time {
if !d.Valid {
return nil
}
t := d.Time
return &t
}
// ToEngineCandidate maps a DB candidate (+ its loaded skills/certs/customer-experience) into the
// pure engine input. asOf is the evaluation date: clearance_expires_at is the source of truth, so an
// Active flag with a past expiry is treated as Expired (review COMPL-1). It NEVER reads a demographic
// field, there is none on the candidate row by design (AEDT).
func ToEngineCandidate(c dbgen.Candidate, skills []dbgen.ListCandidateSkillsRow, certs, custExp []string, asOf time.Time) (engine.Candidate, error) {
level, err := engine.ParseClearanceLevel(c.ClearanceLevel)
if err != nil {
return engine.Candidate{}, fmt.Errorf("candidate %s: %w", c.ID, err)
}
poly, err := engine.ParsePolygraph(c.ClearancePoly)
if err != nil {
return engine.Candidate{}, fmt.Errorf("candidate %s: %w", c.ID, err)
}
status, err := engine.ParseClearanceStatus(c.ClearanceStatus)
if err != nil {
return engine.Candidate{}, fmt.Errorf("candidate %s: %w", c.ID, err)
}
status = effectiveStatus(status, c.ClearanceExpiresAt, asOf)
es := make([]engine.CandidateSkill, len(skills))
for i, s := range skills {
es[i] = engine.CandidateSkill{SkillID: s.ID.String(), Name: s.Name}
}
return engine.Candidate{
ID: c.ID.String(),
Clearance: engine.Clearance{Level: level, Poly: poly, Status: status},
Skills: es,
Certifications: certs,
CustomerExperience: custExp,
YearsExperience: int(c.YearsExperience),
Location: deref(c.Location),
Remote: c.Remote,
Relocatable: c.Relocatable,
Available: c.Available,
}, nil
}
// effectiveStatus downgrades an Active clearance to Expired when its expiry date has passed as-of the
// evaluation date. The date column is authoritative over the stored flag. The comparison is on the
// CIVIL DATE (asOf truncated to midnight UTC of its Y/M/D) so a clearance expiring exactly on the
// as-of day is still effective, matching the Command Center's `clearance_expires_at >= as_of::date`
// byte-for-byte regardless of the wall-clock time carried by asOf [review ENGINE-1/EN-1].
func effectiveStatus(stored engine.ClearanceStatus, expires pgtype.Date, asOf time.Time) engine.ClearanceStatus {
asOfDate := time.Date(asOf.Year(), asOf.Month(), asOf.Day(), 0, 0, 0, 0, time.UTC)
if stored == engine.StatusActive && expires.Valid && expires.Time.Before(asOfDate) {
return engine.StatusExpired
}
return stored
}
// ToEngineRequirement maps a DB requirement (+ its required skills/certs) into the pure engine input.
func ToEngineRequirement(r dbgen.Requirement, reqSkills []dbgen.ListRequiredSkillsRow, reqCerts []string) (engine.Requirement, error) {
level, err := engine.ParseClearanceLevel(r.RequiredLevel)
if err != nil {
return engine.Requirement{}, fmt.Errorf("requirement %s: %w", r.ID, err)
}
poly, err := engine.ParsePolygraph(r.RequiredPoly)
if err != nil {
return engine.Requirement{}, fmt.Errorf("requirement %s: %w", r.ID, err)
}
rs := make([]engine.RequiredSkill, len(reqSkills))
for i, s := range reqSkills {
rs[i] = engine.RequiredSkill{SkillID: s.ID.String(), Name: s.Name, Mandatory: s.Mandatory}
}
return engine.Requirement{
ID: r.ID.String(),
RequiredLevel: level,
RequiredPoly: poly,
RequiredSkills: rs,
RequiredCerts: reqCerts,
CustomerAgency: deref(r.CustomerAgency),
RequiredYears: int(r.RequiredYears),
WorkLocation: deref(r.WorkLocation),
RemoteAllowed: r.RemoteAllowed,
KeyPersonnel: r.KeyPersonnel,
}, nil
}
// ToSlateMemberParams freezes an engine result into the AEDT decision record. Band is the DB Code()
// (never String()), and the full trace + gap summary are persisted as jsonb so the decision is
// reproducible and explainable.
func ToSlateMemberParams(orgID, slateID, candidateID uuid.UUID, res engine.MatchResult, rank *int32) (dbgen.AddSlateMemberParams, error) {
trace, err := json.Marshal(res.Trace)
if err != nil {
return dbgen.AddSlateMemberParams{}, fmt.Errorf("marshal decision trace: %w", err)
}
gaps := []byte("[]")
if len(res.GapSummary) > 0 {
if gaps, err = json.Marshal(res.GapSummary); err != nil {
return dbgen.AddSlateMemberParams{}, fmt.Errorf("marshal gap summary: %w", err)
}
}
return dbgen.AddSlateMemberParams{
OrganizationID: orgID,
SlateID: slateID,
CandidateID: candidateID,
TotalScore: res.Total,
Band: res.Band.Code(),
ClearanceScore: res.Clearance,
SkillsScore: res.Skills,
CertsScore: res.Certifications,
CustomerScore: res.CustomerExperience,
ExperienceScore: res.Experience,
LocationScore: res.Location,
Explanation: res.Explanation,
Trace: trace,
GapSummary: gaps,
Rank: rank,
ModelVersion: strPtr(engine.ModelVersion),
}, nil
}
func strPtr(s string) *string { return &s }
func deref(s *string) string {
if s == nil {
return ""
}
return *s
}