executing-plans
executing-plans
Use when executing an implementation plan in the current session as the implementer yourself — your human partner chose inline execution, or no subagent tool is available
seo-directory-backlinksDirectory and backlink register: platform, domain, target URL, follow attribute, authority and spam scores, approval and NAP-match status. Use for SEO citation tracking.
Independent GitHub snapshot · Automated classification · Original documentation governs
What it is: the off-site citation and link plan - which directory or platform the business belongs on, whether the listing is real and consistent, what the link is worth, and which submissions to refuse.
Works out the smallest useful citation plan for the business in front of it, then builds it only when asked. The default output is a short recommendation, not a submission list. The citation tracker - CSV, SQL DDL, JSON Schema, Notion mapping - is produced on request, from one field list so the four cannot drift apart.
Layer: Layer 3: Acquire. Fits: Growth stage. Table code: n/a.
The rule this table exists to enforce: a directory is a citation, and a citation is
only correct if it matches. NAP Match is therefore a control field with three values,
not a note, and a citation whose NAP does not match is a defect to fix rather than a
backlink to keep. Alongside it, Approval Days and Link Type are recorded because a
listing that was never approved and a nofollow link are two different facts, and a tracker
that cannot distinguish them reports success it did not get.
The second rule: the highest-value links are almost never directories. Chamber of
commerce, council directory, trade association, an industry blog and one genuine local
press mention beat two hundred generic submissions. Tier exists so the plan spends its
effort at the top.
Do not use it for: the website's own structure (business-website-setup), the map profile
(gbp-local-seo-intent), paid advertising, or content marketing and guest posting, which
are editorial decisions rather than directory submissions.
Follow the shared execution contract. The module-specific rules below define only domain fields, decisions, calculations, and safety constraints.
Read the request and pick the intent before asking anything.
Ask only if this is the highest-value missing fact; otherwise proceed without an opener:
Q: What is the business called, and what does one line of it actually do?
Treat ambiguous replies as unanswered and ask which explicit option the user means. Record unknown values as Unknown; Unknown is not zero. A record must not be Done when a required check fails.
Skip anything already answered. Ask the rest one at a time, and stop as soon as the remaining answers would not change the directory list.
Never invent an answer. Domains, DR or DA scores, spam scores, approval times, listing
URLs, referring domain counts and rankings the user has not supplied or measured are
Unknown. A DA score is a third-party estimate, and it is never invented here.
module: seo-directory-backlinks
intent: null # set up | fix | review | report | import
scale: null # Starter | Growth | Scale, only if the answer changes it
areas:
"Business": null
"Current": null
"Goal": null
"Capacity": null
"Risk": null
requested_outputs: []
confirmed_facts: []
open_questions: []
Build an already requested artifact without asking again. For advice-only requests, give a short recommendation and offer the relevant artifact.
Recommended approach: Fifteen to twenty real listings, not two hundred. Tier 0 the two map platforms the business is eligible for, Tier 1 the high-authority review platforms and the professional profile that fit the sector, then the genuinely local sources - chamber of commerce, council directory, trade association, one industry blog, and one local press angle. Everything submitted in one sitting, from one canonical NAP block, and re-checked quarterly. Two or three vertical directories, no link networks.
Why this one: Fifteen real listings are a citation set Google can read and customers can use. Two hundred junk listings are a footprint across domains Google has publicly actioned, and a small local business cannot afford that risk for a marginal gain. The genuinely local links are also the ones competitors skip, so the effort goes further there than on a national directory.
Workflow: Canonical NAP block written once → Tier 0 platforms claimed → Tier 1 review and professional profiles completed → Local chamber, council and trade association joined → Two or three vertical directories chosen by relevance → Tracking row created for each with a review date → Quarterly re-check scheduled → Referring domains and anchor text reviewed once a quarter → Anything with a mismatched NAP corrected or removed
Once the user asks for it, derive the fields from the confirmed context and emit the requested artifacts. For machine-readable text, keep prose outside the data; for files, provide a usable link. Report material validation failures or limitations separately.
A selected Notion output is rendered by notion-manual-import, so route the
Notion step there. When the user selects Notion, hand that step to
@notion-manual-import: it holds the CSV, the property
mapping, the import steps and the verification checklist, and it renders the Field
Reference below instead of defining a table of its own. Do not restate the mapping
here and do not improvise the import steps. Manual CSV and mapping outputs need no
connection. For requested workspace changes, follow the shared contract: verify actual
tool access and the target before writing. A user saying "connected" is not tool evidence.
Never ask for a Notion password or token.
Citation ID,Platform Name,Domain,Tier,Category,Directory Type,Target URL,Link Type,Follow Attribute,Domain Authority,Domain Rating,Spam Score,Listing Status,Submitted Date,Approved Date,Approval Days,NAP Match,Referral Traffic,Review Date,Owner,Status,Notes
,Example Chamber of Commerce,examplechamber.org,Tier 1,Local chamber of commerce,Member directory,https://example.com/,Homepage,Nofollow,Unknown,Unknown,Unknown,Approved,2026-09-27,2026-10-02,5,Match,Unknown,2026-12-27,Unknown,Draft,Example row - replace every value before use.
-- Engine assumption: PostgreSQL. For another engine use the engine's auto-increment
-- equivalent and keep the rest portable.
CREATE TABLE seo_citation (
citation_id BIGINT PRIMARY KEY,
platform_name VARCHAR(255) NOT NULL,
domain VARCHAR(255),
tier VARCHAR(50) NOT NULL,
category VARCHAR(100) NOT NULL,
directory_type VARCHAR(100) NOT NULL,
target_url TEXT,
link_type VARCHAR(50) NOT NULL,
follow_attribute VARCHAR(50) NOT NULL,
domain_authority VARCHAR(50),
domain_rating VARCHAR(50),
spam_score VARCHAR(50),
listing_status VARCHAR(50) NOT NULL,
submitted_date DATE,
approved_date DATE,
approval_days INTEGER,
nap_match VARCHAR(50) NOT NULL,
referral_traffic VARCHAR(50),
review_date DATE,
owner VARCHAR(255),
status VARCHAR(50) NOT NULL,
notes TEXT,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
CONSTRAINT seo_citation_tier CHECK (tier IN ('Tier 0','Tier 1','Tier 2','Tier 3','Tier 4','Tier 5','Tier 6','Tier 7','Refused')),
CONSTRAINT seo_citation_link_type CHECK (link_type IN ('Dofollow','Nofollow','Redirect','JavaScript','No link','Unknown')),
CONSTRAINT seo_citation_nap_match CHECK (nap_match IN ('Match','Mismatch','Unknown','Not applicable')),
CONSTRAINT seo_citation_listing_status CHECK (listing_status IN ('Not started','Submitted','Pending review','Approved','Rejected','Removed','Superseded')),
CONSTRAINT seo_citation_approval_days CHECK (approval_days IS NULL OR approval_days >= 0),
CONSTRAINT seo_citation_approved_after_submitted CHECK (approved_date IS NULL OR submitted_date IS NULL OR approved_date >= submitted_date)
);
CREATE INDEX idx_seo_citation_status ON seo_citation (status);
CREATE INDEX idx_seo_citation_tier ON seo_citation (tier);
CREATE INDEX idx_seo_citation_nap ON seo_citation (nap_match);
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "SEO Directories & Backlinks",
"type": "object",
"additionalProperties": false,
"properties": {
"Citation ID": { "type": "integer" },
"Platform Name": { "type": "string" },
"Domain": { "type": "string" },
"Tier": { "type": "string" },
"Category": { "type": "string" },
"Directory Type": { "type": "string" },
"Target URL": { "type": "string", "format": "uri" },
"Link Type": { "type": "string" },
"Follow Attribute": { "type": "string" },
"Domain Authority": { "type": "string" },
"Domain Rating": { "type": "string" },
"Spam Score": { "type": "string" },
"Listing Status": { "type": "string" },
"Submitted Date": { "type": "string", "format": "date" },
"Approved Date": { "type": "string", "format": "date" },
"Approval Days": { "type": "integer" },
"NAP Match": { "type": "string" },
"Referral Traffic": { "type": "string" },
"Review Date": { "type": "string", "format": "date" },
"Owner": { "type": "string" },
"Status": { "type": "string" },
"Notes": { "type": "string" }
},
"required": [
"Platform Name",
"Tier",
"Category",
"Directory Type",
"Link Type",
"Follow Attribute",
"Listing Status",
"NAP Match",
"Status"
]
}
| CSV column | Notion property | Set after import |
|---|---|---|
| Citation ID | Text (preserve source ID) | Keep imported IDs as Text; optionally add a separate Unique ID property |
| Platform Name | Title | Use as the database title |
| Domain | Text | Leave as Text. The directory's own domain, not the business's |
| Tier | Select (add options after import) | Convert to Select, add options: "Tier 0", "Tier 1", "Tier 2", "Tier 3", "Tier 4", "Tier 5", "Tier 6", "Tier 7", "Refused" |
| Category | Text | Leave as Text. Sector or vertical, in customer words |
| Directory Type | Select (add options after import) | Convert to Select, add options: "Map platform", "Review platform", "Professional profile", "Company profile", "Local body", "Government register", "Industry directory", "Niche directory", "Document sharing", "Bookmarking", "Q&A and forum", "Classifieds", "Link hub", "News service", "Guest post", "Paid placement", "Link network" |
| Target URL | URL | Convert to URL. The exact page that carries the link, not just the domain |
| Link Type | Select (add options after import) | Convert to Select, add options: "Dofollow", "Nofollow", "Redirect", "JavaScript", "No link", "Unknown" |
| Follow Attribute | Select (add options after import) | Convert to Select, add options: "Follow", "Nofollow", "Sponsored", "UGC", "Unknown" |
| Domain Authority | Text | Leave as Text. Record the value and the tool and the date in Notes. Never invent it |
| Domain Rating | Text | Leave as Text. Same rule. DR and DA are different third-party estimates, not interchangeable |
| Spam Score | Text | Leave as Text. Percentage as the tool reports it, with the tool named in Notes |
| Listing Status | Select (add options after import) | Convert to Select, add options: "Not started", "Submitted", "Pending review", "Approved", "Rejected", "Removed", "Superseded" |
| Submitted Date | Date | Convert to Date |
| Approved Date | Date | Convert to Date |
| Approval Days | Number | Convert to Number, digits only. Derived from the two dates above, entered to keep the control visible |
| NAP Match | Select (add options after import) | Convert to Select, add options: "Match", "Mismatch", "Unknown", "Not applicable" |
| Referral Traffic | Text | Leave as Text. "Unknown" until measured in analytics. Never estimate it |
| Review Date | Date | Convert to Date. The next quarterly check |
| Owner | Text | Leave as Text |
| Status | Select (add options after import) | Convert to Select, add options: "Planned", "In progress", "Live", "Needs Review", "Retired" |
| Notes | Text | Leave as Text. The vetting note - what the directory is, and why it is or is not worth it |
The rows above are documentation examples only. Emit empty templates unless the user explicitly requests examples. Every third-party metric is Unknown because
this skill cannot measure it and must never estimate it.
| # | Field | Type | SQL | JSON Schema | Notion | CSV example |
|---|---|---|---|---|---|---|
| 1 | Citation ID | id | BIGINT PRIMARY KEY | integer | Text or Notion auto-ID | (blank) |
| 2 | Platform Name | text | VARCHAR(255) | string | Text | (blank) |
| 3 | Domain | text | VARCHAR(255) | string | Text | (blank) |
| 4 | Tier | select | VARCHAR(50) | string | Select | Tier 1 |
| 5 | Category | text | VARCHAR(100) | string | Text | (blank) |
| 6 | Directory Type | select | VARCHAR(100) | string | Select | Member directory |
| 7 | Target URL | url | TEXT | string, format: uri | URL | (blank) |
| 8 | Link Type | select | VARCHAR(50) | string | Select | Nofollow |
| 9 | Follow Attribute | select | VARCHAR(50) | string | Select | Nofollow |
| 10 | Domain Authority | text | VARCHAR(50) | string | Text | Unknown |
| 11 | Domain Rating | text | VARCHAR(50) | string | Text | Unknown |
| 12 | Spam Score | text | VARCHAR(50) | string | Text | Unknown |
| 13 | Listing Status | select | VARCHAR(50) | string | Select | Approved |
| 14 | Submitted Date | date | DATE | string, format: date | Date | (blank) |
| 15 | Approved Date | date | DATE | string, format: date | Date | (blank) |
| 16 | Approval Days | number | INTEGER | integer | Number | 5 |
| 17 | NAP Match | select | VARCHAR(50) | string | Select | Match |
| 18 | Referral Traffic | text | VARCHAR(50) | string | Text | Unknown |
| 19 | Review Date | date | DATE | string, format: date | Date | (blank) |
| 20 | Owner | text | VARCHAR(255) | string | Text | Unknown |
| 21 | Status | select | VARCHAR(50) | string | Select | Draft |
| 22 | Notes | long_text | TEXT | string | Text | (blank) |
Tier - the value that decides where the effort goes. Refused is a first-class tier,
because recording a platform the business deliberately rejected is how it stays rejected.
Full tier contents: ../../references/backlink-directory-master-list.md.
Tier 0 | Tier 1 | Tier 2 | Tier 3 | Tier 4 | Tier 5 | Tier 6 | Tier 7 | Refused
Directory Type - a starting set, and the column that catches a link network. Anything
Link network is a Refused row.
Map platform | Review platform | Professional profile | Company profile | Local body | Government register | Industry directory | Niche directory | Document sharing | Bookmarking | Q&A and forum | Classifieds | Link hub | News service | Guest post | Paid placement | Link network
Link Type vs Follow Attribute - two columns, not one, because a redirect carries no rel attribute at all. Collapsing them loses the distinction between "no link" and "a noindexed link".
Link Type: Dofollow | Nofollow | Redirect | JavaScript | No link | Unknown
Follow Attribute: Follow | Nofollow | Sponsored | UGC | Unknown
Listing Status - Superseded is for the case where a business moved location or
renamed and the old listing still exists somewhere. It is a real and common state.
Not started | Submitted | Pending review | Approved | Rejected | Removed | Superseded
NAP Match - the control. Mismatch means fix or remove; it never means "leave it, the
link is still a link".
Match | Mismatch | Unknown | Not applicable
Status
Planned | In progress | Live | Needs Review | Retired
Two natural links, both worth confirming before making them live:
Target URL points at a page in business-website-setup. The URL text is the reliable
key across packs; confirm whether the business wants a live join.Owner points at the people register in the operational pack, which is not in this
artifact set. It stays Text with a note naming the intended target.Prompt
Someone sent us a list of 165 free high-DA directories. Some of them want a keyword field
and ask for a reciprocal link. Should we do them all?
Context first - one question per message, nothing already answered:
Q: What is the business called, and what does one line of it actually do? A: Example Retail, home and kitchen goods in Example City.
Q: Are you already listed anywhere - how many places roughly? A: Google, and maybe four or five others from years ago. Nobody checks them.
Q: What is the aim - more calls, more site traffic, or visibility somewhere specific? A: More calls from people nearby.
Q: How much time is there per month for this? A: Maybe an hour, on a Sunday.
Q: Has anyone offered paid placement or a "dofollow network"? A: Yes, an email about a monthly plan. It was not cheap.
Recommended next step - offered, not built:
Not the 165. Skip anything asking for a reciprocal link, a keyword field or payment, and record them as
Refusedso they stay refused. Do about fifteen real ones instead: Google and Bing and Apple Maps, the two review platforms your sector actually uses, the chamber of commerce and the council directory, the trade association, two vertical directories that a kitchen-shop customer would search, and one local press angle. One canonical NAP block, pasted everywhere, re-checked quarterly. That is the highest-value fifteen available, and an hour a month is enough to keep them.Workflow: Canonical NAP block written once → Tier 0 platforms claimed → Tier 1 review and professional profiles completed → Local chamber, council and trade association joined → Vertical directories chosen by relevance → Tracking row created for each with a review date → Quarterly re-check scheduled
Want the CSV, SQL DDL, JSON Schema and Notion mapping for this?
NAP Match is checked by eye against the canonical block, not trusted. Directories
silently reformat, truncate or re-capitalise addresses.Domain Authority, Domain Rating and Spam Score are
third-party estimates from named tools, they change over time, and DA and DR are not the
same metric. This skill never produces them and never presents one as a fact.Unknown and blank are correct.See the Common Pitfalls reference for the full guidance.