← Resources · Product · Workflow

Catalog → funnel

A raw list of providers is not a catalog. This is the SOP I use to turn one into the other — and to keep it correct after the tenth re-run, when the list has changed and nobody remembers what was edited by hand.

The workflow assumes one thing: a file, not a database, is the source of truth. Everything else follows from that. You edit the file, run the tool, and the catalog matches the file. No admin panel, no half-remembered manual fixes, no drift.

The shape

intake file  →  validate  →  dry-run  →  write  →  verify
 (you edit)    (no network)  (no writes)  (commits)  (reads back)

Four gates before anything lands. Each one fails on a different class of mistake, so a broken row never reaches the public catalog.

1 · The intake file

One structured file — YAML or CSV — with one entry per provider. Keep it human-editable: the person curating the list is not going to write SQL.

- name: Example Legal          # natural key — unique and stable
  status: draft                # draft = invisible; active = published
  geo: [ES-CT, PT-30]          # where they actually operate
  services:
    - goal_type: residency     # slug from a fixed taxonomy
      title: Residence permit support
      price_from: 450
  verification:
    checked: 2026-07-14
    by: manual

Three rules that save the most pain

2 · Validate — structure only, no network

The cheapest gate. Runs offline, catches: duplicate keys, missing required fields, malformed dates, unknown taxonomy slugs. Run it after every edit — it costs a second and it means the next three steps are about real problems.

If validation needs a database connection, it isn't validation. It's a deploy with extra steps.

3 · Dry-run — connect, resolve, print the plan

Connects, resolves taxonomy slugs against the live list, prints exactly what would change — and writes nothing:

+ create  Example Legal        (2 services, 1 geo)
~ update  Northern Notary      (services 3 → 2)   ← check this
- orphan  Old Partner Ltd      (in DB, not in file)

The line to read carefully is the middle one. Creates are easy to see; a quiet removal — a service that fell out of the file because someone deleted a line — looks like nothing at all until it's gone from the catalog. Making the diff visible before the write is the whole point of this step.

4 · Write

Same command, minus the flag. It runs with elevated credentials because it bypasses the read-only rules that protect the public catalog — so it stays in your hands, never in the app's.

5 · Verify — read it back the way a visitor would

Not "the tool exited 0". Query the catalog through the public path — the same permissions a stranger has — and confirm:

Where the funnel starts

The catalog is the top of the funnel, and that only works if it's honest. Two things make the difference between a directory and a funnel:

The mistake worth naming

Early on I published a count — "232 providers" — that came from a parsing run, not from the live catalog. It went onto the site, into a deck, into three docs. When the underlying data changed, the number lived on for weeks in places nobody thought to check.

The fix is a rule: a number is either read from the source at render time or it doesn't get published. A hardcoded count is a claim with an expiry date and no label.