Skip to content

Repository files navigation

coldscreen

First-pass deal screening from public sources. One command in, one screening memo out.

coldscreen takes a company name, pulls the public record, and produces the memo a corporate finance analyst would write before anyone agrees to a meeting: registry profile, officer and ownership network, sanctions and PEP exposure, adverse media, and a claims-vs-evidence table showing what the company says about itself against what the public record actually supports.

It is not due diligence. It is the screen that decides whether due diligence is worth anyone's time.

Where this comes from

Type3 Capital is a licensed corporate finance advisory firm. Every inbound mandate goes through the same outside-in screen before we engage: no data room, no management calls, public sources only. In recent months that screen led us to decline two inbound mandates after central marketing claims failed to survive contact with the public record.

This repository is that screen, generalized and automated. A methodology like this gets stronger in public, not weaker, so we open-sourced it.

What it checks

Registry pass. Company profile, incorporation history, officers (current and recently resigned), persons with significant control, filing history, registered charges, insolvency events.

Network expansion. Each officer's other active appointments and any disqualifications. Undisclosed related-party webs show up here.

Sanctions and PEP. Entity, officers, and PSCs matched against OpenSanctions data.

Adverse media. Structured search across fraud, insolvency, regulatory, and litigation queries, with sources and dates recorded.

Claims vs evidence. Discrete, checkable claims extracted from the pitch deck and website, then tested against the record. This table is the point of the whole exercise.

Quickstart

# PyPI publication is pending; install from a checkout until then:
pip install .
export COMPANIES_HOUSE_API_KEY=...  # free key from the Companies House developer hub
export ANTHROPIC_API_KEY=...        # or OPENAI_API_KEY, or point COLDSCREEN_MODEL at local Ollama

coldscreen screen "Acme Holdings Ltd"

Sanctions and adverse media need their own keys (OPENSANCTIONS_API_KEY, TAVILY_API_KEY). Without them those stages do not run, and the memo says so explicitly: skipped coverage is recorded as findings, never silently passed.

With a deck and a website:

coldscreen screen 01234567 --deck pitch.pdf --site https://example.com

Output lands in cases/acme-holdings-01234567/: the memo, a fetch_log.json of URLs, sanitized params, timestamps, and cache flags (no bodies, no keys), plus every piece of raw evidence as JSON with source URLs and retrieval timestamps.

Registry pages are cached locally for up to seven days. To refetch and update that cache:

coldscreen screen 01234567 --refresh

--refresh still writes successful HTTP 200 responses back into the cache, so the next ordinary screen sees the new pages. coldscreen rerun does not fetch and has no --refresh flag.

For a JSON-only pipeline that must not leave an audit pack on disk:

coldscreen screen 01234567 --no-write

--no-write prints the casefile JSON to stdout (same as --json) and does not create a case directory. The HTTP cache is unchanged: successful 200 responses are still stored. --json without --no-write still writes the case directory. coldscreen rerun has no --no-write flag.

Inspect or erase the HTTP cache (no API key needed). Print the configured path rather than guessing a machine-local location:

coldscreen cache path
coldscreen cache stats
coldscreen cache clear

cache stats reports path, existence, entry count, size, and TTL. It never prints URLs or bodies. cache clear empties the configured sqlite file only; a symbolic link at that name is refused.

coldscreen check-language scans memos and casefile statements.

Example output (abridged, fictional company)

This excerpt is genuine pipeline output: it is the repository's golden test case, a fictional company with planted contradictions, regenerated byte-identically by the test suite on every run.

# Screening memo: FABRICATED WIDGETS LTD

## Verdict

**RED**

Triggered:

- **R4** (RED): Material claim directly contradicted by registry record
- **A1** (AMBER): Overdue or irregular filings
- **A2** (AMBER): Wholesale officer changes within 12 months
- **A4** (AMBER): Central claim unverifiable from any public source

## Claims vs evidence

