<!-- Generated from public/nexus/index.html by scripts/markdown-twins.mjs. Do not edit. -->

# 33,136 bid protests, mapped.

Every decision coded against 33 protest grounds and eight case facets, in a 3D constellation you can filter, a decision table, and per-ground sustain rates. As of v2.1 it opens straight in your browser, with a Setup tab for connecting your own model and the full license in the page; the downloadable bundles add full-text search over a smaller corpus.

## Run it in your browser

The whole dashboard is one self-contained page. No download, no Python, no server, no account. It is the largest corpus the project has covered.

**What is in it.** 33,136 GAO bid protest decisions across 33 protest grounds and eight facets — ground, outcome, procurement authority, protest posture, decade, PDF source, text source and digest. Seven tabs: the 3D map, the decision table, Dynamic Search, Ground detail with per-ground sustain rates, Analysis, Setup, and the full license and credits.

**The two model-backed tabs.** **Dynamic Search** runs against a vector index of the full decision text. That index is far too large to ship inside a web page and needs a small server behind it, so the tab is present but says plainly that it is not part of this hosted copy; it works in the [downloadable bundles](https://acqagent.ai/#gao-bid-protest-nexus) below. **Analysis** does run here: point it at any OpenAI-compatible endpoint that allows browser requests, and hand it the decision text or PDF. Your key stays in your own browser and never reaches this site. The bundles remove both chores by reading the local corpus and holding the key server-side.

**What it cannot tell you yet.** The coding is AI-derived and has not been reviewed decision by decision. 82% of ground assignments are model-assigned rather than checked, and are marked with a dagger in the table and popups. Half the corpus has no recorded outcome, so sustain rates cover a subset rather than the whole. 27,239 decisions link to a gao.gov landing page rather than straight to the PDF. The dashboard states all of this on its own face as you use it, and links to a fuller explanation.

**Where it is going.** Accuracy and every feature here are under active work, release over release: more reviewed ground assignments, more recorded outcomes, more direct PDF links, and faster filtering over a corpus this size. Treat any single coded value as a lead to verify against the original decision, never as a finding.

## Two new tabs

Analysis has always been able to run in the browser, but it needed a model endpoint and nothing on the page told you how to give it one. And the licensing sat on this site rather than in the file you were actually using. Both are now tabs in the dashboard itself.

> **Setup** — What each tab needs and how to connect a model, in the page rather than in a README. Four steps for the browser route, the server route for anyone who would rather their key lived in an environment variable, and a table of what the errors mean when it does not work.

> **License** — Which license covers what, a citation you can copy straight out, credit to the corpora underneath, and a plain count of what the coding does not yet cover: 27,232 of 33,136 grounds assigned by a model, half the corpus with no recorded outcome.

**Setup** answers the question the Analysis tab used to raise and leave hanging. Map, Table and Ground detail need nothing; Analysis needs any endpoint speaking the OpenAI `/v1/chat/completions` shape, hosted or running on your own machine; Dynamic Search needs the vector index and a server. The tab says which is which, gives the base URL for LM Studio, Ollama, llama.cpp and vLLM, and is straight about the two catches — your key is held in your own browser's local storage and goes nowhere but the endpoint you name, and an endpoint that refuses browser requests has to be reached through the project's own server instead.

**License** puts the terms in the file. The dashboard is one self-contained page that people download and pass around, and a copy that has left this site should still be able to say what it is: Apache 2.0 for the code, CC BY 4.0 for the taxonomy and the coding, MIT and CC BY for the two upstream corpora, public domain for the decisions themselves. The citation that satisfies the attribution requirement is there to copy, and so is the same limitations accounting the map carries.

## Download a bundle

The basic bundle is the v2.1 dashboard as a file: the same 33,136 decisions and eight facets you get in the browser, running offline with no server and no account. The full bundle adds the one thing neither can do on its own, full-text search over the complete decision text, and it is being rebuilt against v2.1, so its download is off until the new one lands. Note that its vector index still covers the earlier 5,986-decision corpus; Map, Table and Ground detail read the embedded data and are current either way. In the meantime the [repository](https://github.com/acqagent/GAO-Bid-Protest-Nexus) carries every file it contains except the vector index, which you can build yourself. [Screenshots](https://acqagent.ai/#nexus-screens) and [step-by-step setup](https://acqagent.ai/#run-the-dashboard) are below; the [README](https://acqagent.ai/downloads/gao-bid-protest-nexus-README.md) covers hardware requirements and model options in full.

### Download — Basic (.zip)

The whole v2.1 dashboard as one self-contained HTML file, plus the four license files. Unzip, open it in any browser, and you get all 33,136 decisions across eight facets: the 3D map, the full decision table, Ground detail — sustain rates by ground against the corpus base rate, each with the confidence interval its sample size supports — plus the Setup and License tabs. No Python, no server, no models, no network. The Analysis tab works here too, once you point it at a model endpoint and hand it the decision; Dynamic Search is the one tab that asks for a server.

### Download — Full (.zip)

Everything in the basic bundle plus the Dynamic Search backend: the server scripts, the decision metadata, and a 77,979-chunk vector index. Hybrid retrieval — dense vectors, BM25, reciprocal rank fusion, and cross-encoder re-ranking — surfaces the passages that match a plain-language query, each linked back to its source PDF. The server also feeds the Analysis tab straight from that index, so it summarizes a decision without fetching anything. Runs on local models.

Being rebuilt against v2.1, so the download is off until the new one lands. Everything but the vector index is in the [repository](https://github.com/acqagent/GAO-Bid-Protest-Nexus) today, and [the steps below](https://acqagent.ai/#run-the-dashboard) cover building the index yourself.

### Source on GitHub

The dashboard, the search backend, and the decision metadata — browsable and forkable. The vector index ships only in the full bundle above; every other file is in the repository. Code is Apache 2.0; the data is CC BY 4.0.

## What it looks like

Map, Table, Ground detail, Setup and License run from the basic bundle with nothing installed. Dynamic Search needs the full bundle and a local Python server; Analysis needs a model endpoint, which can be one running on your own machine. These shots predate v2.1: they were taken against the earlier 5,986-decision corpus, so the counts are smaller than what you will see, and they show the layout from before Setup and License were added. The [live dashboard](https://acqagent.ai/nexus/dashboard) is the current interface.

> **Map** — Every one of the 5,986 decisions as a dot, clustered around its protest ground. Ball size tracks volume, so Past Performance Evaluation at 808 dwarfs Definitive Responsibility Criteria at 11.

> **Table** — Every matching decision with its five filter values and links straight to the gao.gov page and the PDF.

> **Dynamic Search** — Full-text search over the decision corpus, returning ranked passages with relevance scores and source links. Needs the full bundle.

> **Ground detail** — The sustain rate for one ground against the 12.2% corpus base rate, with its 95% confidence interval, splits by authority and posture, and every sustained decision listed.

> **Analysis** — One decision reduced to its key elements: parties, solicitation, authority and posture, the grounds raised, and GAO's ruling on each. Here, a $706M systems engineering award protested on organizational conflict of interest.

> **Analysis — comparison** — Select up to twelve decisions anywhere in the dashboard and read them against each other: a side-by-side table, what they share, and where they diverge. These three all turn on OCI; one was sustained.

## Run the full dashboard

The basic bundle needs nothing but a browser — Map, Table, Ground detail and License all work offline. These steps add the Dynamic Search tab: plain-language search across the full text of the decisions, running entirely on your own machine. They are written for the full bundle, which ships the vector index already built; while its v2.1 rebuild is [still coming](https://acqagent.ai/#full-bundle), step 1 clones the repository instead and step 2 builds the index. The last step is optional and wires up the Analysis tab.

Clone the repository. It carries everything the full bundle will — `visualization/`, `scripts/`, `data/` and the license files — except the vector index, which step 2 builds.

```
git clone https://github.com/acqagent/GAO-Bid-Protest-Nexus.git
cd GAO-Bid-Protest-Nexus
```

No git? Use the green **Code** button on GitHub, choose **Download ZIP**, and unzip it into a folder of its own. When the [full bundle](https://acqagent.ai/#full-bundle) is posted it will expand to the same layout with `vector/` already filled in, and you can skip step 2 entirely.

Dynamic Search runs over a vector index of the decision text. The bundle will ship one; from a clone you build it once, against any embedding endpoint you can reach. Use a model trained for retrieval — `Qwen3-Embedding`, `BAAI/bge-*` or `intfloat/e5-*`. A general chat model produces vectors, but not ones that put a question near the passage answering it.

```
python3 scripts/vectorize.py --links pdf-links.csv \
        --embedder api --embed-base-url http://localhost:8080/v1 \
        --embed-model your-embedding-model
```

It writes `vector/chunks.jsonl` and `vector/embeddings.npy`. This is the long step; everything after it is quick.

Double-click `visualization/map.html`. Map, Table, Ground detail and License work immediately, with every filter, and no Python, server or network. Dynamic Search needs the steps below; Analysis needs step 8, or your own endpoint entered in the Setup tab.

The rest of these steps add Dynamic Search. You'll need **Python 3.10 or newer**, roughly **1.8 GB of free disk** (0.4 GB index plus a one-time 1.4 GB model download), and **4 GB of RAM** — 8 GB is comfortable. A GPU is optional and only makes re-ranking faster. Confirm your version with `python3 --version`.

From the folder you unzipped into:

```
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
```

Windows PowerShell:

```
python -m venv .venv
.venv\Scripts\pip install -r requirements.txt
```

Three dependencies: `numpy`, `rank-bm25`, and `sentence-transformers`.

```
.venv/bin/python scripts/serve.py
```

Windows: `.venv\Scripts\python scripts\serve.py`

**The first run downloads about 1.4 GB of models** from huggingface.co — `BAAI/bge-base-en-v1.5` for dense embeddings and `BAAI/bge-reranker-base` for cross-encoder re-ranking. They land in `~/.cache/huggingface`, so it happens once and every run afterwards is fully offline. Expect 30–60 seconds of startup while the vector store loads, ending in a `[serve] ready` line.

Visit `http://127.0.0.1:8765/` and switch to the **Dynamic Search** tab. `Ctrl+C` in the terminal stops the server; the Map and Table tabs keep working without it.

Analysis writes the summaries and comparisons; it needs a model, which the bundle does not ship. Point it at anything speaking the OpenAI `/v1/chat/completions` shape — OpenAI, OpenRouter, Groq, or a local LM Studio, Ollama or vLLM. Set it in the server's environment before starting, and the key never reaches your browser:

```
export OPENAI_BASE_URL=http://localhost:1234/v1
export OPENAI_MODEL=your-model
export OPENAI_API_KEY=sk-...            # omit for most local servers
.venv/bin/python scripts/serve.py
```

**If your model is a reasoning model** — o-series, DeepSeek-R1, Qwen3 — add `export OPENAI_REASONING_EFFORT=low`. Those models bill their thinking against the same token budget as the answer, and on a long decision they can spend all of it reasoning and return an empty analysis with no error. Raising the budget does not help; capping the effort does. A local model also streams for minutes, so `export OPENAI_TIMEOUT=1800` is worth setting.

Everything the tab does is also available headless — `python3 scripts/analyze.py B-417327` writes the same Markdown with no browser at all.

The server binds to loopback only, so nothing is exposed to your network. Configuration, the search API, hardware notes, and cloud model options are all covered in the [README](https://acqagent.ai/downloads/gao-bid-protest-nexus-README.md).

## Credits

The Nexus is built on someone else's work, and the corpus it reads came first.

**The decision corpus** came from [Kevin Misener](https://github.com/kmisener90), in two collections. The 33,136 decisions behind the dashboard come from his [GAO-Bid-Protest dataset on Hugging Face](https://huggingface.co/datasets/Kmisener/GAO-Bid-Protest), released under the MIT license. The roughly 5,700 decisions behind the downloadable bundles come from his [GAO-Bid-Protest-Dataset on GitHub](https://github.com/kmisener90/GAO-Bid-Protest-Dataset), which is released under a Creative Commons Attribution license. Both were used the same way: the URLs to the decision PDFs, the 33-ground taxonomy and the per-decision coding were derived from his data using AI, and the dashboard and the search index are built on top of that. If you reuse either corpus, credit that source too.

**The decisions themselves** are works of the U.S. federal government and are not protected by U.S. copyright (17 U.S.C. § 105), so they are in the public domain. Cite GAO as the source.

**Everything added here** carries two licenses. The dashboard code, the search backend and the scripts are [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0). The 33-ground taxonomy, the per-decision coding, the decision metadata and the vector index are [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/). Use it, change it, build on it, at work or commercially — just keep the credit. Releases before v1.0 were published under CC BY 4.0 in full and stay available under that license.

Free to use, share and adapt with attribution, at work or commercially — the dashboard code under Apache 2.0, the taxonomy, coding and decision metadata under CC BY 4.0. Found a miscoded decision or want a ground split differently? [Open an issue.](https://github.com/acqagent/GAO-Bid-Protest-Nexus)
