Investment-committee memos with a paper trail. The model extracts. Python does the math. You approve before a sentence is written.
Citework turns a folder of deal documents into a one-page credit memo a committee can actually trust: every reported figure carries a verbatim quote, every ratio is Decimal arithmetic in Python, and a human gate sits between extraction and prose.
A shipped example — a fictional mid-market software LBO — is already in the repo:
| Python, not the model | |
|---|---|
| Net debt at close | $48.0M |
| Forward leverage | 30.0x |
| LTM leverage | N/M (EBITDA is negative) |
| NRR excluding concentrated expansion | 97.8% |
Full memo: output/memo.txt. Evidence: output/numbers.md.
Language models will invent a 4x leverage multiple if you let them do the arithmetic. Citework does not:
- Closed world. Documents are the only source of truth. Outside knowledge is forbidden. File text is untrusted data, never instructions.
- Quotes, then math. Python checks that each quote appears in the named file (whitespace folded, paraphrases fail,
2inside$52Mdoes not count). Then Python computes net debt, leverage, ARR multiple, and NRR. - A real gate. Assumptions, conflicts, gaps, at most three questions, and the verified numbers are on the table before the second call writes the memo.
flowchart LR
docs[docs txt pack]
step1[Extract]
py[Verify quotes and Decimal math]
gate[Human gate]
step2[Write prose]
out[memo numbers report]
ui[Streamlit review]
docs --> step1 --> py --> gate --> step2 --> out
out --> ui
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
python -m unittest discover -v
python run.py --offline --yes
python -m streamlit run app.py--offline replays the captured model responses already in output/raw/. No API key, no network.
For a live run, copy .env.example to .env, set ANTHROPIC_API_KEY, then:
python run.py # pauses at the gate
python run.py --yes # approve without promptingA live run is two calls, typically under a minute. The Streamlit app is artifact-first: opening it does not call the model. A live button exists; it is not the default.
docs/ is a fictional data room for Redwood Software Inc. — CIM excerpt, banker-call notes, internal financials. Drop a different folder of .txt files in and the same pipeline runs. There is no deal-specific logic in the production modules.
The long version is ARCHITECTURE.md. The short version:
- Step 1 extracts named figures with source + quote. It does no arithmetic.
verify.pytraces every quote.calculations.pyderives every ratio inDecimal. A non-positive denominator is N/M, never a negative multiple.output/numbers.mdis written before you approve.- Step 2 is handed the numbers block, not the raw documents, so the prose cannot invent a multiple the table disagrees with.
report.jsonis the structured contract.memo.txtis pasteable plain text (450–650 words, no markdown). Both come from one object.
citework/
├── run.py CLI: extract → verify → compute → gate → write
├── analysis.py Anthropic structured output (two calls)
├── verify.py Quote traceability
├── calculations.py Decimal math, N/M guards
├── render.py One-page memo + numbers.md
├── app.py Streamlit review UI
├── schema.json Output contract
├── docs/ Sample data room
├── output/ Frozen demo artifacts
└── tests/ Offline suite (~70 tests)
MIT. See LICENSE.