| # | Claim (source) | Public record | Status |
|---|---|---|---|
| 1 | "Operating since 2015 with a national footprint" (deck p.2) | Incorporated on 2019-05-14 per the registry profile, four years after the claimed start. | Contradicted |
| 2 | "The company is debt free and self funded" (deck p.2) | The charges register lists 2 charges, 1 outstanding. | Contradicted |
| 3 | "Our platform eliminates fraud in widget procurement" (deck p.2) |  | not checkable |
| 4 | "A team of 40 widget engineers" (deck p.3) | No public source in this casefile states headcount. | Unverified |
| 5 | "The most trusted name in widgets" (deck p.3) |  | not checkable |

Row 3 is worth a note: the company's own marketing words render verbatim in the table, while the tool's prose never uses accusatory language. That distinction is enforced mechanically, not stylistically; see DECISIONS.md for the design.

How verdicts work

Verdicts follow a published rubric (rubric.md), version-controlled with the code so the logic is auditable.

RED: sanctions or PEP match on the entity or its PSCs; active insolvency; a disqualified director in the current officer set; a material claim directly contradicted by the registry (including an origin year that predates incorporation, which is detected mechanically); an undisclosed related-party network around the officers.

AMBER: overdue or irregular filings; recent wholesale officer changes; registered charges inconsistent with the stated capital story; central claims that no public source can verify; corporate age or scale inconsistent with the stated history; substantive adverse media from a confirmed source; a company status that is not active (dissolved, closed, converted-closed, or removed; insolvency states escalate to RED instead).

GREEN: clean registry, claims supported or plausibly verifiable, no substantive adverse media.

A verdict is an opinion generated from public sources at a point in time. It is a reason to ask better questions, not a substitute for judgment.

Evidence discipline

  • Every finding carries a source URL and a retrieval date. A finding without evidence fails validation and is dropped.
  • Absence is recorded. "No insolvency notices found" is a finding, not silence.
  • Every finding carries a confidence tag: confirmed, indicated, or unverified.
  • Facts are fetched by code. The model synthesizes and drafts, but it never supplies a registry fact from its own memory.
  • Public sources only. Nothing behind a login, no scraping in breach of a site's terms.

Models

Works with hosted models (Anthropic, OpenAI) or local models via Ollama. The deterministic collection layer is identical regardless of model; only synthesis quality varies. Run it fully local if your deal flow should never touch a third-party API.

Verdict discipline follows rubric rule 4: mechanically detected triggers cannot be dropped by the model, and triggers whose evidence conditions are not met cannot be added by it. Every trigger's evidence condition is published in rubric.md (currently version 0.3), and every enforcement intervention is recorded in the casefile and the memo. Origin-year claims that predate the registry incorporation date are a mechanical R4 candidate: the model cannot drop that red. The model's judgment operates inside those conditions; the level arithmetic is never its to change. In cross-model testing, a 30B coder model and a 27B generalist produced identical verdict levels on the same live companies. One practical note for local models: reasoning-family models (qwen3 non-coder variants and similar) need COLDSCREEN_OLLAMA_THINK=false for reliable structured output.

MCP server mode (stdio)

coldscreen speaks the Model Context Protocol over stdio, so the same pipeline runs inside agent workflows. It ships as an optional extra:

pip install '.[mcp]'   # or 'coldscreen[mcp]' once the package is on PyPI
coldscreen mcp         # serves on stdio; stdout is JSON-RPC only

Two tools, and only two:

  • screen_company(query, company_number, deck_path, site_url, model, overwrite, refresh, no_write) runs the full screen and writes a case directory. It returns the company name and number, the enforced verdict level with its rubric trigger ids, the case directory path, and the memo markdown. refresh skips HTTP cache reads and still writes successful 200 responses back. no_write skips the case directory for that run; the payload still carries the memo and verdict, and case_dir is JSON null. The HTTP cache is unchanged.
  • rerun_case(case_dir, model, render_only) re-synthesizes from a case directory that already exists, without refetching anything.

