Instructions for agents working on brick-lead
This brick is built on erp-platform. Read its BRICKS.md before changing anything here — it holds the
rules (ownership, use cases, data, identity, tests) and why each one exists.
Where a change goes
| You are changing… | It goes in |
|---|---|
| a rule about leads, scoring, qualification, conversion or lookalike profiles | src/brick_lead/domain/lead.py — and a test in tests/test_rules.py named for the story |
| who may do something, or which leads they see | src/brick_lead/application/use_cases.py — the permission, or _scope() / _load(write=True) |
| how a neighbour (business-partner, activity, pipeline) is asked, or what its answer means | src/brick_lead/application/neighbours.py — the anti-corruption layer; nowhere else calls a seam |
| a new action for people and agents | a new @LEAD.use_case in application/ — it becomes a route AND a tool |
| a column or table | src/brick_lead/schema.sql (append; never drop or rename) and infrastructure/tables.py |
| the default scoring weights | DEFAULT_POLICY in the domain — but a customer's weights are rows (PUT /scoring-policy) |
Never add an HTTP route or an MCP tool by hand. Never import SQLAlchemy, FastAPI or MCP in domain/.
Use the words in the glossary at the top of domain/lead.py (lead, signal, score, scoring policy,
qualification, conversion, lookalike) in code, docstrings and the history.
Before you commit
pytest -q --tb=short # rules, both doors, and tier 1 with the real bp/activity/pipeline bricks
BUCKET=<scratch> DODIL_ORG=<org> python scripts/live_check.py # after touching SQL, the vector, or scoring
Add a test to tests/test_doors.py for any new use case: the outcome through the API and through MCP, and
the 404 for another person's lead. A change to conversion needs a case in tests/test_suite.py.
Don't
-
Don't open the deal through the pipeline's person route: conversion uses
POST /opportunities/from-sourcewith the lead'ssource_refand the converting person asowner(C2). -
Don't let a half-converted lead's domain change (its partner was made from it), and don't save a lead without its loaded version — an import claims every lead's next version in bulk (
save_many). -
Don't write to another brick's tables — call it through
application/neighbours.py. -
Don't type a score: it is derived by
score()from the lead, its signals and the policy. -
Don't mint a lead id: it is derived from the email or domain (
LeadKey), so a re-import is idempotent. -
Don't write an upsert or DELETE by hand — use the repository (DataK3's ON CONFLICT differs from Postgres).
-
Don't commit credentials or
.env.