# missionMATCH — PLATFORM INVENTORY (read-only audit)
**Scope:** Mission Workforce Technologies platform (engines screenMATCH, slateMATCH, missionMATCH).
**Date:** 2026-06-26. **Method:** read-only. Findings are verified against the code, config, the live
database (M4), and the running services (Bubbles). No files were changed; no secret values are printed
(variable/key names only). Where a fact could not be verified it is marked **UNKNOWN**.
State legend per item: **BUILT AND CONFIGURED** / **BUILT BUT NOT CONFIGURED** / **STUBBED** / **MISSING**.
Verification sources: (a) direct reads of the repo at `/Users/josephanady/missionmatch`; (b) live SQL
introspection of the database on M4 (`100.94.8.94`, psql `-U postgres`, read-only SELECTs); (c) live
read-only inspection of the Bubbles host (systemd/nginx/cron configs, `ss`, env key NAMES only).
---
## STEP 0 — ORIENT
- **Working directory / repo root:** `/Users/josephanady/missionmatch` (the Go module root; `go.mod`
declares `module github.com/missionworkforce/missionmatch`, `go 1.26`).
- **Git:** local repository, **no remote** (`git remote -v` empty). Branch **`master`**, last commit
**`6c7c40f` "Workstream E (deploy): umbrella browser title"**. History is linear merges of workstreams
A–E (the three-tool split).
- **Folder layout (2 levels):**
- `cmd/` — entrypoints: `api`, `sam-sync`, `awards-sync`, `rates-sync`, `import-candidates`,
`opp-sync`, `provision-org`, `provision-owner`.
- `internal/modules/` — domain modules (auth, matching, screenmatch, slatematch, missionmatch,
commandcenter, opportunities, opportunity_intelligence, awards, wages, sourcing, eeo, governance,
plus seven empty dirs — see §13).
- `internal/platform/` — cross-cutting (config, database, tenant, security, audit, storage,
embeddings, extraction, samgov, samawards, marketintel, bls, sourcing, forecast, ratelimit, obs).
- `internal/integration/` — `//go:build integration` live-DB tests.
- `db/` — `migrations/` (0001–0016), `migrations_eeo/` (segregated EEO), `bootstrap/00_roles.sql`,
`queries/` (sqlc sources), `seed/`.
- `frontend/` — Vite + React 19 SPA (`src/`, `public/`, `dist/`).
- `docs/` — `STACK_DECISIONS.md`, `MODEL_CARD.md`, `openapi.yaml`.
- `infra/systemd/` — unit + timer files. `migrations/` (top level) is **empty** (vestigial).
- **Languages / frameworks (proof file):**
- **Go 1.26** backend — `go.mod` (chi `v5`, pgx `v5`, sqlc-generated `dbgen`, `shopspring/decimal`,
`pgvector/pgvector-go v0.4.0`). Toolchain on this machine: `go1.26.4 darwin/arm64`.
- **TypeScript / React 19 frontend** — `frontend/package.json` (React `^19.2.7`, Vite `^8.1.0`,
TanStack Query `^5`, react-router-dom `^7`, Tailwind `^4.3.1` via `@tailwindcss/vite`, oxlint,
TypeScript `~6.0.2`). *(Note: CLAUDE.md says "React 18"; the repo is React 19.)*
- **SQL** — `db/migrations/*.sql` (goose-annotated), compiled by **sqlc** (`sqlc.yaml`).
- **bash** — `cmd/*` are Go; deploy/sync scripts on the host are bash (`/usr/local/bin/mm-*.sh`).
- No Python anywhere in the repo (house rule).
- **Build / run:**
- Backend build (`Makefile:11-18`): `CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOFIPS140=v1.0.0 go build
-trimpath -o mm-api ./cmd/api` (+ the sync/provision binaries). Run on the host via systemd
(`infra/systemd/mm-api.service`, `GODEBUG=fips140=on`).
- Frontend build (`frontend/package.json` scripts): `tsc -b && vite build` → `dist/`, served as static
files by nginx.
- sqlc codegen: `make sqlc`. Migrations: `goose` (`make migrate-up`).
---
## 1 — RUNTIME AND DEPLOYMENT (verified live on Bubbles, read-only)
Self-hosted on **Bubbles**. **State: BUILT AND CONFIGURED and LIVE.**
- **API service:** `mm-api.service` — `active` + `enabled` (systemd). `ExecStart=/home/user/missionmatch/mm-api`,
`EnvironmentFile=/etc/missionmatch/api.env`, `GODEBUG=fips140=on`. Process listens **`127.0.0.1:8094`**
only (loopback; reached via nginx). Unit hardening present: `NoNewPrivileges`, `ProtectSystem=strict`,
`ProtectHome=read-only`, `PrivateTmp`, `ReadWritePaths=/home/user/missionmatch`
(`infra/systemd/mm-api.service`).
- **Process manager:** **systemd** (service + timers); **cron.d** for two jobs. No docker/pm2.
- **Reverse proxy (nginx) — hostname → upstream:**
- `missionmatch.thatwebhostingguy.com` → SPA root `/var/www/sites/missionmatch`; `location /api/` and
`= /healthz` → `proxy_pass http://127.0.0.1:8094`; SPA fallback `try_files … /index.html`. HTTP→HTTPS
301. (`/etc/nginx/sites-available/missionmatch.thatwebhostingguy.com`.)
- `screenmatch.thatwebhostingguy.com` → **`return 301 https://missionmatch.thatwebhostingguy.com/screen`**
(both :80 and :443). The old host is now a redirect only.
- Both vhosts are symlinked into `sites-enabled/`.
- **TLS:** `/etc/letsencrypt/live/thatwebhostingguy.com/fullchain.pem`, SAN `*.thatwebhostingguy.com` +
apex (wildcard covers both subdomains); `ssl_protocols TLSv1.2 TLSv1.3`.
- **Frontend serving:** static `dist/` from `/var/www/sites/missionmatch`; `
` confirmed live as
"missionMATCH, Mission Workforce Intelligence". Live smoke (read-only `curl`): umbrella `/healthz`,
`/command`, `/slate` → 200; `screenmatch` host → 301 to `/screen`; all three `/api/{screenmatch,
slatematch,missionmatch}/…` → 403 (auth-gated, i.e. routes are live).
- **Scheduled jobs (verified):**
- systemd timers **enabled**: `mm-sam-sync.timer` (daily ~04:00), `mm-awards-sync.timer` (daily 04:30),
`mm-rates-sync.timer` (weekly Sun 05:00). `list-timers` shows awards/rates with next-elapse;
**`mm-sam-sync.timer` showed no next-elapse** at audit time → verify its `OnCalendar` is firing
(**flag**).
- cron.d: **`/etc/cron.d/mm-opp-sync`** = `30 4 * * * root /usr/local/bin/mm-opp-sync.sh`;
**`/etc/cron.d/mm-pg-backup`** = `30 3 * * * root /usr/local/bin/mm-pg-backup.sh >> /mnt/storage/mm-backups/backup.log`.
- **PDL import has no schedule** (no timer, no cron) → manual only.
- **Listening ports:** `mm-api` on `127.0.0.1:8094`. Bubbles also runs many unrelated services on
`127.0.0.1:8000–8013` (other vhosts/apps, out of scope). nginx terminates :80/:443.
- **Database host:** the API connects out to Postgres on **M4** (`100.94.8.94:5432`) over Tailscale
(`sslmode=disable`, transport encrypted by WireGuard). The DB is **not** on Bubbles.
---
## 2 — DATABASE (introspected live on M4, read-only)
- **Engine:** **PostgreSQL 16.14** (Homebrew, native on M4, `aarch64-apple-darwin`).
- **Extensions (only two):** `plpgsql 1.0`, **`vector 0.8.0` (pgvector)**. No others (no `pg_trgm`,
no `uuid-ossp`; UUIDs use `gen_random_uuid()` from core).
- **Schemas:** `public` (31 tables incl. 2 goose bookkeeping) + **`eeo`** (1 table). Segregation is
deliberate.
- **Roles (verified `pg_roles`):** `missionmatch_app` (login, **NOBYPASSRLS**), `missionmatch_compliance`
(login, **NOBYPASSRLS**), `missionmatch_migrator` (login, **BYPASSRLS**). **None are superuser.**
Definitions in `db/bootstrap/00_roles.sql`.
- **Migration tool / state:** **goose**. `public` at **version 16** (`db/migrations/0001…0016`); EEO
tracked separately in `goose_db_version_eeo` at **version 1** (`db/migrations_eeo/0001_eeo.sql`).
Live schema matches migrations (no drift detected: 0015 candidate_evaluations + 0016 decision_note
present live).
### Tables, RLS, and reach (RLS = enabled & forced unless noted)
**Tenant tables — RLS ENABLED + FORCED, policy `org = NULLIF(current_setting('app.current_org_id',true),'')::uuid`:**
`organizations` (SELECT/UPDATE policies only), `users`, `candidates`, `candidate_skills`,
`candidate_certifications`, `candidate_customer_experience`, `candidate_evaluations`, `requirements`,
`required_skills`, `required_certifications`, `opportunities`, `slates`, `slate_members`, `sourcing_log`,
`aedt_candidate_notices`, `aedt_inventory`, and **`eeo.candidate_demographics`**.
**Global / non-RLS tables (reference, append-only audit, or auth-secret reached only via SECURITY
DEFINER):** `skills`, `sam_opportunities`, `usaspending_awards`, `bls_wages`, `opportunity_records`,
`aedt_model_versions`, `audit_logs` (append-only, see §6), `auth_credentials`, `auth_sessions`,
`mfa_totp`, `mfa_recovery_codes`, `user_invites`, `login_attempts`, `goose_db_version*`.
**SECURITY DEFINER functions (19, all `public`):** `provision_org`, and the auth set:
`auth_accept_invite`, `auth_create_invite`, `auth_create_session`, `auth_get_credential`,
`auth_lookup_login`, `auth_mfa_activate`, `auth_mfa_advance_step`, `auth_mfa_enroll`, `auth_mfa_get`,
`auth_mfa_reset`, `auth_recovery_claim`, `auth_recovery_lookup`, `auth_recovery_store`,
`auth_revoke_session`, `auth_revoke_user_sessions`, `auth_rotate_session`, `auth_set_credential`,
`auth_validate_session`. These are the only path the app role has to the auth-secret tables;
`provision_org` is **not** granted to `missionmatch_app`.
### Columns (per table; authoritative DDL is `db/migrations/`)
```
organizations: id uuid PK, name text, slug text UNIQUE, status text, created_at, updated_at
users: id uuid PK, organization_id uuid, email text, full_name text, role text, status text,
last_login_at?, created_at, updated_at, mfa_enrolled bool (UNIQUE org_id+id, global lower(email))
candidates: id uuid PK, organization_id, full_name, email?, headline?, clearance_level,
clearance_poly, clearance_status, clearance_granted_at? date, clearance_expires_at? date,
reinvestigation_due_at? date, years_experience int, location?, remote bool, relocatable bool,
available bool, resume_embedding vector(384)?, search_tsv tsvector?, created_at, updated_at,
deleted_at?, source text, external_id?, anonymized_at?, clearance_provenance text,
clearance_verified_at? (UNIQUE org_id+id; UNIQUE external; 6 CHECKs; HNSW + GIN(tsv) indexes)
candidate_skills: organization_id, candidate_id, skill_id (composite FK to candidates+skills, RLS)
candidate_certifications: organization_id, candidate_id, name
candidate_customer_experience: organization_id, candidate_id, agency
candidate_evaluations: organization_id, candidate_id, requirement_id (composite PK), total_score
numeric, band text, clearance_score, skills_score, certs_score, customer_score, experience_score,
location_score (all numeric), explanation text, matched_skills jsonb, gap_summary jsonb,
model_version text, evaluated_by?, evaluated_at, updated_at (8 CHECKs, 3 FKs)
requirements: id uuid PK, organization_id, opportunity_id?, title, customer_agency?, required_level,
required_poly, required_years int, work_location?, remote_allowed bool, key_personnel bool,
created_at, updated_at, deleted_at?, labor_category? text, quantity int (UNIQUE org_id+id, 4 CHECKs)
required_skills: organization_id, requirement_id, skill_id, mandatory bool
required_certifications: organization_id, requirement_id, name
opportunities: id uuid PK, organization_id, name, solicitation_number?, customer_agency?, due_date?,
status, created_at, updated_at, value? numeric, value_source text, federal_ref? (UNIQUE org_id+id)
slates: id uuid PK, organization_id, requirement_id, name, status, created_by?, created_at, updated_at
slate_members: organization_id, slate_id, candidate_id (PK slate+cand), total_score numeric, band,
6 component _score numerics, explanation, trace jsonb, gap_summary jsonb, decision text, decided_by?,
decided_at?, rank? int, created_at, model_version?, decision_note? (9 CHECKs, 3 FKs)
skills: id uuid PK, name text UNIQUE, category?, embedding vector(384)? (partial HNSW index)
candidate (eeo).candidate_demographics: id bigint PK, organization_id, candidate_id, gender?,
race_ethnicity?, veteran_status?, disability_status?, consent_given bool, collected_by?, collected_at
(RLS forced; NO FK to public.candidates; compliance role only)
audit_logs: id bigint PK, organization_id?, actor_user_id?, action, entity_type?, entity_id?,
old_values jsonb?, new_values jsonb?, ip_address?, user_agent?, created_at (append-only triggers)
auth_credentials: user_id PK, organization_id, email_normalized, password_hash, pepper_version int,
failed_streak int, locked_until?, password_changed_at, created_at, updated_at (UNIQUE global email)
auth_sessions: id uuid PK, token_hash bytea UNIQUE, user_id, organization_id, state text,
mfa_satisfied bool, created_at, last_seen_at, idle_expires_at, absolute_expires_at, revoked_at?,
rotated_from?, ip_address?, user_agent? (1 CHECK)
mfa_totp: user_id PK, organization_id, secret_ciphertext bytea, secret_nonce bytea, key_version int,
status text, last_used_step bigint, created_at, activated_at? (1 CHECK)
mfa_recovery_codes: id uuid PK, user_id, organization_id, code_hash, pepper_version, lookup_tag bytea,
used_at?, created_at
user_invites: id uuid PK, organization_id, email_normalized, invited_user_id, token_hash bytea UNIQUE,
role, expires_at, accepted_at?, created_by, created_at (1 CHECK)
login_attempts: id bigint PK, email_normalized, organization_id?, ip_address?, succeeded bool, reason?,
created_at
aedt_model_versions: version PK, weights jsonb, band_cuts jsonb, notes?, effective_from, created_at
aedt_inventory: id uuid PK, organization_id, tool_key, display_name, purpose, model_version?, active
bool, created_at, updated_at (UNIQUE org+tool)
aedt_candidate_notices: id uuid PK, organization_id, candidate_id, notice_version, data_categories
jsonb, retention_days int, disclosed_by?, disclosed_at (2 FKs)
sourcing_log: id uuid PK, organization_id, candidate_id?, external_id, source, provider, query,
legal_basis, collected_at, created_at
sam_opportunities: notice_id PK, title, solicitation_number, department, full_parent_path, sub_agency,
naics_code, psc_code, set_aside_code, set_aside, notice_type, base_type, posted_date?,
response_deadline?, active bool, pop_city/state/zip, ui_link, description_url, resource_links jsonb,
poc jsonb, raw jsonb, source, synced_at (6 filter indexes)
usaspending_awards: (recipient/uei/naics/amount/galapagos-flag; indexes idx_awards_galapagos/naics/
recipient) — keyless USASpending + SAM Contract Awards normalized
bls_wages: series_id PK-part, occupation_code, occupation_title, role, area, area_name, metric,
period_year, value numeric?, raw jsonb, synced_at
opportunity_records: id uuid PK, source_name, source_record_id, dedupe_key, record_kind, title,
agency, sub_agency, naics, psc, pop_*, set_aside*, contract_vehicle, estimated_value numeric?,
value_source, incumbent_name, incumbent_uei, posted_date?, response_deadline?, award_date?,
estimated_award_date?, pop_start/end_date?, ui_link, is_planning bool, raw_object_key, raw_sha256,
fetched_at, synced_at (UNIQUE source+record; 6 indexes)
```
### Indexes of note (verified)
- **Vector (HNSW):** `idx_candidates_emb_hnsw` (candidates.resume_embedding), `idx_skills_embedding_hnsw`
(skills.embedding, partial WHERE NOT NULL).
- **Full-text:** `idx_candidates_tsv` (GIN on candidates.search_tsv).
- **Tenant composites lead with `organization_id`:** `idx_candidates_org`, `idx_requirements_org`,
`idx_slates_org`, `idx_slate_members_org`, `candidate_evaluations` org+req / org+cand, etc.
- **Composite-FK targets:** `uq_{candidates,requirements,opportunities,slates,users,aedt_inventory}_org_id`
(`UNIQUE (organization_id, id)`).
### Live row counts (introspected; tenant tables inflated by integration-test seeding)
| Table | Rows | Note |
|---|---|---|
| organizations | 1017 | **~647 are per-test orgs** (each integration test seeds one) |
| users | 206 | mostly test |
| candidates | 2787 | **demo org `1111…1111` = 1511**; rest ≈ test residue (~8/org) |
| candidate_skills | 1707 | |
| requirements | 387 | |
| slates | 165 | |
| slate_members | 3424 | |
| candidate_evaluations | 64 | persisted screen results (new in WS-B) |
| skills | 61 | global ontology |
| sam_opportunities | 1402 | **real feed load** |
| usaspending_awards | 525 | **real feed load** |
| bls_wages | 20 | **real feed load** (HI/NM × roles × metrics) |
| opportunity_records | 2471 | **real feed load** (opportunity intelligence) |
| sourcing_log | 10 | PDL imports |
| audit_logs | 13123 | append-only |
| eeo.candidate_demographics | 280 | segregated, compliance-only |
**Embedding / FTS population (decisive for an AI layer):**
`candidates`: **resume_embedding non-null = 144 / 2787**; **search_tsv non-null = 2787 / 2787** (FTS fully
populated, generated column). `skills`: **embedding non-null = 0 / 61** (no skill vectors at all). The
pgvector columns + HNSW indexes exist but are essentially **unpopulated**.
---
## 3 — THE DETERMINISTIC MATCHING AND SCORING ENGINE **BUILT AND CONFIGURED**
- **Where computed:** the pure function `engine.ScoreCandidate(ctx, Candidate, Requirement, SkillMatcher)`
in `internal/modules/matching/engine/engine.go:54`. Documented PURE — no DB/IO/clock/randomness
(`engine/types.go:1-16`, `engine/engine.go:47-53`). The skill matcher is **injected**.
- **Inputs:** `engine.Candidate` (`types.go:104-115`, **no demographic field** by design, `types.go:103`)
and `engine.Requirement` (`types.go:126-137`).
- **Output `engine.MatchResult` (`types.go:206-224`):** `Total decimal` (0–100, 2dp), `Band`, six
component decimals, `Explanation`, `GapSummary []string`, `MatchedSkills []SkillMatch`, and
`Trace []ComponentScore` (per-component Weight/Raw/Weighted/Rationale).
- **Fixed weights (`engine.go:20-27`):** Clearance 0.25, Skills 0.25, Certifications 0.15,
CustomerExperience 0.15, Experience 0.10, Location 0.10 (sum = 1.00).
- **Bands (`engine.go:33-37`, `bandFor` `:301-310`):** Green ≥ 90 ("Proposal Ready"); Yellow 75–89.99
("Potential"); Red < 75 ("Recruiting Required"). Semantic threshold `0.80` (`engine.go:31`).
- **Component logic:** Clearance binary 0/100 — exact-or-higher + active + poly (`:114-138`); Skills =
fraction met, exact-then-semantic (`:155-187`); Certs fraction (`:223-242`); Customer binary
(`:246-254`); Experience full-or-linear (`:258-270`); Location = avg(location fit, availability)
(`:274-297`). All math in `shopspring/decimal`. Rationale per component + overall `Explanation`
(`buildExplanation` `:312-321`). `ModelVersion = "v1.0.0"` (`:16`).
- **Single source of truth — per-candidate, YES; one org-level exception:**
- The **only** non-test callers of `engine.ScoreCandidate` are inside `internal/modules/matching`:
`service.go` (ScreenCandidate `:146`, GenerateSlate `:345`, Screen `:445`, RankCandidates `:485`,
rankOpenOpportunities `:558`), `intel.go:215`, `missioncheck.go:96`. So the **screen view, ranked
candidates / top matches, the Candidates Intelligence page, and slate generation all run the one
engine.** Slate detail / proposal-readiness / export / who-can-support read the **frozen persisted**
scores, never rescore (`slate_service.go`, `mission_service.go:31`).
- **Exception (by design, flagged for the AI layer):** the **Command Center org-level "Mission
Readiness" headline is a SEPARATE SQL composite**, NOT `engine.ScoreCandidate`. Its weights are
distinct — clearance_coverage 0.30, skill_coverage 0.25, labor_category_coverage 0.20,
proposal_readiness 0.15, availability_coverage 0.10 (`commandcenter/service.go:30-37`) — and its
eligibility is a greedy SQL assignment that **re-encodes** the clearance/years/mandatory-skill gates
in SQL rather than calling the engine (`commandcenter/service_assignment.go:30-153`). It is labeled
"DISTINCT from the engine's per-candidate weights." So org-level readiness mirrors but does not reuse
the engine path — a second gating implementation to keep in sync.
- **Persisted write path:** `persistEvaluationOnTx` (`service.go:101-107`) → `UpsertCandidateEvaluation`
keyed on `(org, candidate, requirement)`, written inside the same tenant tx as `POST /match` and
`POST /screen`. Slate generation freezes a parallel `slate_members` record with `ModelVersion`. Every
served/frozen score also writes an append-only AEDT audit event (`servedScoreEvent` `service.go:76-83`).
- **Exposing endpoints:** `POST /screen`, `POST /match`, `GET /requirements/{id}/candidates`,
`POST /slates/generate`, `GET /candidates/intelligence`, `POST /mission-check/*`, `GET
/requirements/{id}/support` (reads stored). All also reachable under the three tool namespaces over
the same shared `matching.Handler` (no scoring re-implemented; cross-tool equality proven by
`TestSharedCoreScoresIdenticalAcrossTools`).
---
## 4 — DATA FEEDS
All sync jobs read the write DSN from `MM_MIGRATOR_DATABASE_URL` (fallback `MM_DATABASE_URL`) and API
keys from server-side env (`/etc/missionmatch/sam-sync.env`). Live-env key NAMES present in that file
(verified, values redacted): `SAM_API_KEY`, `SAM_API_BASE`, `BLS_API_KEY`, `PDL_API_KEY`,
`MM_MIGRATOR_DATABASE_URL`, `MM_COMPLIANCE_DATABASE_URL`, plus **unused** `DATA_GOV_API_KEY`,
`KAGGLE_USERNAME`, `KAGGLE_KEY`, `HF_TOKEN` (present in env but not referenced by any code — dead).
| Feed | Code | Auth (key NAME) | Schedule | Live? | Loaded rows |
|---|---|---|---|---|---|
| **SAM opportunities** | `cmd/sam-sync`, `internal/platform/samgov/*` | `SAM_API_KEY` query param (`sam-sync/main.go:33`) | systemd `mm-sam-sync.timer` daily ~04:00 *(no next-elapse at audit — verify)* | **BUILT AND CONFIGURED / LIVE** | `sam_opportunities` 1402 |
| **USASpending awards + SAM Contract Awards + Galapagos past-perf by UEI** | `cmd/awards-sync`, `internal/platform/{marketintel,samawards}/*`, `internal/modules/awards` | USASpending keyless; SAM Awards reuses `SAM_API_KEY`; Galapagos UEI const `N3GDEB78RNF8` (`awards-sync/main.go:26`) | systemd `mm-awards-sync.timer` daily 04:30 | **BUILT AND CONFIGURED / LIVE** | `usaspending_awards` 525 |
| **BLS OEWS wages** | `cmd/rates-sync`, `internal/platform/bls/*`, `internal/modules/wages` | `BLS_API_KEY` in body (`rates-sync/main.go:25`) | systemd `mm-rates-sync.timer` weekly Sun 05:00 | **BUILT AND CONFIGURED / LIVE** | `bls_wages` 20 (HI/NM) |
| **People Data Labs sourcing** | `cmd/import-candidates`, `internal/platform/sourcing/*`, `internal/modules/sourcing` | `PDL_API_KEY` `X-Api-Key` (`import-candidates/main.go:39`) | **none — MANUAL** (`make import-candidates`) | **BUILT BUT NOT CONFIGURED** (live provider, CLI-only, no API trigger) | `sourcing_log` 10 |
| **Opportunity Intelligence ingestion** | `cmd/opp-sync`, `internal/modules/opportunity_intelligence/*` | per-adapter | **cron.d `mm-opp-sync` daily 04:30** (verified on host) | mixed (see below) | `opportunity_records` 2471 |
**Opportunity Intelligence adapters (`…/adapters/`):** `sam.go`, `sam_contract_awards.go`,
`usaspending.go`, `fallback.go` (SAM Awards→USASpending) = **LIVE & registered** (`opp-sync/main.go:62-67`);
`forecast.go` (generic GSA/agency CSV) = **BUILT BUT NOT CONFIGURED** — only appended when
`MM_FORECAST_CSV` is set (`opp-sync/main.go:73-81`); `stubs.go` **GovWin IQ / GovTribe / GSA eBuy** =
**STUBBED**, never registered, return `ErrRequiresLicense`/`ErrRequiresTenantCredential` (`stubs.go:25-63`).
**Compliance handling wired into feeds (BUILT AND CONFIGURED):** contact-field exclusion via
`--includeContact` default-false (`platform/sourcing/pdl.go`); per-import **sourcing compliance log**
(source/provider/query/legal_basis, same tx as the candidate); **right-to-erasure** —
`sourcing/service.go` Delete anonymizes in place (detaches the log FK so the audit survives, wipes
PII + resume vector + child rows, writes `privacy.candidate.anonymized`), gated by `CapPrivacyErase`
(`sourcing/handler.go:26`).
---
## 5 — SKILLS, CLEARANCE, AND LABOR CATEGORIES
- **Skills ontology — BUILT, embeddings EMPTY.** Tables `skills` (global; `embedding vector(384)` +
partial HNSW), `candidate_skills`, `required_skills` (`mandatory bool`) — `db/migrations/0002_matching.sql`.
**No adjacency / synonym / graph table exists** (grep finds "ontology" only in comments); semantic
adjacency is delegated entirely to embedding cosine. The `internal/modules/skills/` dir is **empty**;
all writes upsert **by name only** (`UpsertSkill` from missioncheck/sourcing/adopt), so
`skills.embedding` is never populated in the live path (live count **0/61**), and the generated
`SearchSkillsByEmbedding` query has **no live caller**.
- **Skill matcher — BUILT AND CONFIGURED.** `engine.matchOne` resolves exact-by-id → exact-by-name →
injected `SkillMatcher` at 0.80 (`engine.go:191-219`). The semantic impl `EmbeddingSkillMatcher`
(`matching/skillmatcher.go`) embeds the skill **name strings** at request time and takes best cosine in
`decimal`; it does **not** use the HNSW index. Wired with `embeddings.New(cfg)` (`cmd/api/main.go:109,127`)
— but the embedder default is `fake`/deterministic (see §9 and live-env note), so in practice exact
matching dominates.
- **Clearance / poly / status / reciprocity — BUILT AND CONFIGURED.** Ordered enums in
`engine/types.go` (`ClearanceLevel` Unknownadmin(3)>recruiter(2)>member(1)>viewer(0); 14
capabilities (candidate.read/write, requirement.write, slate.write, search.run, export.run,
sourcing.read, privacy.erase, user.invite, user.role.change, user.disable, org.settings.write,
audit.read, compliance.read). Enforced by `RequireCapability`/`RequireRole` middleware from the
**validated session in context** (never a header), 403 before the handler. `OutRanks` enforces strict
separation of duties (invite/role-change/disable only someone you outrank, never self/peer/superior).
- **Invite / activation / provisioning — link delivery MANUAL.** Invite (`POST /auth/invite`) and
recovery reissue **return the one-time token in the JSON response** (`auth/dto.go:91-95`,
`handler.go:263-269`) — there is **no email/SMS transport anywhere** (grep: zero SMTP/mailer code).
Activation via SECURITY DEFINER `auth_accept_invite`. Org provisioning is **migrator-only**
(`cmd/provision-org` → `provision_org` SECURITY DEFINER; not granted to the app role; no HTTP surface).
- **Audit taxonomy — BUILT AND CONFIGURED.** Writer `internal/platform/audit/audit.go` (`OnTx` org-scoped
+ `System` NULL-org). `audit_logs` is **append-only** (BEFORE UPDATE/DELETE/TRUNCATE triggers
`forbid_mutation()`; app granted INSERT/SELECT only) — `db/migrations/0001_foundation.sql:82-104`. Audit
**reads are tenant-isolated** (the read query binds `organization_id = current GUC`, gated by
`CapAuditRead`). ~33 distinct Action strings, e.g. `aedt.score.served`, `aedt.slate.generated`,
`aedt.slate_member.decided`, `aedt.slate.exported`, `aedt.cross_tool.read`, `aedt.ephemeral.evaluated`,
`retention.candidate.anonymized`, `privacy.candidate.anonymized`, `candidate.pii.view`, the full
`auth.*`/`user.*`/`authz.*` set, `opportunity.ingest`, `requirement.adopted_from_forecast`. Only
`aedt.score.served` is consumed by an audit READ (the EEO screening join, §10).
---
## 7 — API SURFACE (all under `/api`; tenant group = session resolver → CSRF → per-route RBAC)
Wired in `cmd/api/main.go`. The flat matching paths AND the three tool namespaces share one
`matching.Handler` (`main.go:243-249`): flat = backward-compat alias, namespaced = canonical.
**Health/metrics (no `/api`, unauthenticated):** `GET /healthz`, `GET /readyz`, `GET /healthz/fips`,
`/metrics` (`main.go:191-227`).
**auth (`auth/handler.go`):**
- Public (pre-tenant): `GET /api/auth/csrf`, `POST /api/auth/login`, `…/mfa/{enroll,activate,verify,
recover}`, `…/invite/accept`, `…/logout`.
- Session-only: `GET /api/auth/session`, `POST /api/auth/logout-all`, `POST /api/auth/password/change`.
- Admin/RBAC: `GET /api/users` (RoleAdmin), `POST /api/users/invite` (user.invite),
`PATCH /api/users/{id}/role` (user.role.change), `PATCH /api/users/{id}/status` (user.disable),
`POST /api/users/{id}/mfa/reset` (user.disable), `POST /api/users/{id}/recovery` (user.invite).
**matching core (flat + `/api/screenmatch` + `/api/slatematch` + `/api/missionmatch`):**
- ScreenRoutes (cap `search.run`/`candidate.write`): `POST /match`, `POST /screen`,
`GET /requirements/{id}/candidates`, `GET /candidates/{id}/opportunities`,
`POST /mission-check/opportunities`, `POST /mission-check/screen`, `POST /candidates`,
`PATCH /candidates/{id}/clearance-provenance`.
- SlateRoutes: `POST /slates/generate` (slate.write), `PATCH /slates/{id}/members/{cid}/decision`
(slate.write), `GET /slates/{id}/members/{cid}/trace` (candidate.read), `GET
/requirements/{id}/slates`, `GET /slates/{id}`, `GET /slates/{id}/proposal-readiness` (candidate.read),
`GET /slates/{id}/export` (**export.run**, audited).
- ReadRoutes (candidate.read): `GET /candidates`, `GET /candidates/{id}`, `GET /requirements`,
`GET /requirements/{id}`.
- IntelRoutes/MissionRoutes (search.run): `GET /candidates/intelligence`,
`GET /requirements/{id}/support`.
**commandcenter (flat + `/api/missionmatch`):** `GET /command-center` (candidate.read),
`POST /command-center/upcoming/{recordID}/adopt` (requirement.write).
**Other modules:** `GET /opportunities`, `GET /opportunities/{id}` — **no capability gate**;
`GET /opportunity-intelligence` (search.run); `GET /awards`, `GET /awards/galapagos` — **no cap**;
`GET /wages` — **no cap**; `GET /sourcing/candidates` + `/log` (sourcing.read),
`DELETE /sourcing/candidates/{id}` (privacy.erase); `GET /compliance/adverse-impact` + `/export`
(compliance.read, **served by the compliance pool**); governance `GET /aedt/model-versions`,
`GET|POST /aedt/inventory`, `POST /compliance/retention/run`, `GET /audit` (audit.read),
`POST /candidates/{id}/notice` (candidate.write).
**Flags:**
- **Unauthenticated-but-tenant-scoped reads:** `GET /opportunities*`, `GET /awards*`, `GET /wages`
carry **no `RequireCapability`** (any authenticated role; RLS still scopes by org). Every other read
requires ≥ candidate.read.
- **`POST /aedt/inventory`** (a write) is gated on `CapComplianceRead` (a read capability) — minor
authz-granularity smell, not a bug.
- **Seven empty `internal/modules/` dirs** (candidates, slates, resumes, requirements, search,
dashboards, skills) expose no routes (functionality folded into `matching`/`commandcenter`).
- No orphaned/unreachable route found; every `*Routes` method is wired in `main.go`.
- eeo + adverse-impact routes return **503** if `MM_COMPLIANCE_DATABASE_URL` is unset; DB-backed routes
503 if `MM_DATABASE_URL` unset (`main.go:86-107`).
---
## 8 — FRONTEND **BUILT AND CONFIGURED**
- **Stack:** React 19 + react-dom (`package.json:21-22`), Vite 8 (`:35`, `@vitejs/plugin-react`),
TanStack Query 5, react-router-dom 7, **Tailwind v4 via `@tailwindcss/vite`**, oxlint (no ESLint),
TypeScript ~6 **strict** (`tsconfig.app.json:19` + noUnusedLocals/Params, verbatimModuleSyntax).
`vite.config.ts` dev server :5180 proxying `/api`+`/healthz` to `MM_BACKEND` (default :8090). Brand
tokens in `src/index.css:4-32` — shared navy/gold + per-tool `--color-screen` teal / `--color-slate-
accent` violet / gold umbrella. Browser resume parsing libs present: `pdfjs-dist 6.0.227` + `mammoth`.
- **Routes (`src/App.tsx`; all behind the session `Gate` except `/accept`):** **default after login =
index → `Navigate to="/command"`**. `/command` (CommandCenter), `/command/candidates` (Candidates
intelligence), `/compliance`, `/screen` (ScreeningQueue), `/screen/evaluate/:c/:r`
(CandidateEvaluation), `/requirements` (labeled "Opportunities"), `/slate`, `/settings`, `/pipeline`
(Placeholder "Roadmap v2.0"), `/dashboard` (legacy), and legacy redirects `/command-center→/command`,
`/candidates→/command/candidates`, `/evaluate/:c/:r`. Tool switcher in `components/AppShell.tsx` (TOOLS
array, 3 tools, each its own accent + nav).
- **Auth/backend calls:** `lib/AuthContext.tsx` calls `GET /api/auth/session` on mount (200→app, 403→
Login). Session is the `__Host-mm_session` cookie via `credentials:'include'`; **no X-Org-ID anywhere**.
CSRF: signed double-submit token from `GET /api/auth/csrf` + `__Host-mm_csrf`, de-duplicated in-flight,
refresh-once-on-403 (`lib/api.ts:42-105`). All calls hit relative `/api…`. The three tools each have a
thin namespace client (`tools/*/api.ts` → `/screenmatch|/slatematch|/missionmatch/*`); a shared `api`
object still serves Dashboard/Requirements/Compliance/sourcing/wages/awards/Mission-Check. Mission
Check posts only **extracted resume text** (browser parses the file via `lib/resume.ts`; bytes never
leave the tab).
- **Served identity:** `missionMATCH, Mission Workforce Intelligence` (`index.html:6`,
verified live); shell brand `missionMATCH™`, header chip "FIPS 140-3 • RLS isolated".
---
## 9 — EXISTING AI, EMBEDDINGS, AND RETRIEVAL (the substrate a new layer would build on)
- **Embedding generation — BUILT BUT NOT CONFIGURED.** Provider port `Embedder` in
`internal/platform/embeddings/embeddings.go` with: `deterministicEmbedder` (the **`fake`** default —
SHA-256+xorshift hash, offline), `httpEmbedder` (real OpenAI-compatible `/v1/embeddings`, Bearer / Azure
`api-key`), and `bedrock` = **"adapter not wired into core build"** (STUBBED). Config: provider
`MM_EMBED_PROVIDER` (default `fake`), `MM_EMBED_ENDPOINT`, `MM_EMBED_MODEL`, `MM_EMBED_API_KEY`,
`MM_EMBED_DIMENSION` (384). Production `Validate()` **rejects `fake`** — but the **live host runs
`MM_ENV=development`** so that guard is inactive (see §11). **Live-env check:** `MM_EMBED_PROVIDER` is
set, but **no `MM_EMBED_ENDPOINT`/`MODEL`/`API_KEY` exist in `api.env`** → no real embedding backend is
operational; the wired embedder is the deterministic/fake one. This matches the DB: `skills.embedding`
0/61 and `candidates.resume_embedding` 144/2787 populated.
- **Vector store / hybrid search — BUILT BUT NOT CONFIGURED.** Schema is complete: `vector(384)` columns
+ HNSW on candidates & skills, `search_tsv` GENERATED + GIN. A hybrid query `SearchCandidatesHybrid`
(`db/queries/candidates.sql:111-139`) fuses pgvector kNN + `ts_rank` via **Reciprocal Rank Fusion
(k=60)**, sqlc-generated — but it has **no live caller** (referenced only by the integration test); no
code sets `hnsw.ef_search`; `internal/modules/search/` is **empty**. The deterministic engine is what
runs.
- **LLM provider calls — MISSING.** No chat/completion/LLM call anywhere; no LLM SDK in `go.mod`. Every
"openai/azure" reference is the **embeddings** endpoint, not chat. `config.go:61` calls an LLM extractor
"a future addition."
- **Extraction — deterministic only.** `internal/platform/extraction/` port supports **only**
`deterministic` (rules/regex/gazetteer over the closed skill vocabulary); any other provider hard-errors
("not implemented in this build"). `MM_EXTRACT_*` config fields are reserved/unused. The CUI self-hosted-
vLLM extractor named in CLAUDE.md is **MISSING** (port exists, impl does not).
- **Retrieval/ranking beyond the engine — MISSING.** `matching/intelligence/` is pure presentation over
`MatchResult` (no ML). The only live consumer of the embeddings provider is the semantic skill matcher,
which on the `fake` embedder yields near-zero similarity for unrelated skills, so exact matching
dominates.
- **Libraries:** AI/vector deps limited to `pgvector/pgvector-go v0.4.0` (+ pgx adapter). No
OpenAI/Anthropic/Cohere/Bedrock/LangChain SDKs.
---
## 10 — COMPLIANCE AND GOVERNANCE
- **EEO storage + isolation — BUILT AND CONFIGURED; isolation PROVEN (four layers).**
`eeo.candidate_demographics` (gender, race_ethnicity, veteran_status, disability_status, consent_given)
lives in a **separate migration dir** `db/migrations_eeo/0001_eeo.sql` (outside `db/migrations/`, so
sqlc never models it), RLS forced, **no FK to public.candidates**. Grants: SELECT/INSERT to
`missionmatch_compliance` only (append-only); `missionmatch_app` gets **nothing**. It cannot reach
scoring, barred at: (1) physical migration separation; (2) DB privilege (app role zero grant — proven
live by `TestEEOPrivilegeBoundary`, app SELECT → 42501); (3) sqlc codegen (no eeo model in `dbgen`);
(4) the pure engine input type has no demographic field (`TestCandidateHasNoDemographicFields`). Grep:
zero references to `candidate_demographics`/`eeo` from matching/screenmatch/slatematch (one unrelated
comment).
- **Adverse-impact reporting — BUILT AND CONFIGURED (live-capable).** `internal/modules/eeo/service.go`
computes the **four-fifths/80% rule** in `decimal` on the **compliance pool**, two stages: **selection**
(demographics ⋈ `slate_members` decisions) and **screening** (demographics ⋈ `audit_logs WHERE
action='aedt.score.served'`, green = screened-in). Returns 503 if no compliance pool. **Live env has
`MM_COMPLIANCE_DATABASE_URL` set**, so this is operational on Bubbles.
- **Governance docs in `docs/`:** **present & substantial** — `STACK_DECISIONS.md` (~56KB; the FIPS/RLS/
pgvector/auth law), `MODEL_CARD.md` (scoring methodology, NIST AI RMF + ISO/IEC 42001, "what the model
never uses"), `openapi.yaml` (~23KB). **MISSING as standalone docs:** `DEPLOYMENT.md` (deploy guidance is
inline in STACK_DECISIONS + `infra/systemd/`), `COMPLIANCE.md`, `RESPONSIBLE_AI.md` (rules live in
CLAUDE.md + MODEL_CARD), **`BACKUP_DR.md`** (no RTO/RPO/runbook — though a backup cron exists, §1).
---
## 11 — CONFIG AND SECRETS (names only; never values)
- **Config loader:** `internal/platform/config/config.go` — `Load()` reads env + defaults + `Validate()`.
Production `Validate()` hard-requires FIPS, every auth secret, the Origin allowlist, Secure cookies, MFA,
and rejects the `fake` embedder and the dev header resolver. Secret keys decode via explicit
`base64:`/`hex:`/`raw:` prefix; a decode failure is fatal (no silent fallback).
- **Where env lives:** `.env.example` (placeholders, gitignored real files). On Bubbles, root-owned 0600
`/etc/missionmatch/api.env` (API) and `/etc/missionmatch/sam-sync.env` (sync), loaded via systemd
`EnvironmentFile=`.
- **LIVE RUNNING ENVIRONMENT (verified key NAMES present in `api.env`, values redacted):** `MM_ENV`
(**= development**, verified earlier — so production guards are NOT enforced live), `MM_HTTP_ADDR`
(=127.0.0.1:8094), `MM_CLOUD`, `MM_REQUIRE_FIPS` (set), `MM_COOKIE_SECURE` (=true), `MM_MFA_REQUIRED`
(**= false** — MFA disabled for the pilot), `MM_ALLOWED_ORIGINS`, `MM_TRUSTED_PROXY_HEADER`,
`MM_DATABASE_URL`, `MM_MIGRATOR_DATABASE_URL`, `MM_COMPLIANCE_DATABASE_URL` (set), `MM_CREDENTIAL_PEPPER`,
`MM_TOTP_KEK`, `MM_CSRF_KEY`, `MM_RECOVERY_LOOKUP_KEY`, `MM_EMBED_PROVIDER` (set), `GODEBUG`.
**Absent from `api.env`:** `MM_EMBED_ENDPOINT`/`MODEL`/`API_KEY`/`DIMENSION`, all `MM_EXTRACT_*`,
`MM_CREDENTIAL_PEPPER_PREVIOUS`/`VERSION`, `MM_TOTP_KEK_PREVIOUS`/`VERSION`, `MM_TOTP_ISSUER`,
`MM_DECISION_RETENTION_DAYS`, session-timeout overrides (defaults apply).
- **Secret/key inventory (NAME → purpose → where referenced; presence "live" = seen in the running env):**
| Name | Purpose | Referenced | Live env |
|---|---|---|---|
| `MM_DATABASE_URL` | app-role (NOBYPASSRLS) DSN | config.go:131 | **present** |
| `MM_MIGRATOR_DATABASE_URL` | migrator (BYPASSRLS) DSN | config.go:132 | present (api+sync) |
| `MM_COMPLIANCE_DATABASE_URL` | compliance-role DSN (eeo only) | config.go:133 | **present** |
| `MM_CREDENTIAL_PEPPER` (+ `_VERSION`/`_PREVIOUS`) | pre-PBKDF2 HMAC pepper, rotatable | config.go:154-156 | base present; rotation vars absent |
| `MM_TOTP_KEK` (+ `_VERSION`/`_PREVIOUS`) | AES-256 KEK for TOTP secrets | config.go:157-159 | base present; rotation vars absent |
| `MM_CSRF_KEY` | CSRF token HMAC | config.go:160 | present |
| `MM_RECOVERY_LOOKUP_KEY` | recovery-code lookup-tag HMAC | config.go:161 | present |
| `MM_EMBED_API_KEY` / `_ENDPOINT` / `_MODEL` / `_PROVIDER` / `_DIMENSION` | embedding provider | config.go:137-141 | only `_PROVIDER` present → no real backend |
| `MM_EXTRACT_API_KEY` / `_ENDPOINT` / `_MODEL` / `_PROVIDER` | extraction provider (reserved) | config.go:144-147 | absent (deterministic) |
| `SAM_API_KEY` (+ `SAM_API_BASE`/`SAM_AWARDS_BASE`) | SAM opps + contract awards | sam-sync/opp-sync | present (sync env) |
| `BLS_API_KEY` (+ `BLS_API_BASE`) | BLS OEWS | rates-sync/main.go:25 | present (sync env) |
| `PDL_API_KEY` (+ `PDL_API_BASE`) | People Data Labs | import-candidates/main.go:39 | present (sync env) |
| `USASPENDING_BASE`, `MM_FORECAST_CSV`, `MM_OPP_RAW_DIR` | non-secret feed config | opp/awards-sync | base URLs default; `MM_FORECAST_CSV` UNKNOWN |
| `DATA_GOV_API_KEY`, `KAGGLE_USERNAME`, `KAGGLE_KEY`, `HF_TOKEN` | declared in env/.env.example, **not referenced in code** | — | present in sync env but **dead** |
Non-secret config: `MM_ENV`, `MM_HTTP_ADDR`, `MM_CLOUD`, `MM_REQUIRE_FIPS`, `MM_ALLOW_DEV_HEADER_AUTH`,
`MM_ALLOWED_ORIGINS`, `MM_TRUSTED_PROXY_HEADER`, `MM_COOKIE_SECURE`, `MM_MFA_REQUIRED`,
`MM_*_SECONDS` timeouts, `MM_DECISION_RETENTION_DAYS`, `MM_DEMO_ORG_ID`. No secret value was read.
---
## 12 — TESTS AND CI
- **Matching engine unit tests — BUILT (strong).** `engine/engine_test.go` (421 lines): perfect-match,
full clearance hierarchy (higher-satisfies-lower, expired/unknown→0, poly gate), skills exact+semantic
at the 0.80 boundary, certs/customer/experience/location, **band boundaries 74.99/75/89.99/90**,
**weights sum to 1**, determinism/order-independence, and an ID-only-difference no-side-channel test.
`engine/frozen_test.go` pins exact weights/bands/threshold/ModelVersion. `engine/golden_test.go`
snapshots Green/Yellow/Red sub-scores + trace-re-sums-to-total. `engine/codes_test.go` guards that
engine enum codes equal the SQL CHECK sets.
- **EEO no-field proof:** `TestCandidateHasNoDemographicFields` (`engine_test.go:361-380`) reflects over
`engine.Candidate`, tokenizes each field name, and fails on an 18-prefix banned denylist (race, gender,
sex, ethnic, religio, disab, national, origin, marital, dob, birth, pregnan, veteran, orientation,
color, creed, age).
- **Live integration suite — BUILT (`internal/integration/`, `//go:build integration`, ~24 funcs).**
Proves: RLS cross-tenant read isolation; unset-GUC default-deny (0 rows); composite-FK cross-tenant
injection block (23503); audit append-only (app 42501, migrator P0001); EEO privilege boundary; slate
numeric round-trip; hybrid pgvector+RRF search executes; cross-tool scoring **byte-identical** 75.00;
evaluation upsert-latest; slate list/detail/readiness/notes/audited-export; who-can-support reads stored
(no rescore); 5-stage data-flow read-not-recompute + cross-tool audit carries no PII; auth
provision/MFA/session lifecycle + app-cannot-provision + app-cannot-read-5-secret-tables; EEO four-
fifths over the compliance pool; clearance provenance; command-center adopt; mission-check ephemeral;
opportunity records; revenue-at-risk; sourcing dedup. **All 31 pass live on M4** (verified this audit).
- **Frontend tests — MISSING.** No `*.test/*.spec`, no vitest/jest/playwright/testing-library. Zero
coverage.
- **CI — MISSING (no automated pipeline).** No `.github/workflows`, no GitLab/Jenkins/etc. Gates exist
only as manual `make` targets: `test` (unit), `test-integration` (not in `ci`), `ci` (=test + ci-gates +
sbom), `ci-gates` (`verify-backdoor` — asserts no `X-Org-ID` resolver in the release binary;
`verify-fips` — asserts the GOFIPS140 stamp), `sbom` (CycloneDX). The Makefile comments describe an
intended-but-absent pipeline.
---
## 13 — GAPS AND OPEN ITEMS
The repo is clean of accidental `TODO`/`FIXME`/`XXX`/`HACK` in production paths; every gap below is an
intentional, commented pilot deferral.
**Verified suspected gaps:**
- **(a) Resume parsing — BUILT (split), `resumes` module empty.** `internal/modules/resumes/` has **zero
files** (vestigial). Real extraction is server-side `internal/platform/extraction/deterministic.go`
(354 lines: clearance/years/USPS-location/gazetteer skills+certs); the browser does file→text only
(`frontend/src/lib/resume.ts`, pdfjs+mammoth) and posts text. A **model-backed/CUI extractor is
STUBBED** (hard-errors on any non-deterministic provider).
- **(b) Manual invite delivery — confirmed.** No SMTP/mailer anywhere; invite + recovery links are
returned in API responses for manual hand-off (MISSING transport; **needed before non-pilot rollout**).
- **(c) Feeds present but not configured:** GovWin IQ / GovTribe / GSA eBuy = **STUBBED**, never registered
(need a license/credential, not a flag). GSA forecast CSV = **BUILT BUT NOT CONFIGURED** (off unless
`MM_FORECAST_CSV`). PDL live search = **BUILT BUT NOT CONFIGURED** (CLI-only, key-gated, no API trigger).
- **(d) Placeholder UI:** only `frontend/src/screens/Placeholder.tsx`, used solely for `/pipeline`
("Talent Pipeline · Roadmap v2.0").
**Other:**
- **Embeddings/semantic = scaffolding only:** default `MM_EMBED_PROVIDER=fake`; live env has no
endpoint/model/key → semantic skill matching runs on hash vectors; `skills.embedding` 0/61 and
`candidates.resume_embedding` 144/2787 populated; HNSW + RRF hybrid SQL exist but **nothing live calls
them** (`search` module empty). **Bedrock embedding adapter STUBBED** (blocks an AWS GovCloud deploy
as-is).
- **Seven empty `internal/modules/` dirs:** resumes, candidates, dashboards, requirements, search, skills,
slates (dead scaffolding).
- **Public forgot-password** deferred to sub-phase 2.9 (MISSING).
- **No skill adjacency/synonym graph** (embeddings are the only intended semantic layer, and they are
unpopulated).
- **No automated CI; zero frontend tests** (§12).
- **No `BACKUP_DR.md`/RTO-RPO doc** despite a working `mm-pg-backup` cron (§1, §10).
- **Live config posture flags:** the deployed host runs **`MM_ENV=development`** (production validations —
reject-fake-embedder, require-MFA, require-secrets — are NOT enforced) and **`MM_MFA_REQUIRED=false`**
(MFA off for the pilot). These are pilot settings, not code gaps, but they mean the live deployment is
not in its hardened production posture.
- **Command Center org-readiness is a second (SQL) gating path** distinct from the engine (§3) — to keep
in sync.
- Minor: `GET /opportunities`/`/awards`/`/wages` have no capability gate; `POST /aedt/inventory` (a write)
is gated on a read capability; `mm-sam-sync.timer` showed no next-elapse at audit (verify it fires).
---
## STATE OF THE BUILD (plain prose)
**What is solid.** The deterministic core is real, well-bounded, and proven: one pure
`engine.ScoreCandidate` (frozen weights/bands, decimal math, no demographic field) drives every
per-candidate number across screen, slate, ranked, and intelligence views, locked by frozen/golden/
boundary unit tests and a cross-tool byte-identical integration test. The data layer is production-grade:
PostgreSQL 16 + pgvector, RLS enabled-and-forced on every tenant table with a non-bypass app role, an
append-only audit table, a strictly segregated EEO schema the app role provably cannot reach (four
independent barriers), and FIPS-validated in-house crypto (PBKDF2-600k, AES-GCM TOTP, opaque hashed
sessions). The federal data feeds (SAM opps, SAM/USASpending awards + Galapagos UEI past-performance, BLS
wages, Opportunity Intelligence) are live HTTP and scheduled, with real loaded data (1.4k opps, ~2.5k
opportunity records, 525 awards). The three-tool split (screenMATCH/slateMATCH/missionMATCH) is deployed
and serving on one origin with a clean redirect, a tested 24-function live integration suite, and a daily
DB backup cron.
**What is fragile.** The live deployment is not in production posture: it runs `MM_ENV=development` and
`MM_MFA_REQUIRED=false`, so the very production guards the code enforces (reject the fake embedder, require
MFA, require all secrets) are dormant. Invite and recovery links have no delivery transport (returned in
API responses) — fine for a piloted handoff, blocking for real onboarding. There is no automated CI and
zero frontend tests; all gates are manual `make` targets. A couple of read endpoints lack capability gates,
and the Command Center's org-level readiness is a second SQL gating path that mirrors rather than reuses the
engine.
**What is not there yet (and matters most for an AI layer).** The semantic/AI substrate is scaffolding,
not a working system: the embedding columns and HNSW indexes exist but are essentially empty
(`skills.embedding` 0/61, `candidates.resume_embedding` 144/2787); the embedding provider defaults to a
deterministic `fake` and the live host has no real endpoint/model/key wired; the hybrid pgvector+tsvector+
RRF retrieval SQL is generated but **no live code path calls it** and the `search` module is empty; there
are **no LLM calls anywhere** and no LLM SDK; extraction is deterministic-only with the CUI/vLLM extractor
unbuilt; there is no curated skill adjacency graph. In short: the database, retrieval SQL, provider ports,
and brand/route shell are ready to host an AI layer, but embeddings, semantic search, and any
LLM/generative capability are unbuilt or unconfigured — that is the greenfield the new layer will occupy.
*End of inventory. No changes were made.*