Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/missionmatch / internal/integration/curation_integration_test.go |
| Date | Not specified in source |
| Repository | Janady13/missionmatch |
| Revision | 720d47fae476b0d38477ae4401697d372b1ea2bc |
| Branch | feature/opportunity-to-outcome |
| Path or archive member | internal/integration/curation_integration_test.go |
| Original SHA-256 | bbbc6aa0177baa9e4fa0a36de8e26615bfec41c3038e3504d16f4b0795e35d44 |
| Git blob SHA | 75a71ee82822fe24055138efcea43321be4bc4a7 |
| Reading-copy SHA-256 | bbbc6aa0177baa9e4fa0a36de8e26615bfec41c3038e3504d16f4b0795e35d44 |
Every recorded occurrence
These 14 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/integration/curation_integration_test.go | feature/opportunity-to-outcome | 720d47fae476b0d38477ae4401697d372b1ea2bc | Open original |
| Janady13/missionmatch / internal/integration/curation_integration_test.go | collision-deconfliction | 4ee5a09972608ed89f8377c6665542d5f450f0a6 | Open original |
| Janady13/missionmatch / internal/integration/curation_integration_test.go | m5-price-to-win | 41000ad30fff4129fdaf5ec84e15ba14c27d4890 | Open original |
| Janady13/missionmatch / internal/integration/curation_integration_test.go | master | 1e9249840a4302ec82e123b9c9cb6f75badea3e4 | Open original |
| Janady13/missionmatch / internal/integration/curation_integration_test.go | ws8-teaching-loop | c823f0fb6bb2aba0f39001eb6de96f4000d9321f | Open original |
| Janady13/missionmatch / internal/integration/curation_integration_test.go | mag-substrate | b82c7100a492dec67dfd1eda6b3f53def859be35 | Open original |
| Janady13/missionmatch / internal/integration/curation_integration_test.go | time-to-fill-survival | b66e1e505863d4f17741a4b9ede73509e45bd6c6 | Open original |
| Janady13/missionmatch / internal/integration/curation_integration_test.go | graphrag-foundation | b0ddeb9f633fd138d09309d1a17b7d13bc3a67f7 | Open original |
| Janady13/missionmatch / internal/integration/curation_integration_test.go | ws9-bench-ingestion | f585a9ef32fa554663ce1fba9a414a54b6e49cf0 | Open original |
| Janady13/missionmatch / internal/integration/curation_integration_test.go | presentation-readiness | eb574acaa29d71ce4d0f852572e87c275c79fdb7 | Open original |
| Janady13/missionmatch / internal/integration/curation_integration_test.go | vendor-intelligence | fe9796a19c261951d3f2565206c0c1b5ac294465 | Open original |
| Janady13/missionmatch / internal/integration/curation_integration_test.go | phase2-connectors | e038d4c687aec16f8bd9bc4d907eea14d91f8704 | Open original |
| Janady13/missionmatch / internal/integration/curation_integration_test.go | feature/aioe-upgrade | 2178e7298d0e5d9af83afa12402fd587f0ba6ed8 | Open original |
| Janady13/missionmatch / internal/integration/curation_integration_test.go | capture-readiness | bae6af7cb2997e819174a9e9cfff5e16067a1975 | Open original |
Recovered source transcript
Download complete source text ↓327 lines · 14.3 KB
//go:build integration
package integration
import (
"context"
"encoding/json"
"errors"
"testing"
"time"
"github.com/google/uuid"
"github.com/jackc/pgx/v5"
"github.com/missionworkforce/missionmatch/internal/modules/ai"
"github.com/missionworkforce/missionmatch/internal/modules/matching"
"github.com/missionworkforce/missionmatch/internal/modules/matching/engine"
"github.com/missionworkforce/missionmatch/internal/platform/audit"
"github.com/missionworkforce/missionmatch/internal/platform/database/dbgen"
)
// TestCurationTwoStepGateAndInertEdge is the WS8 Phase 4 end to end proof against real Postgres. It proves the
// load bearing property of the whole workstream: a curated edge is staged INERT on approve and goes live ONLY
// after a SECOND distinct verify step, and the engine math is never touched. The flow:
//
// (a) seed an org with recurring approved substitution evidence (a candidate skill B accepted in place of a
// required skill A at a high band, approved twice) plus the governance aggregate that confirms the
// pattern, then run the generator and assert a pending substitutes_for_edge proposal appears with the
// right from (B), to (A), strength, and evidence.
// (b) approve it and assert a skill_relationships row exists with verified_at NULL, then PROVE IT IS INERT:
// a fresh DeterministicAliasResolver (the exact component that reads verified_at IS NOT NULL edges) does
// NOT resolve B to A, so a score that would change if the edge were live is unchanged.
// (c) verify it (the second human step) and assert verified_at is set, then prove the resolver now reads it:
// a fresh resolver resolves B to A at the curated strength, so the edge is live.
func TestCurationTwoStepGateAndInertEdge(t *testing.T) {
ctx := context.Background()
org := seedOrg(t)
// Two real global skills: A is required, B is the candidate substitute. Names are unique per run so the
// resolver's name path is unambiguous and the global unique on skills.name never collides across runs.
skillA := seedSkillByName(t, "ws8 required skill "+org.String()[:8])
skillB := seedSkillByName(t, "ws8 candidate skill "+org.String()[:8])
// Recurring approved substitution evidence: B matched A at a high band (0.93), non exact, approved, twice.
for i := 0; i < 2; i++ {
seedCurationMember(t, org, skillA, skillB, "0.93", "approved")
}
// Governance confidence: the contents free aggregate confirms the substitution pattern is reliably
// approved at the high band, so the double gate is satisfied.
seedSubstitutionAggregate(t, org, "high", "approved", 5, 5)
// Run the generator (app pool RLS scoped scan, migrator pool governance read and proposal write).
gen := ai.NewProposalGenerator(
appDB,
ai.NewMigratorGovernanceReader(migrator),
ai.NewMigratorProposalWriter(migrator),
audit.New(appDB),
)
res, err := gen.GenerateProposalsForOrg(ctxOrg(org))
if err != nil {
t.Fatalf("GenerateProposalsForOrg: %v", err)
}
if res.SubstitutionEdges != 1 {
t.Fatalf("generator produced %d substitution edges, want exactly 1", res.SubstitutionEdges)
}
// (a) A pending substitutes_for_edge proposal exists with from B, to A, the high band strength, and the
// recurring evidence counts.
prop := getOnlyPendingProposal(t, skillB, skillA)
if prop.Kind != "substitutes_for_edge" {
t.Fatalf("proposal kind = %q, want substitutes_for_edge", prop.Kind)
}
if prop.ProposedStrength == nil || prop.ProposedStrength.String() != "0.9" {
t.Fatalf("proposed strength = %v, want 0.90 for the high band", prop.ProposedStrength)
}
if prop.EvidenceSuccesses != 2 || prop.EvidenceObservations != 2 {
t.Fatalf("evidence = %d of %d, want 2 of 2", prop.EvidenceSuccesses, prop.EvidenceObservations)
}
if prop.ModelVersion != engine.ModelVersion {
t.Fatalf("proposal model_version = %q, want engine.ModelVersion %q", prop.ModelVersion, engine.ModelVersion)
}
// A rerun is idempotent: the pending proposal already exists, so no duplicate is created.
if _, err := gen.GenerateProposalsForOrg(ctxOrg(org)); err != nil {
t.Fatalf("generator rerun: %v", err)
}
if n := countProposals(t, skillB, skillA); n != 1 {
t.Fatalf("rerun created a duplicate proposal, count = %d, want 1", n)
}
apply := ai.NewCurationApplyService(migrator, audit.New(appDB), true) // enforce separation of duties
approver := uuid.New()
verifier := uuid.New()
// (b) APPROVE (the first human step). The proposal moves to approved and a staged INERT edge is inserted.
if _, err := apply.Approve(ctx, prop.ID, approver); err != nil {
t.Fatalf("Approve: %v", err)
}
edgeID, verifiedAt := substitutesEdge(t, skillB, skillA)
if edgeID == uuid.Nil {
t.Fatal("approve did not stage a skill_relationships edge")
}
if verifiedAt != nil {
t.Fatalf("staged edge verified_at = %v, want NULL (the edge must be inert until verify)", verifiedAt)
}
// PROVE INERT: a fresh resolver loads only verified_at IS NOT NULL edges, so it does NOT resolve B to A.
// This is the score that would change if the staged edge were live; it does not, so the edge is inert.
if matched := resolveBtoA(ctx, skillB, skillA); matched {
t.Fatal("INERT EDGE BROKEN: the staged verified_at NULL edge was read by the resolver and changed a match")
}
// The proposal approver and approved_at are recorded; the verifier is NOT yet set on the edge.
assertProposalApproved(t, prop.ID, approver)
// SEPARATION OF DUTIES: the approver cannot also verify when enforcement is on, so the two person control
// is real, not procedural. The edge stays staged and inert.
if _, err := apply.Verify(ctx, prop.ID, approver); !errors.Is(err, ai.ErrVerifierMustDifferFromApprover) {
t.Fatalf("self verify by the approver should be refused, got %v", err)
}
if _, stillNil := substitutesEdge(t, skillB, skillA); stillNil != nil {
t.Fatal("a refused self verify must leave the edge staged (verified_at NULL)")
}
// (c) VERIFY (the second, distinct human step). A DIFFERENT human verifies and the edge goes live.
if _, err := apply.Verify(ctx, prop.ID, verifier); err != nil {
t.Fatalf("Verify: %v", err)
}
_, verifiedAt2 := substitutesEdge(t, skillB, skillA)
if verifiedAt2 == nil {
t.Fatal("verify did not set verified_at on the staged edge")
}
assertEdgeVerifier(t, edgeID, verifier)
// PROVE LIVE: a fresh resolver now reads the verified edge and resolves B to A at the curated strength.
if matched := resolveBtoA(ctx, skillB, skillA); !matched {
t.Fatal("verified edge is not live: the resolver did not resolve B to A after verify")
}
}
// ---- seed and assertion helpers (migrator, BYPASSRLS, so they can write the global tables across tenants) ----
// seedSkillByName inserts a global skill with a unique name and returns its id.
func seedSkillByName(t *testing.T, name string) uuid.UUID {
t.Helper()
var id uuid.UUID
if err := migrator.QueryRow(context.Background(),
"INSERT INTO skills (name) VALUES ($1) RETURNING id", name).Scan(&id); err != nil {
t.Fatalf("seed skill %q: %v", name, err)
}
return id
}
// seedCurationMember seeds one requirement, candidate, slate, decided slate member, and a candidate_evaluations
// row whose matched_skills records the candidate skill B accepted for the required skill A at the given
// similarity (non exact). It writes the real skill ids into the SkillMatch so the generator derives the pair by
// id, exactly the production shape.
func seedCurationMember(t *testing.T, org, skillA, skillB uuid.UUID, similarity, decision string) {
t.Helper()
ctx := context.Background()
q := dbgen.New(migrator)
req, err := q.CreateRequirement(ctx, dbgen.CreateRequirementParams{
OrganizationID: org, Title: "WS8 Curation Role", RequiredLevel: "none", RequiredPoly: "none",
})
if err != nil {
t.Fatalf("seed requirement: %v", err)
}
cand := seedCandidate(t, org, "none", "active", true)
slate, err := q.CreateSlate(ctx, dbgen.CreateSlateParams{
OrganizationID: org, RequirementID: req.ID, Name: "WS8 Curation Slate", Status: "in_review",
})
if err != nil {
t.Fatalf("seed slate: %v", err)
}
mv := engine.ModelVersion
if _, err := q.AddSlateMember(ctx, dbgen.AddSlateMemberParams{
OrganizationID: org, SlateID: slate.ID, CandidateID: cand.ID,
TotalScore: dec0("80"), Band: "yellow",
ClearanceScore: dec0("100"), SkillsScore: dec0("80"), CertsScore: dec0("100"),
CustomerScore: dec0("100"), ExperienceScore: dec0("100"), LocationScore: dec0("100"),
Explanation: "seeded", Trace: []byte("[]"), GapSummary: []byte("[]"), ModelVersion: &mv,
}); err != nil {
t.Fatalf("seed slate member: %v", err)
}
if _, err := q.SetSlateMemberDecision(ctx, dbgen.SetSlateMemberDecisionParams{
SlateID: slate.ID, CandidateID: cand.ID, Decision: decision,
}); err != nil {
t.Fatalf("seed decision: %v", err)
}
matched := []map[string]any{{
"RequiredSkillID": skillA.String(), "RequiredSkillName": "req",
"CandidateSkillID": skillB.String(), "CandidateName": "cand",
"Similarity": similarity, "Exact": false, "Matched": true,
}}
mj, err := json.Marshal(matched)
if err != nil {
t.Fatalf("marshal matched skills: %v", err)
}
if err := q.UpsertCandidateEvaluation(ctx, dbgen.UpsertCandidateEvaluationParams{
OrganizationID: org, CandidateID: cand.ID, RequirementID: req.ID,
TotalScore: dec0("80"), Band: "yellow",
ClearanceScore: dec0("100"), SkillsScore: dec0("80"), CertsScore: dec0("100"),
CustomerScore: dec0("100"), ExperienceScore: dec0("100"), LocationScore: dec0("100"),
Explanation: "seeded", MatchedSkills: mj, GapSummary: []byte("[]"), ModelVersion: engine.ModelVersion,
}); err != nil {
t.Fatalf("seed evaluation: %v", err)
}
}
// seedSubstitutionAggregate writes one live in tenant teaching_signal_aggregates row for the substitution
// pattern, so the generator's governance confidence read finds the supporting evidence.
func seedSubstitutionAggregate(t *testing.T, org uuid.UUID, band, outcome string, obs, succ int) {
t.Helper()
if _, err := migrator.Exec(context.Background(),
`INSERT INTO teaching_signal_aggregates
(tenant_id, dp_applied, pattern, band, outcome, model_version, observations, successes, window_start, window_end)
VALUES ($1, false, 'adjacent_skill_substitution_outcome', $2, $3, $4, $5, $6, '1970-01-01', now()::date)`,
org, band, outcome, engine.ModelVersion, obs, succ); err != nil {
t.Fatalf("seed substitution aggregate: %v", err)
}
}
// getOnlyPendingProposal reads the single pending proposal for the pair and fails if there is not exactly one.
func getOnlyPendingProposal(t *testing.T, from, to uuid.UUID) dbgen.CurationProposal {
t.Helper()
rows, err := migrator.Query(context.Background(),
`SELECT id, kind, from_skill_id, to_skill_id, relation, proposed_strength,
evidence_observations, evidence_successes, model_version, status, approver, approved_at, created_at
FROM curation_proposals
WHERE from_skill_id = $1 AND to_skill_id = $2 AND status = 'pending'`, from, to)
if err != nil {
t.Fatalf("query pending proposal: %v", err)
}
defer rows.Close()
var out []dbgen.CurationProposal
for rows.Next() {
var p dbgen.CurationProposal
if err := rows.Scan(&p.ID, &p.Kind, &p.FromSkillID, &p.ToSkillID, &p.Relation, &p.ProposedStrength,
&p.EvidenceObservations, &p.EvidenceSuccesses, &p.ModelVersion, &p.Status, &p.Approver, &p.ApprovedAt, &p.CreatedAt); err != nil {
t.Fatalf("scan proposal: %v", err)
}
out = append(out, p)
}
if len(out) != 1 {
t.Fatalf("want exactly 1 pending proposal for the pair, got %d", len(out))
}
return out[0]
}
// countProposals counts all proposals (any status) for the pair, to assert idempotency.
func countProposals(t *testing.T, from, to uuid.UUID) int {
t.Helper()
var n int
if err := migrator.QueryRow(context.Background(),
"SELECT count(*) FROM curation_proposals WHERE from_skill_id = $1 AND to_skill_id = $2", from, to).Scan(&n); err != nil {
t.Fatalf("count proposals: %v", err)
}
return n
}
// substitutesEdge returns the staged or live skill_relationships edge id and its verified_at for the pair.
func substitutesEdge(t *testing.T, from, to uuid.UUID) (uuid.UUID, *time.Time) {
t.Helper()
var id uuid.UUID
var verifiedAt *time.Time
err := migrator.QueryRow(context.Background(),
`SELECT id, verified_at FROM skill_relationships
WHERE from_skill_id = $1 AND to_skill_id = $2 AND relation = 'substitutes_for'`, from, to).Scan(&id, &verifiedAt)
if errors.Is(err, pgx.ErrNoRows) {
return uuid.Nil, nil
}
if err != nil {
t.Fatalf("query substitutes edge: %v", err)
}
return id, verifiedAt
}
// assertProposalApproved checks the proposal status, approver, and approved_at after the first human step.
func assertProposalApproved(t *testing.T, id, approver uuid.UUID) {
t.Helper()
var status string
var gotApprover *uuid.UUID
var approvedAt *time.Time
if err := migrator.QueryRow(context.Background(),
"SELECT status, approver, approved_at FROM curation_proposals WHERE id = $1", id).Scan(&status, &gotApprover, &approvedAt); err != nil {
t.Fatalf("read approved proposal: %v", err)
}
if status != "approved" {
t.Fatalf("proposal status = %q, want approved", status)
}
if gotApprover == nil || *gotApprover != approver {
t.Fatalf("proposal approver = %v, want %v", gotApprover, approver)
}
if approvedAt == nil {
t.Fatal("proposal approved_at is NULL after approve")
}
}
// assertEdgeVerifier checks the verifier recorded on the live edge after the second human step.
func assertEdgeVerifier(t *testing.T, edgeID, verifier uuid.UUID) {
t.Helper()
var gotVerifier *uuid.UUID
if err := migrator.QueryRow(context.Background(),
"SELECT verifier FROM skill_relationships WHERE id = $1", edgeID).Scan(&gotVerifier); err != nil {
t.Fatalf("read edge verifier: %v", err)
}
if gotVerifier == nil || *gotVerifier != verifier {
t.Fatalf("edge verifier = %v, want %v (distinct from the approver)", gotVerifier, verifier)
}
}
// resolveBtoA builds a FRESH DeterministicAliasResolver (so it re reads the edges from the DB, never a stale
// cache) and asks whether candidate skill B resolves to required skill A. The resolver is the exact component
// that loads verified_at IS NOT NULL substitutes_for edges, so this is the live test of the two step gate: it
// returns false while the edge is staged inert and true once it is verified.
func resolveBtoA(ctx context.Context, skillB, skillA uuid.UUID) bool {
r := matching.NewDeterministicAliasResolver(appDB)
_, ok := r.ResolveAlias(ctx,
engine.RequiredSkill{SkillID: skillA.String(), Name: "req", Mandatory: true},
[]engine.CandidateSkill{{SkillID: skillB.String(), Name: "cand"}})
return ok
}