The FY27 Recompete Cliff: Which Federal Contracts Will Change Hands?
Between October 1, 2026 and September 30, 2027, 16,281 federal contracts of $5M or more reach the end of their current period of performance. When that work is bought again, some incumbents keep it and some lose it. Capture teams, and the incumbents themselves, spend real money guessing which is which.
I built Fed-Spend, a federal-contract intelligence platform, and it already ships a hand-weighted "incumbent vulnerability" score. So this project asks an uncomfortable question of my own product: does that score actually predict who loses the recompete? And if not, what does?
The short version: a calibrated model trained on seven years of public award data predicts a change of vendor far better than the heuristic (0.754 vs 0.585 AUC on a year it never saw). Most of the signal is in how the last award was bought, not in the algorithm.
Metric | Value |
Contracts of $5M+ ending Oct 2026 to Sep 2027 | 16,281 |
Lifetime obligations on those contracts | $1,027.2B |
Labeled recompetes, FY2019-FY2025 | 6,883 |
Held-out AUC, FY2025 (model / my old heuristic) | 0.754 / 0.585 |
Changed hands, model's top 10% (base rate 50%) | 88% |
The question
Predict: six months before a contract ends, how likely is the follow-on to go to a different vendor?
Explain: which award patterns separate retained incumbents from displaced ones?
Compare: how does that stack up against the heuristic Fed-Spend ships today, graded on the same labels?
Operate: rank every FY27 expiring contract, with reasons, and keep the list current every week.
The data
USAspending.gov bulk award downloads: 554,649 prime contracts signed FY2012-FY2026, pulled through the public download API (async jobs, polled to completion) and reconciled to the award count endpoint, plus two years of award modifications to catch contracts signed before FY2012 that are still running (61 FY27 contracts, $176.1B, mostly Energy laboratories, Defense, and NASA). Public domain.
Fed-Spend API: the GAO bid protest docket and Fed-Spend's live recompete radar, pulled with a key that lives in Infisical and never touches disk.
DuckDB: typed, deduplicated, and rolled up to 46,843 parent vendors, so a subsidiary winning the follow-on counts as the same incumbent.
Building ground truth that did not exist
Federal data never says "award B replaced award A." Without that link there is nothing to predict, so the first real job was a follow-on matcher:
Vendor-blind by design. Candidates come from the same office with a matching product or industry code, a start date in a window around the predecessor's end, and a similar size. The match is scored on description similarity (TF-IDF), timing, and size. The vendor is never an input, and a unit test proves it; otherwise the label would simply learn "same vendor means retained."
Four rounds of blind hand audits. Each round drew 100 matched pairs, 50 retained and 50 changed, with vendor names hidden, and graded them as the same requirement, related, or wrong. Each round changed the rules: excluding orders under the same parent vehicle, requiring informative descriptions, penalizing place-of-performance conflicts for services, and dropping construction.
Final audit: 66% clearly the same requirement, 17% related, 17% wrong. Wrong matches cluster in the "changed" class (28% vs 6%), so change rates lean high and measured accuracy is, if anything, understated. Full audit log.
No time travel
Point-in-time features. Everything is computed as of 180 days before the contract ended: competition, offers, vehicle type, set-aside, incumbent share of the office, office and incumbent recompete history: 38 features in all.
Already-decided contracts are dropped. If the follow-on was awarded before that date, the outcome was knowable, so the row leaves training.
Temporal split. Train on contracts ending FY2019-FY2023, calibrate on FY2024, test once on FY2025.
Leakage check. Removing every feature derived from past labels leaves AUC unchanged (0.756), so the result is not riding on a leak.
Results
FY2025 test set, 1,126 recompetes | AUC | Top 10% hit rate | Calibration error |
Fed-Spend heuristic, as shipped | 0.585 | 53% | 0.151 |
Logistic regression baseline | 0.743 | 80% | 0.059 |
LightGBM, calibrated (shipped) | 0.754 | 88% | 0.053 |
The model beats the heuristic by 0.169 AUC (95% bootstrap CI 0.131 to 0.207). Of the 10% of contracts it flags most strongly, 88% changed hands against a 50% base rate.
It does not meaningfully beat logistic regression (+0.012, CI includes zero). The honest reading: the features carry the signal, and the heuristic's problem is its weights, not its lack of machine learning.
Calibrated probabilities. Isotonic calibration on FY2024 brings expected calibration error to 0.053, so a 70% means roughly 70%.
Where it is weaker: AUC is 0.786 inside DoD, 0.693 for small-business incumbents, and 0.604 for other agencies. On high-confidence labels only, it rises to 0.793.

