Rebuild estimate engine to effort-based SOW model

Replace flat per-service ranges with OurDigital's real quoting model
(role_rate × billing_rate 0.70 × standard hours), sourced from the
06_Working Template quotes:
- rate_card.yaml: role rate card, billing/basis/terms, tools, scaling bands
- sow_templates.yaml: basic + treatment task-hour templates
- estimate.py: assemble SOW from findings, scale Technical/On-page hours by
  properties_total, 제안가 = 합계 floored to 500k
- build_deck.py: estimate slide shows module 소계 + 제안가 (point)
- findings_to_service.md / SKILL.md / DESIGN.md: synced to new model

Validated: reproduces real Basic ₩10.5M and Treatment ₩25.0M exactly;
SHR (25 properties) scales to ₩71.5M, L'Escape (1) = ₩25.0M.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-28 00:51:52 +09:00
parent 01201ee4a2
commit 5f66f57a8e
7 changed files with 343 additions and 269 deletions

View File

@@ -1,26 +1,40 @@
# Findings → service rubric
# Findings → estimate mapping (effort-based)
How `estimate.py` maps detected findings (from `findings.json`) to `rate_card.yaml`
service lines. The script reads the structured signals below; this file is the
human-readable source of truth for the rules.
`estimate.py` builds the 견적 from `sow_templates.yaml` priced via `rate_card.yaml`:
**cost = role_rate × billing_rate (0.70) × standard_hours**, grouped by module;
제안가 = 합계 floored to `rounding_unit`. This mirrors OurDigital/D.intelligence's
real SOW-based quoting.
| Trigger (signal in findings.json) | Service line(s) | Scope driver |
|---|---|---|
| `discovery.sitemap_status != 200` OR `discovery.robots_sitemap_declared == false` OR `discovery.discoverable_urls` low vs `estimated_pages` | `technical_audit` + `technical_remediation` | site size / # templates |
| `technical.cwv.perf < 0.5` OR `cls > 0.1` OR `lcp_ms > 2500` OR `ttfb_ms > 600` | `technical_audit` (if not already) + `technical_remediation` | # templates |
| `technical.schema.org == "bare"/"none"` OR `entity.panel != "hotel"` OR `entity.name_split` OR `entity.legacy_contamination` OR `entity.subbrands_with_entity == 0` | `schema_build` (one-time) + `onpage_entity` (retainer) | # sub-brands + # properties |
| `entity.properties_with_entity == 0` OR `url_hygiene` contains GBP/local mismatch | `local_seo` | # properties |
| `findings[].class` includes measurement gap / no GSC-GA4 | `ga4_impl` and/or `dashboard` (+ `gtm_setup` if tag gaps) | — |
| `technical.meta_dupe` OR `technical.title_i18n_mismatch` OR `technical.hreflang == "incomplete"` | `onpage_entity` | # templates |
## Baseline selection (basic vs treatment)
- `treatment` if any finding `severity == critical` **OR** `entity.properties_total > 3`
- else `basic`
- override with `--baseline`.
**Severity → priority** (for the brief/deck ordering, not pricing):
- `critical`: crawl/index blocking, CWV failing, entity mistyped
- `high`: entity/sub-brand gaps, duplicate URLs, meta dupes
- `medium`: hreflang, H1, hygiene
## Module inclusion
Each baseline carries the standard module set (P&M · Technical SEO · On-page SEO ·
SEO Growth), matching real quotes. Findings justify modules via the `trigger` field
in `sow_templates.yaml`:
**Quantity rules**
- `monthly` line items use `rate_card.defaults.retainer_months` (default 6).
- `local_seo` scope note scales with property count (`entity` / discovery counts).
- One-time items counted once even if triggered by multiple findings.
| Module | trigger finding classes |
|---|---|
| Planning & Management | always |
| Technical SEO | crawlability, cwv, schema_entity |
| On-page SEO | onpage, schema_entity |
| SEO Growth | measurement, always |
Edit this file and `rate_card.yaml` together when rates or rules change.
## Hours scaling (portfolio)
Tasks marked `scale: true` (Technical SEO + On-page SEO) have their **hours**
multiplied sub-linearly by `entity.properties_total` per `rate_card.scaling.bands`.
P&M and SEO Growth stay fixed (management/KPI overhead is ~flat). A single-property
prospect → ×1.0, which reproduces the real single-site quotes exactly.
## Tools & terms
`tools` (e.g. SEMrush Guru) are listed separately — client-subscribed, with +15%
procurement markup if billed through us. VAT 별도 · 유효기간 14d · 현금 · 절사 from
`rate_card.terms`.
## Validated reproduction (2026-05-28)
- `treatment` ×1.0 → 합계 25,340,000 → 제안가 **25,000,000** (real Treatment quote)
- `basic` ×1.0 → 합계 10,612,000 → 제안가 **10,500,000** (real Basic quote)
Edit `rate_card.yaml` and `sow_templates.yaml` together when rates or standard hours change.