# Prevention and the monthly loop

A cleanup that does not change the writer comes back next month. Every FAIL and every recurring WARN in the report needs a creation path, a source fix and an owner.

## Finding the creation path

- HubSpot: "Record source" (Import, Integration, Forms, CRM UI and the other values on HubSpot's change sources page) and "Record source detail 1" (for an import, the import name), plus "Original Traffic Source" for older records. Event badge scans arrive as Record source Import; tell them apart from list imports by the import name in Record source detail 1. Property history on a sampled record shows which writer set a bad value.
- Salesforce: CreatedById (an integration user means an integration), LeadSource, and the field history if tracked. Duplicate record sets show which rule caught what.
- Attio: created-by actor on the record, mailbox and calendar sync for people created from email, import names.
- The profiler copies the source or created-by value into duplicates.csv and profile.json, and trend.md groups new duplicate groups by it. It reads one column per object: to group by import name, rerun with `--map contacts.source="Record source detail 1"`.

## Defect to source fix

- Duplicate companies from an integration. Path: companies created through the HubSpot API, sync apps included, are not deduplicated by domain. Fix: write a unique source ID property, search before create, send unmatched records to a review queue. Monitor: new D01 and D05 groups whose newest member came from that integration.
- One person split across work and personal email or event scans. Path: imports without email or a unique key; forms creating contacts for unknown emails. Fix: collect and upsert on a product user ID; import with Record ID or a unique property; review the form's handling of unknown emails. Monitor: new D04 pairs by source.
- Salesforce duplicates from imports and API batches. Path: records saved in the same operation are not compared with each other by block or alert rules. On data import tools and APIs an alert rule shows no alert and refuses the save. A REST client can send `allowSave=true` in the Duplicate Rule header to save anyway. Report rules do compare records saved together. Fix: dedupe the file before loading; duplicate rules with the Report option; duplicate jobs where the edition allows (Performance and Unlimited). Monitor: duplicate record items created per week.
- Salesforce duplicate rules that never ran. Path: rules skip lead conversion without Apex Lead Convert, manual merges, undelete, Quick Create, Community Self-Registration, Lightning Sync and Einstein Activity Capture. Fix: ask Salesforce Customer Support to turn on Apex Lead Convert, and test each path. Monitor: duplicate groups whose members share a creation timestamp and creator.
- Duplicate deals. Path: double form submissions, integration retries, reps re-creating deals. Fix: a unique source opportunity key; in Attio, a unique attribute on deals. Monitor: D06.
- Records owned by people who left. Path: offboarding without reassignment. Fix: reassign before deactivating (HubSpot recommends it), remove leavers from meeting pages and rotations (HubSpot keeps booking deactivated users on group and round-robin pages), and add both to the offboarding checklist. Monitor: O02 monthly.
- False customers. Path: copied forms or imports setting lifecycle Customer. Fix: review form lifecycle settings and import mappings; correct with clear_then_set. Monitor: new L01 records by source.
- Bounced or unsubscribed but still marketable. Path: suppression not synced across sending tools; merges keeping the most marketable status. Fix: suppression sync (preferences-caps-and-suppression if installed) and consent review in every merge. Monitor: E06 and E07 weekly after a FAIL.
- Formatting regressions. Path: a writer reintroducing bad formats; HubSpot's data quality tool does not flag a record again after a later bad write. Fix: change the writer's mapping or validation. Monitor: "new on records that existed last time" in trend.md.
- Spreadsheet damage. Path: exports opened and re-saved in a spreadsheet before import. Fix: never round-trip exports or import files through a spreadsheet; keep IDs as text. Monitor: X02, X03, F05.
- Enrichment overwriting or creating records. Path: enrichment and prospecting tools writing back. Fix: hand off to clay-to-crm-writeback if installed, and to martech-integration-engineering for the sync repair. Monitor: D05 and A04 by source.

## Vendor monitors worth switching on

- HubSpot: duplicate alerts with a daily limit and notifications (Data Hub Professional and Enterprise), the weekly data quality digest, property insights.
- Salesforce: duplicate rules with the Report option feeding duplicate record sets and reports; duplicate jobs on Performance and Unlimited; duplicate error logs, deleted after 90 days.
- Attio: unique attributes and duplicate prompts; `record.merged` webhooks to keep the crosswalk current.
- These detect; they do not fix the writer. Keep the monthly profile as the measure.

## Recurrence metrics with --previous

- Pass last month's profile.json with `--previous`. trend.md then lists:
  - check deltas, with the rules version and hash of both runs (definition changes are listed first, before any trend claim)
  - records resolved since last time, and records that existed last time but now fail (regressions or newly broken)
  - failing records created since the previous profile: the sign that a writer still creates the defect, even when the count did not move (the December run in [worked-example.md](worked-example.md))
  - record IDs present last month and missing now (merged or deleted)
  - duplicate groups with a member created since the last as-of date, by creation source
  - groups still open from the previous review
- A defect that recurs two months running escalates its source fix ahead of another cleanup.

## Cadence

- Monthly: the full profile and report.
- Weekly, until two clean weeks: E06, O01, O02 and L10 when any of them failed last month.
- Immediately: after an import, an integration change, a migration or a batch of merges.

## decisions.md entries to propose

Propose, never write without approval:

- Account boundary (legal entity, billing account or business unit) and the date it was decided.
- Unique keys per object, and any key demoted because it was not unique.
- Threshold overrides with the reason (for example a 120-day no-activity window for an enterprise pipeline).
- Accepted exceptions by check and record ID (they go into `exemptions` in the rules file).
- Merge policy: who approves, batch size, crosswalk location.
- Required fields and allowed values for `data-dictionary.md`.
