# Writing the fix list

The audit changes nothing. Fixes leave as a written list that the owners of models, connectors, dashboards, CRM properties and syncs apply after approval. Template: [../assets/fix-list-template.md](../assets/fix-list-template.md).

## Rules

- One root cause per fix. "Pipeline overstated" can be two fixes (fan-out and deleted deals) when they need different changes and tests; it is one fix when one model rebuild removes both and one test proves both.
- Order by priority (P1 to P4 from score_audit.py), then by value at stake, then by the number of outputs affected.
- Every fix names: the finding IDs it closes; the entity (model, connector, property, registry, dashboard, sync) by its exact name; the current state with evidence; the change; the owner who applies it and the person who verifies it; the test that proves it, with the expected result; the expected effect on reported numbers (direction, size, periods); any backfill or re-sync with scope and cost driver; the restatement notice; the approver; the rollback.
- A test may depend only on its own fix. If it needs another fix first, say so under "Depends on".
- A fix that lowers a reported number (fan-out removed, deleted rows excluded, platform claims replaced by business totals) says so, and names who tells the report's users before release, so a correction is not read as a performance drop.
- A re-sync or backfill rewrites history and can cost money (connector usage, bytes processed). Draft it with its scope and cost driver; a named owner runs it after approval.
- An activation or upload output with a P1 item gets a "pause until fixed" recommendation for its owner to decide. The agent never pauses, re-syncs, rebuilds or edits anything.
- Hand-offs: when a sibling skill owns the repair, name it and the evidence to hand over (tracking-audit for click ID capture, utm-convention-and-registry for the registry, purchase-reconciliation for orders, crm-data-hygiene-audit for CRM records, crm-audit for CRM configuration and field writers, martech-integration-engineering for a sync or upload's keys, retries, removals and gates). When it is not installed, name the vendor tool instead.

## Example entries (fictional Acme Cloud, audit run 2026-11-01)

```markdown
### FX-1 Restore the missing Google Ads days and stop zero-filling (P1; FRS-03 FAIL F03, FRS-07 WARN F11)
- Entity: Fivetran connection google_ads (customer 1234567890); dbt model fct_paid_media_daily.
- Current state: stg_ads__daily_spend has no rows for 2026-10-10, 2026-10-11 and 2026-10-12
  (Q04 missing_count 3; reconcile_exports.py missing_in_b 3, platform spend 6,900.00 USD).
  fct_paid_media_daily joins a date spine with COALESCE(spend, 0), so Paid media daily shows
  0.00 spend on those days.
- Change: 1) re-sync the connection's report tables for 2026-10-10 to 2026-10-12;
  2) join the spine to a load manifest (connector sync log or a loaded-days table) and leave
  unconfirmed days NULL, shown as "not loaded" on the dashboard.
- Owner: Analytics engineer. Verifies: Paid media lead.
- Test: Q04 shows 28 of 28 days for 1234567890; reconcile_exports.py on the same exports shows
  0 missing keys and the google_ads 2026-10 rollup within 0.5%; a day removed from a dev copy
  of staging shows as "not loaded", not 0.00.
- Expected effect: October Google Ads spend +6,900.00 USD; cost per trial for 2026-10-10 to
  2026-10-12 moves from undefined to real values; O1 and O5 restate October.
- Backfill: a re-sync of three days on one connection; cost driver is connector usage (rows
  re-synced). Approver: Marketing ops lead.
- Restatement notice: Paid media lead tells dashboard users that October spend rises by 6,900.00.
- Rollback: revert the model change in version control; the re-synced rows stay correct.

### FX-2 Rebuild pipeline at deal grain without deleted deals (P1; KEY-04 FAIL F16, KEY-06 FAIL F19, closes OPS-01 FAIL F54)
- Entity: dbt model mart_pipeline_by_source; source fivetran_hubspot.deal.
- Current state: Q08 shows 1,496,000.00 USD in the mart against 1,310,000.00 at deal grain with
  the same filters (+186,000.00 from joining deals to every associated contact). Q09 shows 12
  deals with _fivetran_deleted true worth 94,000.00 counted in the mart. The model has no tests.
- Change: aggregate to one row per deal before joining source attributes (take source from the
  primary contact by a documented rule); filter _fivetran_deleted = false; add
  unique and not_null on deal_id and relationships to the deals fact.
- Owner: Analytics engineer. Verifies: RevOps lead.
- Test: Q08 difference 0.00 and deal counts equal; Q09 deleted_deals_in_published 0;
  check_dbt_artifacts.py shows a grain test on mart_pipeline_by_source.
- Expected effect: pipeline falls from 1,496,000.00 to 1,216,000.00 USD (-280,000.00:
  186,000.00 fan-out and 94,000.00 deleted deals); pipeline by source shares change for every
  source with multi-contact deals. Weekly pipeline reports restate from the model's first date.
- Backfill: a full rebuild of one table; cost driver is bytes processed. Approver: RevOps lead.
- Restatement notice: RevOps lead tells the pipeline review attendees before the next Monday
  review; the change is a correction, not a pipeline drop.
- Rollback: revert the model version. Does not undo: decisions already taken on the old numbers.
```

## Release evidence to ask for

Close each fix with three answers:

- Did the configuration change as written? (pull request, connector setting screenshot, sync configuration)
- Do the checks that found it now pass on a frozen period? (the same queries and scripts, same window, attached output)
- Does the consumer show the corrected number? (the dashboard tile, the audience size or the upload count, with its data-through date)

A yes to the first question does not stand in for the other two. Re-run the targeted checks after release; the targeted counts must move and no other check may worsen.
