All estimates now archive to ~/Workspaces/ourdigital-space/estimates/ <YYYY-MM-DD>_<prospect|account>_<service>/ (single 견적 archive). Engine SKILL.md documents the output-home convention (engine stays --out-dir-agnostic). presales-seo Stage 5 writes the estimate to $EST (archive); Stage 6 deck reads from $EST but stays in the engagement bundle. (Workspace side, not in this repo: SHR estimate moved to the archive, pointer left in the engagement, ourdigital-space/CLAUDE.md documents estimates/.) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
94 lines
7.0 KiB
Markdown
94 lines
7.0 KiB
Markdown
---
|
||
name: ourdigital-presales-seo
|
||
description: Standardized pre-sales SEO + Knowledge Graph diagnostic for a prospect domain that produces a technical/on-page scan, KG/entity analysis, consolidated brief, a rate-card-based cost estimate (견적), and an editable PPTX sales deck. Use for pre-sales prospecting, sales briefing prep, "pre-sales SEO audit", "프리세일즈 진단", "견적 + 제안 슬라이드", or when preparing for a prospect meeting. Public-data only (no client GSC/GA4 access required). Korean-first output.
|
||
---
|
||
|
||
# OurDigital Pre-sales SEO Workflow
|
||
|
||
Runs the full pre-sales diagnostic for **any prospect domain** as **step-by-step
|
||
confirmed stages**, ending in a cost estimate and a sales-briefing deck. Public-data
|
||
only — designed for prospects where you don't yet have GSC/GA4/GTM access.
|
||
|
||
Origin: standardizes the Sono Hotels & Resorts pre-sales diagnostic.
|
||
|
||
## Execution model — STEP BY STEP
|
||
|
||
Run one stage at a time. After each stage, **present the result and WAIT for the
|
||
user's go-ahead** before the next. Create a TodoWrite/Task item per stage. Stages 5
|
||
(estimate) and 6 (deliverables) are explicit review gates — never send without sign-off.
|
||
|
||
## Stage 0 — Scope & preflight
|
||
Collect (ask only for what you can't infer):
|
||
- `domain` (required); `brand_name` + `aliases` (앤/& / EN variants — infer from `<title>`/og first)
|
||
- `sub_brands`, `properties` (auto-extract from crawl URL patterns in Stage 1 if not given)
|
||
- `competitors` (else pick a set from `references/competitor_sets.md` by vertical)
|
||
- `market`/`language` (default South Korea / ko), `vertical`
|
||
- `output_dir` — default routing: if `~/Workspaces/<slug>-workspace/` exists →
|
||
`…/audits/<YYYY-MM-DD>-presales/`, else `~/Workspaces/seo-workspace/prospecting/<YYYY-MM-DD>-<prospect>/`
|
||
|
||
Preflight tool check (report missing + fallback): Firecrawl, DataForSEO, `GOOGLE_KG_API_KEY`,
|
||
headless Chrome, python-pptx. Create `data/` subfolder. Initialize `findings.json` (see `findings.schema.json`).
|
||
|
||
## Stage 1 — Discovery & crawl
|
||
- `WebFetch` robots.txt + `/sitemap.xml` + `/sitemap_index.xml` (note status; 500/404 = finding).
|
||
- `firecrawl_map` (limit high, includeSubdomains) → URL inventory; record `discoverable_urls`,
|
||
derive URL architecture, `estimated_pages`, and hygiene flags (`/test`, params, duplicate path schemes).
|
||
- Populate `findings.json.discovery`. → write scan §1.
|
||
|
||
## Stage 2 — Technical / on-page
|
||
- `firecrawl_scrape` (json) homepage + 1-2 property pages: JSON-LD `@type`s, Organization completeness
|
||
(sameAs/alternateName/address), meta/title/H1, hreflang set, robots/googlebot meta.
|
||
- `mcp__dfs-mcp__on_page_lighthouse` homepage (+ a property) → CWV.
|
||
- Populate `findings.json.technical`. → write `01_technical-onpage-scan.md`.
|
||
- **Honesty rule:** never claim "noindexed" if the page ranks — flag conflicting directives as "verify".
|
||
|
||
## Stage 3 — Knowledge Graph / entity
|
||
- `python scripts/kg_query.py --brand … --aliases … --parent … --legacy … --subbrands … --properties … --competitors … --out-dir <out>/data`
|
||
(needs `GOOGLE_KG_API_KEY`; run with sandbox disabled — read-only API GET).
|
||
- Live SERP panel verification: `mcp__dfs-mcp__serp_organic_live_advanced` (ko, South Korea) for brand,
|
||
a sub-brand, a property, and one competitor → record actual panel type/title, local packs, reseller leakage.
|
||
- Populate `findings.json.entity` (panel, name_split, legacy_contamination, sub-brand/property entity counts,
|
||
competitor_benchmark[], wikipedia). → write `02_knowledge-graph-entity.md` + `data/serp_panels_findings.md`.
|
||
|
||
## Stage 4 — Consolidated brief
|
||
- Synthesize, rank by severity, build the competitive-gap table. Populate `findings.json.findings[]`.
|
||
→ write `03_presales-opportunity-brief.md`.
|
||
|
||
## Stage 5 — Estimate (견적) — REVIEW GATE
|
||
Pricing is delegated to the **`ourdigital-estimate-engine`** skill (`../96-ourdigital-estimate-engine`). Estimates are archived **centrally in OurDigital's space**, not in the engagement bundle:
|
||
- `EST=~/Workspaces/ourdigital-space/estimates/<YYYY-MM-DD>_<account_code>_seo` — create it (central 견적 archive).
|
||
- `python scripts/findings_to_scope.py --findings <out>/data/findings.json --out "$EST/data/scope.json" --seq <N> [--tier auto|smb|basic|treatment] [--billing 0.70]`
|
||
- `ENG=../96-ourdigital-estimate-engine; python $ENG/scripts/estimate.py --rate-card $ENG/references/rate_card.yaml --catalog-dir $ENG/catalog --scope "$EST/data/scope.json" --out-dir "$EST"`
|
||
- Engine (effort method, `seo` catalog): auto-tier (smb/basic/treatment) by portfolio + premium-vertical floor; On-page hours scale by `subbrands_total` (cap ×2.0); 제안가 = 합계 절사. Reproduces Basic ₩10.5M / Treatment ₩25.0M; SMB ~₩3M; 25-property chain ~₩29.5M.
|
||
- Produces `05_estimate_ko.md`, `05_estimate.xlsx`, `data/estimate.json` in `$EST` (the central quote archive; effort shape → consumed by `build_deck.py`). Present the 견적; get sign-off.
|
||
- SEO findings→scope/tier mapping lives here (`findings_to_service.md`); **rates/hours/tiers live in the engine** (`rate_card.yaml` + `catalog/seo.yaml`) — edit pricing there, not here.
|
||
|
||
## Stage 6 — Deliverables — REVIEW GATE before send
|
||
- **Client PDF**: author the short brief HTML from `templates/client_brief.html` (fill the content; keep the CSS),
|
||
then `bash scripts/render_pdf.sh <brief>.html` → PDF. Verify Korean renders (Read the PDF).
|
||
- **Sales deck**: `python scripts/build_deck.py --findings <out>/data/findings.json --estimate "$EST/data/estimate.json" --out <out>/sales-deck.pptx` (deck stays in the engagement bundle; reads the archived estimate.json from `$EST`)
|
||
- Sanitize the client-facing pieces: no internal pricing strategy beyond the 견적; tasteful competitor benchmark only.
|
||
|
||
## Stage 7 — Archive (standard)
|
||
Push the consolidated report to the OurDigital SEO Audit DB:
|
||
```
|
||
python <notion-writer path>/notion_writer.py \
|
||
--database 2c8581e5-8a1e-8035-880b-e38cefc2f3ef \
|
||
--title "<프로스펙트> SEO 사전진단 (Pre-sales) — <YYYY-MM-DD>" \
|
||
--properties '{"Target URL": "<domain>", "Audit Date": "<YYYY-MM-DD>", "Account Code": "<CODE>"}' \
|
||
--file <out>/03_presales-opportunity-brief.md
|
||
```
|
||
Property names are exact: `Target URL`, `Audit Date`, `Account Code`. **Do NOT set `Audit ID`**
|
||
(read-only formula). notion-writer path: `~/Project/our-claude-skills/custom-skills/32-notion-writer/code/scripts/notion_writer.py`.
|
||
|
||
## Tool gotchas (learned)
|
||
- OurSEO `crawl_website` **caps ~60 pages** regardless of `max_pages` — use Firecrawl `map` for inventory; report the discoverable count as a finding.
|
||
- Firecrawl `scrape` cache may return empty — re-scrape.
|
||
- KG API + headless Chrome + Notion push need network → run those Bash calls with the sandbox disabled.
|
||
- Korean PDF: headless Chrome uses system fonts (AppleSDGothicNeo on macOS). On Windows set deck font to Malgun Gothic in `build_deck.py`.
|
||
|
||
## Conventions
|
||
- Korean-first for all client-facing output; keep technical terms in English (SEO, CWV, Schema, hreflang).
|
||
- File names: `01_/02_/03_/05_` prefixes as above; raw artifacts in `data/`.
|
||
- Outputs go to the workspace (Stage 0 routing), **never** into this skills repo.
|