Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/missionmatch / docs/CLAUDE_CODE_BUILD_DIRECTIVE.md |
| Date | Not specified in source |
| Repository | Janady13/missionmatch |
| Revision | 720d47fae476b0d38477ae4401697d372b1ea2bc |
| Branch | feature/opportunity-to-outcome |
| Path or archive member | docs/CLAUDE_CODE_BUILD_DIRECTIVE.md |
| Original SHA-256 | 99b67fd52e6728ebf8fcaf8853474ea7172aad5b7cfd954f76a224204b3ba890 |
| Git blob SHA | a3087ba3fc9935d5b9de04ecf00276a21a271a48 |
| Reading-copy SHA-256 | 99b67fd52e6728ebf8fcaf8853474ea7172aad5b7cfd954f76a224204b3ba890 |
Recovered source transcript
Download complete source text ↓1,000 lines · 55.2 KB
# CLAUDE CODE BUILD DIRECTIVE: OPPORTUNITY TO OUTCOME
missionMATCH complete update. Generated 27 July 2026. This is the single
source of truth for the build. It absorbs and supersedes
INTELLIGENCE_EXPANSION_SPEC.md, whose content now lives in Phases 0, 2, and 4.
Move that file to docs/archive in Phase 0 so exactly one work order exists.
The mission: an opportunity may enter through the SAM.gov API, manual entry,
uploaded solicitation documents, a customer or teaming partner scope, a
spreadsheet import, or a future agency forecast feed. Every intake path
converges into one canonical workflow:
Opportunity -> Source Documents -> Scope Extraction -> Human Review ->
Approved Requirements -> Staffing Slates -> Proposal Workspace ->
Submission -> Outcome
This UPDATES missionMATCH. Nothing is built beside it.
--------------------------------------------------------------------------------
0. SESSION PROTOCOL, HOW CLAUDE CODE RUNS THIS BUILD
--------------------------------------------------------------------------------
1. Read order at the start of every session: CLAUDE.md, this directive,
docs/BUILD_STATE.md, then only the files needed for the current item.
2. Phase lock. Work exclusively on the lowest incomplete phase. Never start
phase N+1 until every item in the phase N gate is verified and recorded.
3. Ledger. docs/BUILD_STATE.md is append only in practice: one line per
completed gate item recording phase, item number, verification evidence
(the command run, the rows observed, the screen checked), commit hash,
and date. Only verified items enter. Aspirations never enter.
4. Commit convention: "phaseN itemM: short description". One logical change
per commit.
5. Frozen and untouchable: internal/modules/matching/engine (the candidate
scoring engine), db/migrations_eeo, every existing migration file,
generated sqlc output (regenerate, never hand edit), and the append only
audit semantics.
6. Extend, never build beside (Section 1). If a change appears to require a
parallel structure, stop, record the conflict in BUILD_STATE, and wait
for Joseph rather than forking.
7. Verification method, unchanged from how this system was built: reproduce
the defect or behavior against the live database first, implement, verify
against the same database, verify end to end through the live
authenticated API, then look at the rendered screen. Concurrency is
proven by racing it, not by reasoning about it.
8. Inherited non negotiable rules: No Python anywhere, Go backend,
TypeScript frontend, bash scripts. No dashes in written output, commas or
rewrite, hyphens only inside code identifiers, protocol headers such as
User-Agent, and official designations. Every API key stays on the
server, root owned 0600, never in the repo, never in the browser bundle.
Clearance is clearance intelligence, never verified. Fail closed
everywhere.
9. Environment rule, AMENDMENT 1, reinstated 28 July 2026 by the consolidated
amendments (docs/DIRECTIVE_AMENDMENTS_CONSOLIDATED.md). This supersedes the
27 July decision to build directly against production, which is now void for
all work from 28 July forward.
Every migration, every live database verification, and every end to end check
through Phase 5 runs against missionmatch_staging. The production tenant
database is touched only at Gate 6, and only after the backup and restore test
passes. If any task appears to require production before Gate 6, stop and
record the conflict in BUILD_STATE instead of proceeding.
RESOLVED LOCATION. The amendment names staging as missionmatch_staging on
megamind-agi port 5433. Verified on 28 July: that database exists but is an
EMPTY SHELL, 0 organizations, 0 candidates, and its goose table is not even
readable by the app role. The working staging is missionmatch_staging on M4
port 5432: 76 tables, 2766 organizations, 8872 candidates, and it is the
database the live mm-api-staging service on 127.0.0.1:8097 actually serves.
Gate verification requires an end to end check through a live authenticated
API and a rendered screen, which only the M4 database supports. Per Section 0
item 5 of the amendments, the smallest change satisfying both is to honor the
amendment's database NAME and its staging only rule while using the host where
that database actually functions. Staging therefore means
missionmatch_staging on 100.94.8.94 port 5432.
Safety rails on every schema change, unchanged: assert the connected database
name before any write, pg_dump the target before a migration batch, keep
migrations additive and transactional, and keep down migrations refusing to
destroy application authored rows.
--------------------------------------------------------------------------------
1. EXTEND, NEVER BUILD BESIDE (THE ANTI DRIFT MAP)
--------------------------------------------------------------------------------
EXTEND these existing components. Do not duplicate their function anywhere:
cmd/sam-sync upgraded in Section 4, never replaced
cmd/opp-sync unified opportunity ingestion
cmd/awards-sync gains task order depth in Phase 4
internal/modules/opportunities gains the tenant tracked layer
internal/modules/opportunity_intelligence
internal/modules/awards
internal/modules/bidoutcomes outcomes write through here
internal/modules/alerts notifications ride here, with alert-sweep
internal/modules/slatematch the proposal workspace lives here
internal/modules/commandcenter readiness tiles read from here
internal/modules/recruitmatch completed in Phase 5
internal/modules/governance evidence pack export lands here
internal/platform/docextract and internal/platform/extraction
internal/platform/storage document bytes, keyed by hash
internal/platform/secrets the only home for keys
internal/platform/inference circuit breaker, bounded retries
internal/platform/scrub extended with egress payload classes
internal/platform/audit OnTx coverage for every material action
internal/platform/tenant, tenantpool, database, config, csvsafe, obs
frontend/src/tools/slatematch new screens mount here
frontend/src/App.tsx routes and ToolGuard, existing pattern
db/migrations numbering continues from the current max
NEW things allowed, and this list is exhaustive:
Tables named in Section 3.
Binaries named in Section 8.
Package internal/modules/proposals with pure subpackage engine.
Frontend screens named in Section 6.
docs/BUILD_STATE.md and docs/archive/.
FORBIDDEN, full stop:
A new repository, a new API binary, or a second candidate scoring engine.
A fifth product entitlement.
Renaming or restructuring existing tables.
Schema changes outside numbered migrations.
Ingestion paths that edit approved requirements or tenant authored data.
Secrets anywhere except the secrets store.
NL to SQL, model authored SQL, or model authored parameters.
Web scraping of candidate data of any kind.
--------------------------------------------------------------------------------
2. DECISIONS RECORDED, DO NOT REOPEN
--------------------------------------------------------------------------------
1. The proposal workspace ships inside the slatematch entitlement. A
proposal is the continuation of a slate. Routes mount in the slatematch
route group with flat aliases registered per the existing pattern.
Record this in STACK_DECISIONS.
2. Tenant tracked opportunities live inside internal/modules/opportunities
as a tenant owned layer above the shared public cache. No new capture
module.
3. The readiness engine is a new pure package
internal/modules/proposals/engine: shopspring/decimal, no database, no
I/O, a versioned READINESS_ALGO_VERSION constant. It never touches or
resembles the candidate scoring engine.
4. Proposal outcomes write through the existing bidoutcomes module, closing
the loop that AIOE already expects.
5. Deadline and amendment notifications use the existing alerts module.
6. External inference: OpenAI becomes a third gateway backend under the
rules in Section 5.3. The key exposed in chat on 27 July 2026 is revoked
and replaced before the enabled flag ever turns true.
7. Capabilities: map new actions onto the existing 19 first. If none fits,
add exactly these, at these role thresholds, and update the exhaustively
tested capability matrix: opportunity.manage at recruiter and above,
scope.review at recruiter and above, requirement.approve at manager and
above, proposal.manage at recruiter and above, proposal.submit at
manager and above. Superuser holds all by rule, never by matrix row.
8. Migration numbers continue from the current maximum in db/migrations.
Never renumber, never reuse, never touch the EEO set.
9. ingest_manifest lives wherever the public opportunity cache lives today.
Inspect the sam-sync write target and follow that exact pattern: if the
cache is per tenant, the manifest carries organization_id like every
tenant table; if the cache is shared reference data, the manifest is
shared reference data. Tenant rows reference manifest rows by id.
--------------------------------------------------------------------------------
3. DATA MODEL
--------------------------------------------------------------------------------
Invariants for every new tenant table, no exceptions: organization_id,
forced row level security, a composite index leading with organization_id,
composite foreign keys, ON DELETE SET NULL only in the column list form
(migration 0053 exists because the bare form is a trap), audit coverage
through OnTx, and down migrations that refuse with a count rather than
destroy application authored rows.
3.1 tracked_opportunities
organization_id, id, public_opportunity_id, source_type,
source_record_id, sam_notice_id, title, solicitation_number,
agency_name, office_name, notice_type, set_aside_code, naics_code,
psc_code, place_of_performance, posted_at, response_deadline,
estimated_value_min, estimated_value_max, ceiling_value,
period_of_performance_start, period_of_performance_end, incumbent_name,
prime_or_sub, capture_owner_user_id, status, source_url,
source_manifest_id, created_by_user_id, created_at, updated_at
source_type enum: sam_gov, agency_forecast, customer, partner, internal,
spreadsheet, manual.
status enum: identified, qualifying, qualified, pursuing, no_bid,
submitted, won, lost, cancelled, archived.
Unique partial index on (organization_id, public_opportunity_id) where
public_opportunity_id is not null, so a tenant cannot track one public
notice twice.
3.2 opportunity_versions, immutable snapshots
organization_id, id, opportunity_id, version_number, amendment_number,
source_version_label, source_payload_hash, normalized_payload,
fetched_at, effective_at, supersedes_version_id, ingest_manifest_id,
is_current, created_by_user_id
Unique on (organization_id, opportunity_id, version_number). is_current
flips inside the same transaction that inserts the successor. Rows are
never updated after insert except that flag, enforce with a trigger in
the audit style.
3.3 opportunity_documents
organization_id, id, opportunity_id, opportunity_version_id,
document_type, file_name, media_type, storage_key, size_bytes, sha256,
source_url, source_type, uploaded_by_user_id, extraction_status,
extraction_error_code, supersedes_document_id, is_current, created_at
document_type enum: solicitation, amendment, sow, pws, soo,
labor_categories, pricing_template, evaluation_criteria, security,
q_and_a, past_performance, subcontract_scope, customer_scope,
attachment, other.
extraction_status enum: pending, running, done, failed. Failed is
retryable. Duplicate detection by sha256 within the opportunity.
Bytes go through internal/platform/storage; the malware scan
integration point sits between upload and extraction_status pending.
3.4 opportunity_document_chunks, exact extraction provenance
organization_id, id, document_id, page_number, section_label,
text_content, content_hash, extraction_method, confidence, created_at
3.5 opportunity_scope_items
organization_id, id, opportunity_id, scope_type, title, description,
source_document_id, source_page_number, source_section_label,
source_quote_hash, extraction_method, extraction_confidence,
review_status, reviewed_by_user_id, reviewed_at, parent_scope_item_id,
display_order, created_at, updated_at
scope_type enum: objective, task, deliverable, labor_category,
staffing_quantity, skill, certification, clearance,
customer_experience, location, travel, schedule, security_control,
reporting, transition, key_personnel, pricing_instruction,
evaluation_factor, compliance_clause, assumption, dependency, risk,
other.
review_status enum: extracted, accepted, edited, rejected, merged,
manual.
3.6 requirement_drafts, the safety boundary before live scoring
organization_id, id, opportunity_id, source_scope_item_id,
labor_category, quantity, clearance_level, polygraph, compartments,
required_skills, mandatory_skill_flags, required_certifications,
minimum_years_experience, required_customer_experience, work_location,
remote_allowed, travel_percent, availability_date, employment_type,
pay_band_min, pay_band_max, bill_rate_min, bill_rate_max,
derivation_method, derivation_confidence, review_status,
reviewed_by_user_id, reviewed_at, approved_requirement_id, created_at,
updated_at
review_status enum: draft, in_review, approved, rejected. Promotion to
the existing requirement table happens in one audited transaction that
takes FOR UPDATE on the draft row, so racing approvals elect exactly one
winner, matching the nine existing lock patterns.
3.7 Proposal tables
proposals: organization_id, id, opportunity_id, title, stage, status,
proposal_owner_user_id, capture_owner_user_id, customer_deadline,
internal_deadline, submitted_at, outcome_recorded_at, created_at,
updated_at.
proposal_sections and proposal_section_versions: section_code, title,
owner_user_id, required, page_or_word_limit, due_at, draft_status,
review_status, approved_by_user_id, version_number, content
reference.
proposal_compliance_items: source requirement text, source_document_id,
page or section, response_location, owner_user_id, due_at, status,
evidence reference, reviewer_user_id.
proposal_milestones: kind enum covering question_deadline, pink_team,
red_team, gold_team, final_review, upload, submission, plus due_at
and completed_at.
proposal_risks: likelihood, impact, owner_user_id, mitigation, trigger,
status, related_scope_item_id.
proposal_team_members, proposal_staffing_links (requirement_id,
slate_id, required_quantity, approved_count), proposal_attachments,
proposal_readiness_snapshots (overall decimal, one decimal column
per component, blockers, drivers, algorithm_version,
calculated_at).
3.8 ingest_manifest
source, url, license, fetched_at, record_count, content_hash, and
organization_id if the Section 2 item 9 inspection says per tenant.
Every synced row and every knowledge card traces to a manifest row.
--------------------------------------------------------------------------------
4. SAM.GOV INGESTION UPGRADE, INSIDE cmd/sam-sync
--------------------------------------------------------------------------------
The complete flow, in order:
1. Retrieve official notice metadata.
2. Write or refresh the shared public opportunity cache.
3. Record provenance in ingest_manifest.
4. Retrieve the full description where permitted. The description field
returns a URL that costs a second authenticated call, so the call budget
is double the notice count. Opportunity pages cap at one thousand
records. Personal keys allow roughly ten calls a day, role backed
roughly one thousand, federal system accounts roughly ten thousand.
Explicit pagination and caching, always.
5. Retrieve attachment metadata.
6. Compare the current source hash to the last stored source hash.
7. On change, create an immutable opportunity_versions row for every tenant
tracking the notice.
8. Queue approved public attachments for cmd/sam-attachment-sync.
9. Extract document text through the existing extraction packages.
10. Generate draft scope items, Section 5 rules.
11. Notify tracking tenants through the alerts module when an amendment
lands or a deadline changes.
12. Never touch tenant authored data. Refresh writes only to the public
cache and to versions. A test asserts that every tenant owned column on
tracked_opportunities survives a refresh byte for byte.
Amendment handling, always: a new amendment creates a new version, retains
the original, diffs deadlines, labor categories, quantities, security
requirements, and evaluation criteria, flags affected approved
requirements, and requires a named human to accept operational changes.
Nothing silently rewrites an approved requirement.
--------------------------------------------------------------------------------
5. EXTRACTION, NORMALIZATION, AND THE EXTERNAL BOUNDARY
--------------------------------------------------------------------------------
5.1 Deterministic extraction first, before any model call, in the existing
extraction packages: headings, numbered clauses, tables, dates, deadlines,
dollar values, labor categories, quantities, clearance terms,
certifications, locations, travel percentages, period of performance,
deliverables, evaluation factors. Output lands in
opportunity_document_chunks with provenance.
5.2 Model normalization second, through the existing inference router:
1. The model returns typed JSON matching a declared schema.
2. Every item carries document provenance: document id, page, section,
quote hash.
3. Uncited output is rejected.
4. Invalid enum values, dates, lengths, and quantities are rejected.
5. Model output creates scope item drafts and requirement drafts only.
6. Only a named, authorized human promotes a draft into anything the
scoring engine can see. Extract, draft, review, approve, promote.
5.3 The external boundary. megamind-gateway gains a third backend:
backend openai, endpoint https://api.openai.com/v1/chat/completions,
bearer from the secrets resolver, model as a config value validated non
empty at boot, enabled flag default false. Enabled without the secret
fails the boot closed.
internal/platform/scrub gains egress payload classes. Only these may leave
the machine: PUBLIC_KNOWLEDGE, PUBLIC_DATA_SUMMARY, ONTOLOGY_SUGGEST, and
REQUIREMENT_NORMALIZE. Public SAM.gov notice text may use the external
backend under REQUIREMENT_NORMALIZE. Tenant uploaded documents and every
private scope stay on the local plane, always. Never external, enforced by
the gate and by tests: candidate rows, resume text, names, emails, phone
numbers, tenant identifiers, anything from the eeo schema, RouteRAG and
RouteGraph over tenant data, scoring inputs, scoring outputs, and slate
contents. The gate fails closed to the local plane. Every external call
audits backend name, payload class, byte count, and content hash, never
content. The verifier gates all returned prose exactly as it does for local
models. Record the backend in the model registry and AEDT inventory as
outside the FIPS boundary, restricted to non regulated tasks.
--------------------------------------------------------------------------------
6. FRONTEND, INSIDE frontend/src/tools/slatematch
--------------------------------------------------------------------------------
Intake screens:
1. Create opportunity: title, source type, agency or customer,
solicitation or reference number, response deadline, prime or
subcontract, capture owner, status. Minimum eight fields, nothing else
required.
2. Opportunity intelligence: NAICS, PSC, set aside, place of performance,
estimated value, period of performance, incumbent, source URL, notes,
known partners. All optional.
3. Upload documents: drag and drop plus file picker, required document
classification, file type allowlist, file size limit, sha256 duplicate
detection, upload progress, extraction status, retryable extraction
errors, malware scan hook.
4. Review scope: for every extracted item show the text, document name,
page or section, extraction method, and confidence, with accept, edit
and accept, reject, split, merge, and add manually.
5. Build requirements: generate drafts from accepted staffing scope, show
missing required fields, allow manual completion, require named
approval, promote, preserve lineage.
Proposal workspace tabs: Overview (opportunity summary, customer deadline,
internal deadline, owners, stage, readiness, hard blockers, scope changes,
staffing coverage, compliance completion, section completion, review
milestones, risks, recent activity), Scope, Requirements, Compliance
Matrix (source requirement, document, page or section, response location,
owner, due date, status, evidence, reviewer), Sections (code, title, owner,
required, page or word limit, due date, draft status, review status,
approval, version), Staffing (requirement, required quantity, linked slate,
approved candidates, remaining gap, last scoring date, stale score
warning), Milestones, Risks, Attachments, and Activity fed from the append
only audit trail. Every new tile is clickable through to its filtered list.
Reuse AppShell, ToolGuard, lib/queries, and the existing screen patterns.
--------------------------------------------------------------------------------
7. READINESS ENGINE, internal/modules/proposals/engine
--------------------------------------------------------------------------------
Pure Go, shopspring/decimal, no database, no I/O, injected inputs, a
READINESS_ALGO_VERSION constant. Weights:
Staffing coverage 0.30
Compliance matrix 0.25
Required sections 0.20
Review and approval 0.10
Schedule health 0.10
Risk posture 0.05
Hard blockers cap readiness regardless of total, mirroring the band cap
philosophy of the candidate engine without sharing a line of code with it:
customer deadline passed, required compliance item open, required section
missing, required staffing quantity uncovered, final approval missing,
unreviewed amendment affecting scope, required attachment missing,
submission validation failed. Every result returns overall score, component
scores, driver explanations, hard blockers, calculation timestamp, and
algorithm version. Submission is blocked server side while any hard blocker
exists. Snapshots persist to proposal_readiness_snapshots. This makes the
command center distinction concrete: low proposal readiness can be a
documentation problem, not a people problem.
--------------------------------------------------------------------------------
8. BINARIES, EXHAUSTIVE LIST OF NEW COMMANDS
--------------------------------------------------------------------------------
cmd/sam-attachment-sync download queued public attachments
cmd/opportunity-extract run extraction over pending documents
cmd/opportunity-scope-normalize model normalization pass, Section 5
cmd/opportunity-amendment-diff version diffing and impact flags
cmd/proposal-readiness-snapshot scheduled readiness persistence
cmd/proposal-deadline-alerts deadline sweeps into alerts
cmd/stale-requirement-scan flag requirements older than policy
cmd/oews-metro-sync Phase 4, BLS OEWS at MSA level, API v2
cmd/sca-wd-sync Phase 4, SAM.gov wage determinations
cmd/usajobs-sync Phase 4, USAJOBS with the three headers
Authorization-Key, User-Agent set to the
registered email, Host data.usajobs.gov
Task order depth extends cmd/awards-sync using USASpending File D1 through
api.usaspending.gov bulk_download. Every binary follows the sam-sync
pattern: idempotent, bounded, observable, audited, provenance aware, safe
to rerun.
--------------------------------------------------------------------------------
9. API ROUTES
--------------------------------------------------------------------------------
POST /api/opportunities
GET /api/opportunities/{id}
PATCH /api/opportunities/{id}
POST /api/opportunities/{id}/documents
GET /api/opportunities/{id}/documents
POST /api/opportunities/{id}/extract
GET /api/opportunities/{id}/scope
POST /api/opportunities/{id}/scope/items
PATCH /api/opportunities/{id}/scope/items/{item_id}
POST /api/opportunities/{id}/requirement-drafts
POST /api/requirement-drafts/{id}/approve
POST /api/requirement-drafts/{id}/reject
POST /api/proposals
GET /api/proposals/{id}
PATCH /api/proposals/{id}
GET /api/proposals/{id}/readiness
POST /api/proposals/{id}/readiness/snapshot
POST /api/proposals/{id}/submit
POST /api/proposals/{id}/outcome
Plus normal CRUD for proposal sections, compliance items, milestones,
risks, attachments, and staffing links. Every route follows the existing
change discipline: thin handler, service layer logic, RunInTenantTx,
capability check, tool entitlement gate answering 404 never 403, the same
gate on flat alias paths, and an endpoint catalogue update.
--------------------------------------------------------------------------------
10. PHASES AND GATES
--------------------------------------------------------------------------------
PHASE 0: SECURITY AND PREFLIGHT
1. HUMAN STEP, Joseph and Tom: revoke the exposed OpenAI key in the OpenAI
dashboard. Claude Code verifies no key material exists anywhere in the
repo or in any file it authors.
2. HUMAN STEP: create a fresh project scoped key under a Mission Workforce
Technologies organization with a monthly usage cap and budget alerts.
It enters internal/platform/secrets as openai_api_key, root owned 0600
on M4, and nowhere else.
3. HUMAN STEP: begin the SAM.gov system account application. Approval can
take weeks, so it starts now even though Phase 4 consumes it.
4. Implement the scrub gate egress classes and external call audit rows
with the backend flag still false.
5. Move INTELLIGENCE_EXPANSION_SPEC.md to docs/archive.
6. Create docs/BUILD_STATE.md with the ledger format from Section 0.
GATE 0: gateway with openai enabled and no secret refuses to boot, proven
against the live gateway. Scrub tests pass. BUILD_STATE exists. The archive
move is committed.
PHASE 1: OPPORTUNITY FOUNDATIONS
1. Migrations for tracked_opportunities, opportunity_versions,
opportunity_documents, opportunity_document_chunks, and ingest_manifest
per Section 2 item 9.
2. Intake screens 1 through 3 with create, intelligence, and upload flows.
3. sam-sync upgrade steps 1 through 8 of Section 4, including hash
comparison, versioning, and attachment queuing.
4. cmd/sam-attachment-sync.
5. Amendment detection wiring into the alerts module.
GATE 1: a SAM.gov opportunity ingests twice without duplication. A manual
opportunity is created with only the eight minimum fields and supports
every downstream capability a SAM.gov opportunity supports. Versions are
immutable, proven by attempted update failing. Tenant authored fields
survive a source refresh byte for byte, proven by test. Uploads are
validated, hashed, versioned, and traceable. All touched screens render
with zero 5xx and zero page errors.
PHASE 2: SCOPE AND REQUIREMENTS
1. Deterministic extraction into chunks, Section 5.1.
2. Local model normalization producing cited scope item drafts.
3. External REQUIREMENT_NORMALIZE for public SAM.gov notice text only,
behind the scrub gate. Backend may be enabled only if Phase 0 items 1
and 2 are recorded in BUILD_STATE.
4. Screen 4 scope review queue with accept, edit, reject, split, merge,
and manual add.
5. requirement_drafts, screen 5, and the single transaction promotion
path.
6. cmd/opportunity-extract, cmd/opportunity-scope-normalize,
cmd/opportunity-amendment-diff.
7. Amendment impact analysis flagging affected approved requirements for
named human acceptance.
GATE 2: every extracted scope item carries a source citation. Uncited model
output is rejected, proven by test. A fixture containing a candidate row
and a fixture of tenant uploaded document text each fail the scrub suite
when placed anywhere in an external bound payload. Eight racing promotions
of one draft elect exactly one approved requirement. An ingest run during
promotion corrupts nothing. Approved requirements are never rewritten by
any ingest path, proven by test.
PHASE 3: PROPOSAL OPERATIONS
1. Proposal migrations from Section 3.7.
2. The workspace tabs from Section 6.
3. The readiness engine from Section 7 with snapshots and
cmd/proposal-readiness-snapshot.
4. cmd/proposal-deadline-alerts and cmd/stale-requirement-scan.
5. Submission workflow with server side hard blocker enforcement.
6. Outcome recording through bidoutcomes.
7. SLATE APPROVAL LIFECYCLE, merged from consolidated amendment 2.3. The
slate member record gains status in proposed, approved, withdrawn, plus
approved_by_user_id, approved_at, withdrawn_by_user_id, withdrawn_at,
and withdrawal_reason, which is REQUIRED on every withdrawal and stored.
Approve is one transaction taking FOR UPDATE on the member row: set
status and stamps, and where the slate links to a proposal increment
proposal_staffing_links.approved_count, audited through OnTx. The
approval shows in the proposal Staffing tab, readiness inputs, and slate
exports with who and when. Withdraw uses the same transaction discipline:
requires the reason, sets status and stamps, decrements approved_count,
audits, and flags the latest readiness snapshot stale. Withdrawal never
deletes anything. Re approval after withdrawal is allowed and the audit
trail carries the full sequence. Both actions use the existing slate
decision capability and notify the capture owner and proposal owner
through the alerts module. UI: approve and withdraw buttons on the member
row, withdraw opens a reason modal, the row shows current status with the
latest stamp.
GATE 3: a proposal with any hard blocker cannot be submitted through the
API, not merely the UI. The same inputs produce bit identical readiness
scores across runs. Every readiness result includes component scores,
driver explanations, blockers, timestamp, and algorithm version. All
workspace tabs render with zero 5xx. An outcome lands in bidoutcomes.
GATE 3 ADDITIONS, merged from consolidated amendment 2.3: approve, withdraw,
and re approve leave approved_count exactly correct under an EIGHT WAY RACE,
proven by racing it. A withdrawal without a reason is rejected by the API,
not just the form. A withdrawal flips readiness staleness and the next
snapshot reflects the reduced coverage. The full sequence is visible in the
audit trail.
PHASE 4: INTELLIGENCE TIER A
1. cmd/oews-metro-sync, cmd/sca-wd-sync (roughly ninety thousand wage
determination records, current revision flagged, no bulk download, so
page and cache), cmd/usajobs-sync including the unauthenticated code
list API, and task order depth in cmd/awards-sync.
2. ontology-sync bump to O*NET 30.3 now and 31.0 when it lands late
August 2026, CC BY 4.0 attribution recorded in the manifest, Career
Exploration Tools excluded because their license is no derivatives.
3. Verify whether CALC+ under buy.gsa.gov exposes a usable bulk path
before building on it. If not, ceiling rates load manually and are
versioned.
4. Eleven compliance knowledge cards through curation.curate, each cited
to primary sources: NYC Local Law 144, Illinois HB 3773 with the AI
Video Interview Act, California FEHA automated decision rules,
California CCPA ADMT rules, Colorado SB 26-189 and its stay, Texas
TRAIGA, New Jersey LAD guidance, Mobley v. Workday through the 22 June
2026 order, the Kistler v. Eightfold FCRA theory and why missionMATCH
is first party only, OFCCP after EO 11246 with the 5.1 percent VEVRAA
benchmark and the ESDS listing duty, and the EU AI Act with Digital
Omnibus timing.
5. Five new closed TAG intents, read only, parameterized, the model
selects the name only: wage_benchmark_by_msa(soc_code, msa),
sca_wd_lookup(state, county), recompete_radar(agency, naics,
months_ahead), vevraa_benchmark_status(), and
clearance_runway(clearance_level).
6. The AEDT evidence pack export stub in governance: scoring inputs,
engine version, weights, band caps applied, model registry entry,
exportable per decision.
GATE 4: every sync writes ingest_manifest rows and reruns without
duplicates. All eleven cards render with citations and appear in
GET /knowledge/cards. All five intents live in the registry with tests.
PHASE 5: recruitMATCH DESK COMPLETION
1. Expand the New Client form until a real client can be recorded:
contacts, addresses, billing terms, status, notes.
2. Fix the stale hardcoded copy at
internal/modules/recruitmatch/derive.go:73 so the dashboard reflects
what is actually tracked.
3. Make dashboard stat cards clickable through to their filtered lists,
platform wide.
4. Wire the existing time to fill migration to a real service.
5. Add revenue and bill rate tracking on placements, and candidate source
on candidates, with reporting.
6. Add the redeployment metric with an assignment end date alert through
the alerts module, plus submissions per job and fill rate analytics.
MERGED FROM THE CONSOLIDATED AMENDMENTS. The full text of each item is in
docs/DIRECTIVE_AMENDMENTS_CONSOLIDATED.md at the section cited; the summary
here is the work order.
7. DESK INTEGRATION, amendment 2.4. (A) Requisition to requirement bridge:
requisitions gain requirement_id, creating a job order either links an
existing requirement or creates and links one using the structured fields
the platform already scores against. The FROZEN engine is reached ONLY
through this bridge. Editing a linked requirement after screenings exist
marks those results stale, never silently. (B) Resume pipeline: upload on
the candidate record and through apply intake, sha256, versioned, current
flag, priors retained, bytes through internal/platform/storage; parsing
through docextract and extraction into DRAFT profile suggestions accepted
or edited by a recruiter, never silently applied; clearance always labeled
clearance intelligence, self reported; duplicate detection on email and
phone prompts open or merge. (C) Screen from the desk: a Screen action on
an application runs screenMATCH against the requisition's linked
requirement, storing score, band, caps applied, engine version, and
timestamp on the application, with re screening history and a stale badge.
8. PUBLISHING TO THE TENANT WEBSITE, amendment 2.5. Requisition visibility
internal_only or published with public label open, on_hold, filled,
closed, behind a publish checklist. The mm_api public jobs handler renders
SERVER SIDE HTML from Go templates: a careers index, one canonical detail
page per published open job, and the apply endpoint. Exactly one
JobPosting JSON-LD block per detail page mirroring visible content, none
on the list page. nginx on Bubbles proxies fg-talent.com/careers/ to the
mm_api upstream with the tenant slug fixed server side, and
/careers/apply with its own limit_req zone. JOSEPH REVIEWS BOTH LOCATION
BLOCKS BEFORE THEY TOUCH THE LIVE VHOST. Apply intake is same origin,
creating or dedupe matching a candidate and an application in the applied
stage, source career_site, consent event stored with notice version,
honeypot plus rate limiting. Nothing is ever posted to Google: indexing is
the careers sitemap section regenerated on publish and unpublish, internal
links, GSC Request Indexing, and the URL Inspection API. Expiry
discipline through cmd/jobs-expiry-sweep. FG TALENT CONTENT RULE, enforced
in template AND in test: only Patricia Cox and Brian Atwood may appear on
the public site or in schema; Tom and Glennette appear nowhere.
hiringOrganization is FG Talent Partners. Tenant mapping is configuration:
tenant_slug, public_base_url, brand template, consent notice reference; a
mapped tenant's jobs path on the missionMATCH domain issues a 301.
9. DESK WORKFLOW ADDITIONS 1 through 11, amendment 2.6: structured intake
brief (role context, candidate profile, submission logistics), pipelines
per placement type, client submittal records with feedback reason codes
and an aging SLA timer, a reason taxonomy on every rejection and backward
stage move, days in stage aging, activities and tasks wired into every
record surface, talent pools and saved searches, status triggered alerts
with human send, offers completed with decline reasons flowing into
placements, an optional per client requisition approval chain, and client
specific submittal requirements.
10. THE IMPORT CENTER AND UPLOADS, amendment 4. ONE upload subsystem, three
entry points (candidate record, recruitMATCH Import Center, screenMATCH
screen from resume), and apply intake uses the same path. Every bulk
import is STAGED, never direct: import_batches, import_rows,
import_mappings, then validate, preview, and a HUMAN COMMIT with a first
party attestation. Protected class columns are REJECTED at validation by
name pattern and content sniffing; EEO data never enters through an
import. cmd/import-process is asynchronous, bounded, observable, audited,
and idempotent by row action. Archive safety on every zip: list entries
first, per entry allowlist and size cap, entry count cap, reject nested
archives and path traversal.
11. THE CANDIDATE SPINE AND THE CANDIDATES PAGE, amendment 5. ONE candidate
spine shared by every surface, with shared frontend profile components
composed by both the command center and the new recruitMATCH Candidates
page. Candidate search is DETERMINISTIC SQL through the existing hybrid
fusion exposed as a repository query; the AI Ask path is untouched.
Reverse match runs the frozen engine, one candidate against every open
requisition's linked requirement, persisting score, band, engine version,
and computed_at, going stale on change. Add to job order creates the
application under FOR UPDATE with exactly ONE active application per
candidate per requisition enforced by a partial unique index over non
terminal statuses. Applications record added_from and, when added from a
match, the score snapshot at add time. Job order status is a state
machine (draft, open, on_hold, filled, closed) that UPDATES ITSELF:
filling the last seat flips to filled inside the placement transaction the
seat lock governs, moves remaining active applications to
filled_by_another (a distinct outcome, never a rejection) with
notifications, and unpublishes the public page; a fall off frees the seat,
reopens the order, alerts, and NEVER republishes without a human confirm.
GATE 5: a real client is created through the UI. The full pipeline,
contacted to interview to offer to hire or no hire, is exercised end to end
with data. Time to fill, fill rate, submissions per job, redeployment rate,
revenue, bill rate, and source all render with real numbers. Zero 5xx.
GATE 5 ADDITIONS, merged from consolidated amendments 2.8, 4.6, and 5.7.
Publishing and desk (2.8): publish a job order and load it at
https://fg-talent.com/careers/ and its detail URL, with curl showing full job
content in the INITIAL HTML response, no JS required. Exactly one JobPosting
JSON-LD block mirroring visible content, passing Rich Results. Apply from
fg-talent.com creates a candidate and an application, stage applied, source
career_site, consent event stored with notice version, duplicate check firing
on a repeat email. Filling the order removes the detail page, the index entry,
the sitemap entry, and the markup. The missionMATCH domain path for the FG
Talent slug issues a 301. Page and schema contain Patricia Cox and Brian
Atwood only, with a TEST asserting Tom and Glennette appear nowhere in
rendered output or JSON-LD. Unknown slug returns 404 leaking nothing, and the
apply rate limit answers 429 under a race. The careers sitemap regenerates on
publish and unpublish, GSC Request Indexing is exercised once and recorded,
and the feed renders on a test page. Screen from an application stores a
banded result that goes stale when the requirement changes; resume parse
produces suggestions and never overwrites; duplicate detection fires. An
intake brief saves with all three blocks, a client submittal records feedback
with a reason code whose SLA timer ages, a rejection carries a taxonomy
reason, and an accepted offer flows into a placement carrying rates forward.
Imports (4.6): a three file resume bundle commits to three candidates with
parsed suggestions and batch and row lineage. A planted duplicate triggers the
dedupe prompt and a merge produces ONE candidate. A spreadsheet with a
protected class column is REJECTED at validation with the column named and
nothing reaches a live table. A five hundred row spreadsheet commits with
correct counts and a clean error export. KILL cmd/import-process MID COMMIT
and rerun: the batch completes with no duplicates, proven by counts. Screen
from resume produces a banded result and the dedupe prompt fires. A bulk
screen fills the queue and lists a corrupt file as failed. A zip with path
traversal and a zip with a nested archive are both rejected AT LISTING. Every
commit stores the attestation with user and timestamp. An uncommitted batch is
invisible to candidates, search, matching, and slates, proven by query.
Candidates and dataflow (5.7): the Candidates page finds a planted candidate
by skills filter and by free text through the fusion query, and the command
center Candidates screen still renders from shared components with ZERO
regression. Reverse match returns an IDENTICAL ranking across repeated runs
and the engine diff remains empty. Add to job order from the match list
creates the application with the score snapshot and added_from match; EIGHT
RACING ADDS of the same pair produce exactly ONE application; a duplicate add
through the API is rejected. Stage counts update through every stage and each
tile clicks through. Placing the final seat flips the order to filled, moves
remaining applications to filled_by_another with alerts, and removes the
fg-talent.com page and sitemap entry. A simulated fall off frees the seat,
reopens the order, does NOT republish, and a one click reactivation restores a
filled_by_another candidate to their prior stage, audited. Re adding a
previously rejected candidate demands a reopen reason. A candidate on a
proposal slate and in a desk pipeline resolves to ONE candidate id everywhere.
PHASE 5A: IDENTITY AND ACCESS
Inserted between Phase 5 and Phase 6 by docs/DIRECTIVE_AMENDMENT_6.md, triggered
by live FG Talent feedback. Full text in that file, section 4.
1. MATRIX REBUILD. Capabilities become entity action pairs carrying an
OWNERSHIP SCOPE of own, team, or organization, plus feature capabilities for
export, bulk edit, import commit, publish, and the existing approvals. The
nineteen current capability keys REMAIN AS ALIASES mapped forward, never
renamed destructively. Role defaults, tenant scoped, each row building on the
one before it: viewer reads only with no export and no rates and no EEO ever;
member adds notes, tasks, and activities; recruiter adds full desk work,
imports including commit with attestation, pools and saved searches, pay rate
visible, export limited to own and team; manager adds orders create and edit,
publish and unpublish, requirement approval and proposal submission, bill rate
and margin visible, organization wide export and bulk edit, fall off
reactivations; admin adds user management, tenant settings, stage templates,
notice templates, import mappings, and integration configuration; owner adds
billing and plan, retention settings, and full data export; superuser is owner
plus everything else BY RULE, WITHIN ITS TENANT ONLY; operator is everything
everywhere including platform surfaces, always audited, MFA always.
2. OWNERSHIP SCOPES as an attribute of each entity action capability, with team
defined by the light teams model (an optional team assignment per tenant).
3. FIELD SENSITIVITY CLASSES, first class rates: pay rate visible to recruiter
and above; bill rate and margin visible to manager and above and INVISIBLE
below that everywhere. Wired through render, export, feed, and API layers so a
hidden class is ABSENT FROM THE PAYLOAD, not blanked in the UI. The mechanism
is generic so future sensitive fields reuse it.
4. IMPERSONATION, operator only: view as a user with a persistent on screen
banner, read only by default, write only through explicit escalation, auto
expiry within the hour, every impersonated request audited with BOTH
identities.
5. ORGANIZATIONS PROVISIONING, operator console: create a tenant, set product
entitlements per tool, configure the site mapping, and invite the first
superuser by email. The productized path for onboarding the next client.
6. The exhaustive capability matrix tests are REGENERATED for the new shape and
pass in full, including the alias mapping from the nineteen legacy keys.
GATE 5A: the regenerated matrix tests pass for every role, every tool, every
scope. A recruiter cannot obtain bill rate or margin through ANY surface, screen,
export file, feed, or raw API response, proven by fixture inspection of the
payloads. The grant ceiling holds at every rung, proven by attempting each
violation. Impersonation shows the banner, expires on schedule, defaults read
only, and writes both identities to every audit row it touches. Provisioning on
staging creates a tenant, entitles products, configures a site mapping, and
delivers a working superuser invitation end to end. A tenant superuser requesting
any platform route receives 404, and the operator with MFA reaches all of them.
PHASE 6: PRODUCTION HARDENING AND GO LIVE
1. Enable MFA. Set MM_ENV to production and validate the
MM_ALLOW_SELF_HOSTED_PRODUCTION path so every production gate is live.
2. Queue and sync metrics through obs.
3. Backup and restore test, large document tests, and a rerun of the full
concurrency suite including the original sixteen racing stage
transitions.
4. Documentation sweep: Owner's Manual, MODEL_CARD, STACK_DECISIONS,
endpoint index, table index, runbooks, and the honest work queue, which
must match BUILD_STATE exactly.
GATE 6: the final acceptance list in Section 11 passes in full.
POST GO LIVE FAST FOLLOW, not gated for launch: agency forecast ingestion,
VEVRAA state job bank posting starting with design partner states, the
SkillBridge authorized organizations directory (organizations, never
people), SBA socioeconomic data through SAM.gov entity extracts, the hand
curated quarterly clearance runway reference table from GAO and
performance.gov, and licensed compensation benchmarks, never scraped.
Merged from consolidated amendment 2.7: the client portal rendering the
submittal and feedback model, read only, per client contact authentication;
automated candidate engagement sequences on status triggers once a sending
policy is decided; VEVRAA ESDS posting reusing the publish pipeline as its
source of truth; knockout questions and reference check records on
applications; a placement start onboarding checklist; and requisition and
placement history import.
--------------------------------------------------------------------------------
TASK T1: MARKETING SITE LOGIN BUTTON, STANDALONE
--------------------------------------------------------------------------------
Registered from consolidated amendment 2.2. This is standalone work OUTSIDE
the phase gates and OUTSIDE the missionMATCH repo, executed on the Bubbles
vhosts.
1. Enumerate the nginx vhosts on Bubbles and identify where missionMATCH is
marketed publicly. The application itself lives at
https://missionmatch.thatwebhostingguy.com, so the button belongs on the
marketing surface, not inside the app.
2. If the target is ambiguous, list the candidate vhosts for Joseph in ONE
message and WAIT for his pick. Never guess on a production website.
3. Add a prominent Log in button in the site header and hero linking to
https://missionmatch.thatwebhostingguy.com. Static HTML, CSS, and vanilla
JS, brand consistent, no framework, no Python.
4. If no marketing page exists, say so and build a single branded page with a
hero, a product line summary, and Log in, at the vhost Joseph names.
5. Verify rendered in a browser over HTTPS before recording done.
--------------------------------------------------------------------------------
11. FINAL ACCEPTANCE, ALL MUST PASS
--------------------------------------------------------------------------------
1. A SAM.gov opportunity is ingested without duplication.
2. A manual opportunity is created with minimum information.
3. Manual and SAM.gov opportunities have full feature parity.
4. Amendments create immutable versions.
5. Tenant capture data survives source refreshes.
6. Uploaded files are validated, hashed, versioned, and traceable.
7. Every extracted scope item has a source citation.
8. Uncited model output is rejected.
9. Private scopes never use external inference, proven by the scrub suite.
10. Requirement drafts cannot enter candidate scoring without named human
approval.
11. Amendments never silently rewrite approved requirements.
12. Proposals cannot be submitted with hard blockers, enforced server side.
13. Readiness calculations are bit reproducible.
14. Every readiness result includes explanations and an algorithm version.
15. Every new tenant table has organization_id, forced RLS, and the
composite index.
16. Cross tenant queries return zero rows.
17. All jobs are idempotent and rerun safe.
18. Every external source writes provenance to ingest_manifest.
19. The gateway with openai enabled and no secret refuses to boot.
20. No new Python exists anywhere, confirmed by rg.
21. No dashes exist in written docs, confirmed by rg.
22. Audit records remain append only, update and delete still revoked.
23. The candidate scoring engine diff against the starting commit is empty.
24. BUILD_STATE, the honest work queue, and reality agree.
Merged from the consolidated amendments, sections 2.9, 4.7, and 5.8.
25. Slate approval and withdrawal maintain correct counts under race, and
every withdrawal carries a stored reason.
26. Public jobs surfaces expose only published data, resolve tenants by slug
only, and pass Rich Results validation on every live posting.
27. Expired or filled postings drop their markup immediately, verified by the
sweep job's test.
28. Apply intake stores a consent event with notice version for every
submission.
29. Desk screenings reach the engine only through the requisition to
requirement bridge, and the engine diff remains empty.
30. No import writes a live record except through a committed batch, and every
imported record carries batch and row lineage.
31. Protected class data cannot enter through any import path, proven by the
rejection tests.
32. Import batches are idempotent under rerun and partial failure, with counts
that always reconcile to rows.
33. Exactly one active application exists per candidate per requisition,
enforced by the database, proven by the race.
34. Every job order status transition, automatic or manual, carries an audited
cause, and filled_by_another is distinguishable from rejection in every
report.
35. Reverse match rankings are bit reproducible, persisted with engine version,
and the frozen engine remains untouched.
Merged from docs/DIRECTIVE_AMENDMENT_6.md section 5.
36. Every role can edit its own profile, and identity changes are audited and
propagate everywhere the identity renders.
37. The role grant ceiling is enforced at the API for every rung of the ladder.
38. Rates above pay are invisible below manager on every surface, including
exports, feeds, and raw API payloads.
39. Operator requires MFA without exception, platform surfaces answer 404 to
everyone else, and every impersonated action carries both identities in the
audit trail.
40. User accounts are never deleted; deactivation blocks login while preserving
ownership, activity, and audit history.
DEFINITION OF DONE
A company can discover an opportunity from SAM.gov or enter one manually,
upload and version its solicitation package, extract and review the scope,
approve workforce requirements, generate staffing slates, manage proposal
compliance and content, measure explainable readiness, submit the
proposal, record the outcome, and trace every material decision back to
its original source, all inside missionMATCH, with nothing built beside
it.
================================================================================
APPENDIX A. THE CANONICAL DATAFLOW, PERMANENT
================================================================================
Merged from consolidated amendment 5.1 as a permanent part of this directive.
One candidate spine. One linking record. Everything hangs off it.
INTAKE CANDIDATE WORK
apply on fg-talent.com \ / desk pipeline
Import Center bulk \ / (applications)
screenMATCH resume > candidates table < proposal slates
quick add on the desk / one record per person \ screenings
(all paths dedupe) / all tools, same spine \ pools, searches
THE LINKING RECORD: application (candidate x requisition)
exactly one active application per candidate per requisition
requisition (job order, openings count, linked requirement)
|
| add to job order (from match, search, pool, or manual)
v
application: prescreen -> internal submittal -> client submittal
-> interview -> offer -> placed
or out at any stage, always with a reason code
|
| offer accepted
v
placement takes a seat under the existing FOR UPDATE seat lock
|
v
JOB ORDER UPDATES ITSELF
stage counts derive live on the order
last seat placed flips status to filled in the same transaction
remaining active applications become filled_by_another, notified
filled unpublishes the fg-talent.com page via the expiry sweep
a fall off frees the seat, reopens the order, alerts the owner,
and never republishes without a human confirming
|
v
placement end approaching -> redeployment alert -> candidate returns
to the available pool -> the spine loops
Both match directions use the SAME FROZEN ENGINE: a job order ranks
candidates, and a candidate ranks open job orders, both reading through the
requisition to requirement bridge. screenMATCH is the deep evaluation of one
pairing, stored on the application. slateMATCH is the proposal branch off the
same spine, and a person may sit on a slate and in a desk pipeline at once
because both point at one candidate record.