# Pursue or Pass: five rules for every federal notice, and a five-check script (free sample)

From the PaperChaseWebb Pursue or Pass bundle. Version 1.0, 2026-11-03. Plain markdown and one Python 3 script, standard library only. It reads one file, writes nothing and opens no network connection. Works with Grok Bot, Claude Code, Codex and Hermes through the prompt below. Not legal advice.

## Install

Save this file as pursue-or-pass-sample.md, then pull the script out of it:

~~~sh
python3 -c "import re;t=open('pursue-or-pass-sample.md').read();open('pursue_mini.py','w').write(re.search(r'~~~python\n(.*?)~~~',t,re.S).group(1))"
~~~

Save the worked example below as pursuits.csv, or write your own, and run `python3 pursue_mini.py pursuits.csv`. Exit 0 clean, 1 findings, 2 missing input.

## Five rules

1. Gate before you score. If any eligibility gate fails, the answer is no-bid, however good the notice looks.
2. The score sets a ceiling. You can decide lower than the band your score gives. Never higher.
3. Write the reason. Three lines at most, in plain words, for yes and for no.
4. You decide, by name. An agent can draft the decision. It cannot make it.
5. Keep every row, no-bids included. They show you what to target next quarter.

## The five checks

| Check | What it catches |
|---|---|
| Q1 | a gate stopped the notice but the decision is not NO-BID |
| Q2 | a decision above the band its score gives (75 BID, 60 BID WITH CONDITIONS, 45 RESHAPE) |
| Q3 | a decision with no reason |
| Q4 | a decision with no one's name on it |
| Q5 | an agent named as the decider |

~~~python
#!/usr/bin/env python3
"""pursue_mini.py: five read-only checks on a pursuit register (free sample).
Usage: python3 pursue_mini.py pursuits.csv   Exit 0 clean, 1 findings, 2 missing input."""
import csv, re, sys
AGENTS = re.compile(r"\b(grok|claude|codex|hermes|gpt|chatgpt|agent|bot|assistant|ai)\b", re.I)
NEED = ["notice_id", "gate_result", "score_total", "decision", "why", "decided_by"]

def band(s):
    return "BID" if s >= 75 else "BID WITH CONDITIONS" if s >= 60 else "RESHAPE" if s >= 45 else "NO-BID"
ORDER = ["NO-BID", "RESHAPE", "BID WITH CONDITIONS", "BID"]

def check(rows):
    out = []
    for r in rows:
        r = {k: (v or "").strip() for k, v in r.items() if k}
        n, g, d = r["notice_id"], r["gate_result"].upper(), r["decision"].upper()
        if g == "STOP" and d and d != "NO-BID": out.append(("Q1", n, "a gate stopped it, so the only decision is NO-BID"))
        if g == "PASS" and d:
            try: s = float(r["score_total"])
            except ValueError: out.append(("Q2", n, "decided with no score")); s = None
            if s is not None and d in ORDER and ORDER.index(d) > ORDER.index(band(s)):
                out.append(("Q2", n, "%s is above the band for %g (%s)" % (d, s, band(s))))
        if d and not r["why"]: out.append(("Q3", n, "decision with no reason written"))
        if d and not r["decided_by"]: out.append(("Q4", n, "no decider named"))
        if r["decided_by"] and AGENTS.search(r["decided_by"]): out.append(("Q5", n, "an agent cannot decide: " + r["decided_by"]))
    return out

def main(a):
    if len(a) != 1: print(__doc__.split("\n")[1]); return 2
    try:
        with open(a[0], newline="", encoding="utf-8") as f:
            rd = csv.DictReader(f); rows = list(rd); h = rd.fieldnames or []
    except OSError as e: print("cannot read:", e); return 2
    miss = [c for c in NEED if c not in h]
    if miss: print("missing columns:", ", ".join(miss)); return 2
    res = check(rows)
    for c, n, m in res: print(c, n, m)
    print("%d pursuits, %d findings" % (len(rows), len(res)))
    return 1 if res else 0

if __name__ == "__main__": sys.exit(main(sys.argv[1:]))
~~~

## Worked example (fictional)

~~~csv
notice_id,gate_result,score_total,decision,why,decided_by
EX-26-0141,PASS,81,BID,Scope matches two past jobs; capacity free in January,Dana Reyes (owner)
EX-26-0162,PASS,52,RESHAPE,Too large to self-perform; team next cycle,Dana Reyes (owner)
EX-26-0170,STOP,,NO-BID,Over the size standard for the assigned NAICS,Dana Reyes (owner)
~~~

It checks clean. Change the RESHAPE row to BID and run it again: Q2 fires, because 52 only allows RESHAPE.

## Prompt for your agent

"Add this notice as a row in pursuits.csv. Gate it first. If it passes, score it and write the band. Draft the decision and the reason, leave decided_by empty for me, then run pursue_mini.py and show me what it prints."

## What the full bundle adds

The paid bundle carries all four GovCon packs in full (Triage Kit, Gate Checklist, Evidence-Only Capability Statement, Buyer Outreach Screen) and the Pursuit Desk: a twenty-column register for the whole pursuit and pursue_check.py with seventeen checks, including no bid without a clean capability statement and no buyer contact without a clean outreach screen. If you want it set up on your own notices, book a call on the paperchasewebb.ai homepage.
