Custos turns Mike into a counterparty-first contract platform

Six commits reshape the upstream tabular-review tool into something that thinks in customers and vendors, not just documents.

contract-reviewknowledge-management

Custos is repositioning the fork as a contract-lifecycle product organised around the other side of the deal. Projects now carry a template (vendor, customer, internal) and a role (buyer, seller, mutual), and a new Customers view groups everything by counterparty with role tabs and an editable counterparty field on each project.

The arc is the interesting part. It starts as free-text typed by humans. Then an LLM quietly reads each uploaded document on the way in and fills the counterparty for you, without overwriting anything you set by hand. A merge tool cleans up the inevitable "Adobe" vs "Adobe Inc." duplicates. Finally, the system stops trusting the project-level label at all and derives counterparty membership from the documents themselves - so a single "Customer Contracts" project containing Adobe, Stripe and Intercom paperwork shows up under all three customers, with a per-counterparty timeline of everything they've ever sent or signed.

So what Worth a look for anyone building or buying CLM: it's a clean sketch of how a document tool becomes a relationship tool.

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