Custos gives legalos a memory for who you're dealing with

Six commits turn a document tool into a contract lifecycle system organised around the counterparty, not the file.

contract-reviewknowledge-management

Custos has built a core that classifies every project by type - vendor, customer, or internal - and by which side of the deal you're on. From there, the fork indexes everything by counterparty: a single page lists every party you've dealt with, how many contracts you have with each, and when you last touched them. Click a name and you get a chronological timeline of every project, document, and extracted fact tied to that party.

The clever part is that it fills itself in. When a document lands, the fork quietly reads the opening pages, asks an AI model to name the counterparty, and writes it back - but only if no one has set it manually. A merge tool collapses duplicates like "Acme Inc." and "Acme, Inc." into one canonical name.

So what In-house and legal-ops teams who want a contract picture organised by relationship, not by folder, should watch this one.

View this fork on GitHub →

Spotted something wrong? Or know the PR text has fresher detail than the writeup above?

Commits in this thread

6 commits from Custos/legalos, oldest first. Source extracted verbatim from the harvested git log.

SHA Subject Author Date
fa1a3bbb Project templates: vendor / customer / internal classification Custos 2026-05-04 ↗ GitHub
commit body
- Add projects.template (slug) + projects.role (buyer/seller/mutual).
- New PROJECT_TEMPLATES registry in backend (vendor, customer, internal).
- GET /projects/templates returns the registry.
- POST /projects accepts template; backend resolves slug → role.
- New Project modal: type picker pills below the CM number.
- Projects overview: filter chips by template type.
e7a3d0c9 Counterparties: per-project counterparty + customer index page Custos 2026-05-04 ↗ GitHub
commit body
- projects gain counterparty + parent_counterparty (free text for now;
  will be promoted to a dedicated counterparties table in a follow-up).
- PATCH /projects/:id accepts counterparty / parent_counterparty / template.
- New GET /projects/counterparties?role= aggregates by counterparty,
  returning project counts and last-activity for the customer index.
- Frontend: new /customers route grouping customer-template projects by
  counterparty, with role tabs (Customers / Vendors / All).
- Frontend: editable counterparty + parent fields on the project page,
  template badge surfaced under the title.
- Sidebar: 'Customers' nav entry below Projects.
- .gitignore: exclude .claude/.
5d6cc8e1 Counterparties: auto-extract from uploaded documents Custos 2026-05-04 ↗ GitHub
commit body
- New lib/counterpartyExtraction.ts: maybeAutofillCounterparty() runs an
  LLM call on the doc's text to identify the counterparty (and parent
  entity) using the project's role hint (buyer/seller/mutual).
- Move extractPdfMarkdown/extractDocxMarkdown to lib/textExtraction.ts so
  both tabular review and counterparty extraction share them.
- Document upload fires the extractor fire-and-forget for templated
  projects with no counterparty set. Never overwrites manual values.
ce650956 Counterparty merging in the customer index Custos 2026-05-04 ↗ GitHub
commit body
- POST /projects/counterparties/merge {from, to} reassigns every project
  the caller owns where counterparty matches (case-insensitive trim) to
  the target name.
- Customer index: small merge icon on each row opens an inline picker;
  pick a target counterparty and confirm. Refreshes after merge.
11895ef6 Counterparty timeline page Custos 2026-05-04 ↗ GitHub
commit body
GET /projects/counterparties/:name/timeline returns every accessible
project for one counterparty plus the documents and contract_facts tied
to those projects. New /customers/[name] route renders them on a
vertical chronological timeline with per-project metadata (effective
date, term, value), the executed/draft/etc. status of each document,
and a counterparty-level total active value.

Customer index rows now link into the detail page (except the
"(Unassigned)" group).
dd8e28e2 Projects can span multiple counterparties Custos 2026-05-04 ↗ GitHub
commit body
Projects are now treated as free-form buckets - counterparty membership
is derived from the documents in the project (intake_counterparty),
plus an optional projects.counterparty as a "primary" hint. A project
named "Customer Contracts" with Adobe + Stripe + Intercom docs now
appears under each of those three counterparties in /customers.

- GET /projects/counterparties: project rows pull every distinct
  intake_counterparty across the project's docs; project_count is
  incremented per-counterparty. Role filter applies at the document
  level so a multi-cp project surfaces correctly.
- GET /projects/counterparties/:name/timeline: includes any project
  whose docs reference this counterparty (not just primary-counterparty
  matches). Document list is scoped to docs that actually belong to
  this counterparty (or all docs when this IS the project's primary).

Capture this thread into my fork

Download a single Markdown prompt that tells Claude how to port every commit above into your working tree — adapting paths and structure to match your repo. Run it via claude -p < capture-thread-42.md from inside the repo you want the changes in.

⬇ Download capture-thread-42.md