Three properties worth knowing before you wire it up:

  • Keys never travel as tool arguments. Every key comes from the server process environment, which the host sets. No tool has a field for one, and a missing COMPANIES_HOUSE_API_KEY comes back as a plain error naming the variable.
  • Ambiguity is never resolved for you. A company name matching several companies returns a candidate list with status ambiguous and writes nothing. Choose one and call again with company_number. There is no picker on this path and nothing is guessed.
  • rerun_case is confined to the configured output directory (COLDSCREEN_OUTPUT_DIR, default cases/), because on this path the directory is chosen by a host rather than typed by a person.

Most hosts configure stdio servers with a command, args, and env block. The file and the exact top-level key differ per host, so check your host's documentation; the block itself looks like this, with your own values in place of the empty strings:

{
  "mcpServers": {
    "coldscreen": {
      "command": "coldscreen",
      "args": ["mcp"],
      "env": {
        "COMPANIES_HOUSE_API_KEY": "",
        "COLDSCREEN_MODEL": "",
        "COLDSCREEN_OUTPUT_DIR": ""
      }
    }
  }
}

Connecting this server to a hosted MCP host means memo content, including officer names from the register, is sent to that host. PRIVACY.md covers what flows where. Streamable HTTP and a hosted MCP deployment are not built; stdio is the shipped transport.

Scope and non-goals (v0.1)

  • UK companies only. A 2026-08-19 official-docs pass found neither planned candidate is a second Companies House: Bolagsverket HVD is number-only identity lookup, and SEC EDGAR is a filings archive for SEC filers, not a register. The adapter interface stays open until a real second register forces it (see FUTURE.md and DECISIONS.md).
  • No financial statement analysis. Filings are inventoried, not parsed.
  • No people search beyond registered officers and PSCs.
  • No continuous monitoring. One screen, one memo.
  • No data room ingestion. Other open projects cover post-engagement document review; this tool runs before any documents change hands.

Data sources

  • Companies House (UK registry): free API, key required. Contains public sector information licensed under the Open Government Licence v3.0; you are bound by their reuse terms, and officer data remains personal data.
  • OpenSanctions: sanctions, PEP, and watchlist data, licensed CC BY-NC 4.0. OpenSanctions treats use inside a for-profit business, including compliance screening that earns no revenue, as commercial use requiring a paid licence from them. This tool bundles no key: you bring your own key under your own licence relationship, and this repository's MIT licence does not extend to their data.
  • Web search: via your model provider's search tool or a pluggable search API.

Disclaimer

coldscreen is a research aid. It is not investment advice, not a credit reference, and not a consumer report. Do not use it for decisions regulated under the US Fair Credit Reporting Act (employment, credit, tenancy screening) or equivalent regimes. Officer data is personal data sourced from public registers; you are responsible for processing it lawfully in your jurisdiction. Verify everything independently before acting on it. Memos describe what the public record shows and with what confidence; they do not make accusations.

Roadmap

  • Swedish number-only identity lookup (Bolagsverket HVD) and a US filings archive (SEC EDGAR), not a second Companies House. Adapter interface still open. See FUTURE.md.
  • A versioned JSON schema contract for pipelines (the --json flag exists today; the schema guarantee is the future work)
  • Streamable HTTP for MCP, and a hosted MCP deployment (the stdio server is built; see above)
  • Better deck parsing (tables, charts)

Contributing

Issues and PRs welcome. A new jurisdiction is valuable when it is a real company register that can force an adapter contract. That interface is not extracted yet; do not send a Protocol wrapper around the UK client. See ARCHITECTURE.md section 17 and DECISIONS.md. Maintainers use uv sync --locked --extra dev; uv.lock is tracked. Users still pip install ..

License

MIT. Data sources retain their own licenses.

Maintainer

Maintained by @samrusani and the team behind Type3 Capital.

About

First-pass screening memos for UK companies from public records. One command in, one evidence-backed memo out.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages