Files
our-claude-skills/custom-skills/96-ourdigital-estimate-engine/DESIGN.md
Andrew Yim c9bdbb57f7 Extract ourdigital-estimate-engine; presales-seo now calls it
New skill 96-ourdigital-estimate-engine: method-aware quoting engine
(effort / coaching / procurement) with universal rate_card + per-service
catalog. Real catalogs: seo (effort), education (coaching); stubs:
digital_ads, digital_branding. Validated to reproduce real quotes —
SEO basic ₩10.5M / treatment ₩25.0M, SHR chain ₩29.5M, L'Escape basic
₩10.5M, GA4/GTM coaching ₩1,570,000, procurement +15%.

Refactor 95-ourdigital-presales-seo: remove rate_card.yaml, sow_templates.yaml,
estimate.py (migrated to engine); add findings_to_scope.py; Stage 5 now maps
findings→scope.json and calls the engine CLI. build_deck/kg_query unchanged;
end-to-end validated on SHR (29.5M) + deck renders engine estimate.json.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 01:54:11 +09:00

121 lines
6.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Design — `ourdigital-estimate-engine` skill
- **Status**: Approved design (2026-05-28). Implementing.
- **Origin**: Extracted from `ourdigital-presales-seo`'s estimate engine and generalized to
cover all OurDigital / D.intelligence professional services via multiple costing methods.
- **Source data**: real quotes in Google Drive `06_Working Template` (SEO) and `견적 자료`
(GA4/GTM, education). Company: (주)디인텔리전스 / D.intelligence Lab, info@ourdigital.org.
## 1. Purpose & scope
A reusable, **method-aware** estimate engine that produces OurDigital 견적 for any service
line. Consuming skills (e.g. `ourdigital-presales-seo`) map their context → a generic
`scope.json` and call the engine CLI. The engine owns the rate card + service catalog
(single source of truth).
### Costing methods (from real data)
1. **effort**`role_rate × billing_rate(0.70) × hours`, grouped into modules; tiering,
sub-brand scaling, premium-vertical floor, 절사. (SEO Audit & Treatment, etc.)
2. **coaching**`Σ(lesson_type_price × hours)` over a lesson plan, with student-count
discount. Base type prices reproduce real quotes; optional subject×level list-price
matrix (`base × level_multiple`). (GA4/GTM courses, workshops, education.)
3. **procurement**`unit_cost × qty × (1 + 0.15)` for non-labor items (tools, 3rd-party).
All methods share: cover-sheet output, 부가세 별도, 유효기간 14d, 현금, 십만 단위 미만 절사
(500k floor for project quotes), quote no `OD-YYYY-NNN`.
### Non-goals
- No invented rates. `digital_ads` / `digital_branding` ship as **effort stubs** (clearly
marked) until real quotes exist.
- No GA4/GTM *effort-implementation* catalog (delivered as coaching → a course in education).
- Does not replace consuming skills' domain logic (e.g. SEO findings→scope mapping stays in
presales-seo).
## 2. Structure
```
96-ourdigital-estimate-engine/
SKILL.md
references/
rate_card.yaml # UNIVERSAL pricing config (see §3)
catalog/
seo.yaml # method: effort — tiers smb/basic/treatment (real)
education.yaml # method: coaching — courses + per-subject levels (real)
digital_ads.yaml # method: effort — STUB
digital_branding.yaml # method: effort — STUB
scripts/
estimate.py # CLI dispatcher
methods/effort.py
methods/coaching.py
methods/procurement.py
render.py # md / xlsx / json output (cover-sheet)
scope.schema.json
```
## 3. `rate_card.yaml` (universal)
- `company`, `quote_prefix: OD`, `currency: KRW`, `rounding_unit: 500000`
- `terms`: vat 부가세 별도, validity_days 14, payment 현금
- **effort**: `billing_rate: 0.70` (floor 0.60), `basis {8h/day, 4wk/mo}`,
`procurement_markup: 0.15`, `role_rates {대표 180k … 과장 70k … 인턴 12k}`,
`tiering {premium_verticals, premium_min_tier}`, `scaling {driver: subbrands_total,
bands cap ×2.0}`, `tools`.
- **coaching**:
- `lesson_type_prices`: 화상 80,000 · 대면 100,000 · 실습 150,000 · 워크숍 300,000 · 트리트먼트 500,000
- `level_multiple`: Beginners 1.0 · Intermediate 1.5 · Advanced 2.0 · Expert 2.5 · Trainers 3.0
- `subject_levels`: {Content Marketing: Beginners, SEO: Beginners, Google Analytics: Intermediate,
Google Tag Manager: Advanced, Digital Marketing Strategy: Advanced, Digital Communication:
Intermediate, KPI Setup & Measurement Plan: Expert, Business Model Canvas: Expert,
e-Commerce Audit: Advanced}
- `pricing_mode: base` (default; reproduces real quotes) | `matrix` (list price = base×level_multiple)
- `student_discount_bands`: [[5,0.20],[10,0.0],[20,0.20],[30,0.30]] (30+ = 별도 협의)
## 4. Catalog entries
Each file: `service`, `method`, and method-specific body.
- **seo.yaml** (effort): the current `sow_templates.yaml` verbatim (tiers smb/basic/treatment,
modules→tasks with role/hours/scale). Validated to reproduce 10.5M/25.0M.
- **education.yaml** (coaching): `courses:` — named lesson plans, each `lessons: [{subject,
type, hours}]`. Seed with `ga4_gtm_intermediate` (the real 17-lesson, ₩1,570,000 plan) and
`ga4_gtm_marketing_analytics`. Subjects resolve levels from rate_card.subject_levels.
- **digital_ads.yaml / digital_branding.yaml** (effort): STUB — one placeholder tier +
`_stub: true`, with a header comment to populate from a real quote.
## 5. `scope.json` (generic input — `scope.schema.json`)
```jsonc
// effort
{"service":"seo","method":"effort","tier":"auto", // or smb|basic|treatment
"signals":{"properties_total":25,"subbrands_total":5,"vertical":"hotel_resort"},
"billing_rate":null, "seq":1, "prospect":{"name":"","audit_date":""}}
// coaching
{"service":"education","method":"coaching","course":"ga4_gtm_intermediate",
"students":1, "pricing_mode":"base", "prospect":{...}} // or "lessons":[{subject,type,hours}]
```
`tier:"auto"` → engine runs tiering (size + premium floor) from `signals`.
## 6. CLI
`python scripts/estimate.py --catalog-dir catalog --rate-card references/rate_card.yaml
--scope scope.json --out-dir <dir> [--seq N]`
- Dispatcher loads scope → catalog entry → method module → `render.py`.
- Outputs `05_estimate_ko.md`, `05_estimate.xlsx`, `data/estimate.json`.
- **effort `estimate.json` keeps the current shape** (modules + 제안가 + scope + terms) so
`ourdigital-presales-seo/build_deck.py` keeps working unchanged.
## 7. `ourdigital-presales-seo` refactor (consumption = CLI)
- **Move** `references/rate_card.yaml` + `references/sow_templates.yaml` → engine
(`catalog/seo.yaml`). Delete `scripts/estimate.py` from presales-seo.
- **Add** `scripts/findings_to_scope.py` (thin): findings.json → scope.json (tier signals,
vertical, prospect). Keeps SEO-specific mapping out of the engine.
- **Stage 5** in SKILL.md: `findings_to_scope.py` → call engine
`estimate.py --service seo --scope scope.json`.
- `build_deck.py`, `kg_query.py`, `render_pdf.sh`, `findings_to_service.md`,
`findings.schema.json`, templates → unchanged.
## 8. Validation (before commit)
- effort: SEO basic ₩10.5M, treatment ₩25.0M (1 property); SHR ₩29.5M; L'Escape `basic` ₩10.5M
— identical to pre-refactor.
- coaching: `ga4_gtm_intermediate` (17 lessons, 1 student) → **₩1,570,000** exactly.
- procurement: unit_cost × qty × 1.15 sanity check.
- presales-seo end-to-end: findings→scope→engine→견적 + deck reproduces SHR/L'Escape.
## 9. Future
Populate `digital_ads` / `digital_branding` from real quotes; add `content_marketing` as a
project-service if effort quotes emerge (currently a coaching subject); optional matrix-mode
quotes; more education courses.