What predicts a change of vendor
Pattern at the last award, FY2019-FY2025 | Changed hands |
Competed / sole-source | 59% / 26% |
Order under a multiple-award / single-award vehicle | 68% / 42% |
Two or more offers / one offer | 63-66% / 37% |
Incumbent holds under 20% / 20%+ of the office's spend | 54% / 25% |
Incumbent is a small business / is not | 58% / 45% |
The pattern is intuitive once it is measured: work that was competed, split across a multiple-award vehicle, and drew several offers is contestable. A sole-source incumbent holding a large share of the office's spend usually keeps it. The change rate also drifted up, from 44% in FY2019 to 55% in FY2024.

The FY27 watchlist
Every expiring contract gets a calibrated probability, a band (top 5%, elevated, typical, low), and its top three reasons in plain language. Summed, the probabilities imply about 8,299 vendor changes in FY27. The top 5% band holds 815 contracts and $14.3B. Open GAO protests appear next to each contract as context, not as a model input, because the docket only reflects protests that are live now.


Built to run, not just to demo
Weekly refresh. A GitHub Actions job runs every Monday: it pulls new and modified awards, refreshes the Fed-Spend radar, rescores the watchlist with the exact model that was evaluated, and commits the new data. Retraining is a reviewed change, never automatic.
n8n digest and watchdog. The job posts a week-over-week digest (contracts new to the top 5%, the biggest risk moves, and top-band contracts ending within 90 days) to an authenticated n8n webhook that emails it. A Tuesday watchdog alerts if a week passes with no digest.
Governance. A model card mapped to the NIST AI Risk Management Framework (Map, Measure, Manage, Govern), a datasheet, subgroup metrics, and known gaps.
Secrets stay out of the repo. Keys are pulled from Infisical into the process at runtime locally and read from GitHub Actions secrets in CI.
What I would do with it
Replace the hand-weighted score. For Fed-Spend, the takeaway is direct: swap the heuristic for the calibrated model, and show the reasons next to the number.
Capture teams: start with the contestable profile. Competed, multiple-award, several offers, and an incumbent with a small share of the office. Those recompetes changed hands about two times in three.
Incumbents: read the band as an early warning. A top-5% contract 12 months out is a signal to invest in the relationship and the proposal now, not in the final quarter.
Treat small agencies with care. The model is weakest outside DoD; use it there as a sort order, not a verdict.
Tools
Python 3.12, pandas, and uv with a Makefile for a reproducible pipeline
DuckDB SQL for typing, deduplication, vendor rollup, candidate generation, and point-in-time features
scikit-learn and LightGBM for TF-IDF matching, logistic regression, gradient boosting, and isotonic calibration
SHAP for global importance and per-contract reasons
Streamlit and Plotly for the watchlist app; matplotlib for the figures
GitHub Actions, n8n, and Infisical for the weekly refresh, digest email, watchdog, and secrets
USAspending API and the Fed-Spend API for the data
Reproduce it
Everything runs from one repository: github.com/jasonpellerin-aisolutionist/federal-recompete-cliff. Clone it, run make download, make build, make label, make features, make train, and make score, and every number in this post is recomputed from public data. The docs folder holds the methodology, label audit, and model card.
Limits: labels are inferred from award data, not recorded, and 17% of audited matches were wrong. Only 7,332 of 59,888 ended contracts received a confident label, so scores apply best to contracts like those. The model sees award patterns, not performance ratings, pricing, or proposals. A score describes a pattern, not a judgment about any incumbent.
Data: USAspending.gov (public domain) and the GAO bid protest docket via Fed-Spend. This project is independent and not affiliated with any federal agency.




Comments