--- type: Skill title: "Agent Effort Estimation" description: "Forecasts agent wall-clock (primary), human-hours-equivalent, and a confidence cone for work NOT yet started, using measured rates by work type from the local ledger of completed items. Use only when invoked by name or when the user explicitly asks to apply calibrated/measured rates or convert an existing estimate to agent time. Do not auto-select for generic \"how long will this take\" questions — answer those conventionally first so the input stays unanchored. For work already shipped, use look-back-effort-accounting." resource: "https://localgradient.dev/okf/resources/agent-effort-estimation/index.md" tags: ["coding-agents", "skill", "estimation"] status: stable generated: { by: human:local-gradient, at: "2026-07-25T16:00:00-04:00" } okf_version: "0.2" save_as: "agent-effort-estimation/SKILL.md" --- # Agent Effort Estimation (Forward) Forecast how long upcoming work will take **before** it starts. The headline output is **agent wall-clock** — what you actually schedule against — because agent dev time is far faster than human-only dev time. Human-hours-equivalent and a confidence band come alongside. > **Invocation.** Invoke this explicitly, after a conventional estimate or work > breakdown already exists. Reading the rates puts the multiplier in context, so > a first-pass estimate produced under this skill is anchored to it — produce > that estimate first, then convert. > **Direction matters.** This skill looks FORWARD at work not yet started. To > account for work already shipped, use **`look-back-effort-accounting`**. That > skill writes the ledger this one reads. Forecast quality rises every time the > ledger grows — estimation is only as good as the actuals behind it. ## What these numbers are, and are not Read this before quoting a forecast to anyone: - **Human-hours-equivalent is a size proxy, not a duration.** It is computed as counted lines ÷ fixed tier rates, so it says "a solo human writing this volume at these rates would take N hours" — nothing about value, quality, or what a team would actually take. Per 100 lines it lands near-constant (~3.0–3.8 h) across every work type, which is the tier mixture showing through rather than a discovered difference between kinds of work. - **The multiplier is one global constant, not a per-type measurement.** `attach_agent()` shares a single attended-hours total across items in proportion to their human-hours, so `human/agent` is identical for every row by construction. `derived_agent_h` is exactly `median_human_h / overall_multiplier`. No per-type speed signal exists until agent wall-clock is attributed per item. - **The attended denominator may be assumed.** Absent `ATTENDED_WALLTIME_H`, it is `distinct active days × 6 h nominal`. Check `run_manifest.json → denominators.attended_walltime_source`; when it reads `assumed`, say so rather than presenting the multiplier as measured. - **Rates are conditional on line attribution.** Medians come from countable rows only (`net_lines > 0`); `coverage` reports that fraction. Judge thin basis on `n_countable`, not `n` — a type can show n=115 on a median resting on 51 rows. - **Work-type size differences are contaminated by day-buckets.** `kind: direct` rows lump a day of mixed commits under one type, so a type dominated by them looks larger without being harder. Compare `kind: pr` rows when the distinction matters. ## Current calibration snapshot (real, anonymized) One personal workflow's rates — published so the method is reproducible and criticizable, not so the numbers can be copied without local recalibration. Window: **2026-06-17..2026-07-14** · Rates version: **v2.1-countable-medians** · Built: **2026-07-26** · Overall human-equivalent → attended-agent multiplier: **43.8×** (denominator source: **assumed**) | Work type | n | countable | coverage | Median human-eq h | p25–p75 | Median net lines | Derived agent h | |-----------|--:|----------:|---------:|------------------:|---------|-----------------:|----------------:| | bug | 65 | 48 | 0.74 | 23.73 | 10.69–142.03 | 729.5 | 0.542 | | chore | 13 | 8 | 0.62 | 21.8 | 3.58–59.7 | 586.0 | 0.498 | | docs | 4 | 2 | 0.5 | 2.87 | 1.91–3.83 | 100.5 | 0.066 | | feature | 115 | 51 | 0.44 | 6.14 | 1.26–17.12 | 184 | 0.14 | | infra | 18 | 8 | 0.44 | 4.85 | 4.13–14.85 | 163.0 | 0.111 | | orchestration | 26 | 12 | 0.46 | 9.45 | 5.85–36.07 | 276.5 | 0.216 | | research | 2 | 2 | 1.0 | 95.38 | 35.14–155.61 | 2522.5 | 2.178 | **Read `coverage` before trusting a row.** Medians come from countable rows only (items whose commits produced counted lines). Where coverage is low, the rate describes the countable subset, not the work type as a whole. ## How it works 1. **Describe the work as items.** For each intended unit, give: - `work_type` ∈ {feature, bug, chore, docs, research, orchestration, infra} - a rough size: expected net lines OR a complexity tier (T1–T5), plus the files/areas it touches. 2. **Read our measured rates** from the calibration built off the ledger (the pipeline's versioned home is `/measure/devmetrics/`): ``` /measure/devmetrics/rates.json # rates by work_type (committed seed) /measure/devmetrics/work_items.jsonl # raw ledger (recompute if stale) ``` Each work_type carries: `derived_agent_h` (median human-hours ÷ the global multiplier — a transform, not an observed agent median; see the caveat above), median human-hours, `n_countable`/`coverage`, and a p25–p75 spread. 3. **Project** each item, then sum: - `agent_h ≈ size × (derived_agent_h per unit for that work_type)` - report a **cone** from the p25–p75 spread (early estimates are wider — McConnell's cone of uncertainty). 4. **Cite the basis.** Every estimate states n and the rate it used, e.g. *"feature, n=12 prior items, median 0.9h agent / 38x → 1.1h agent (0.6–1.8h cone)."* ## Output format ``` ## Estimate: [work description] | Item | Type | Size | Agent h | Cone (p25–p75) | Human-equiv h | |------|------|-----:|--------:|---------------:|--------------:| | ... | feature | 400 ln | 1.1 | 0.6–1.8 | 18 | | **Total** | | | **X.X agent h** | **lo–hi** | **NN human h** | Basis: rates.json built YYYY-MM-DD from N ledger items. Multiplier (this mix): NN× vs human-only. ``` ## When the ledger is thin If a work_type has few COUNTABLE prior items (`n_countable` < 5 — judge on that, not the full `n`; a type can show n=115 on a median resting on 51 rows), say so and fall back to the look-back-effort-accounting COCOMO tier rates for that type, flagged as low-confidence. Never present a forecast as precise when the basis is thin — widen the cone and name the n. ## Closing the loop After the work ships, run `look-back-effort-accounting` on it. Comparing the forecast here to the actual there is the **estimate-accuracy** signal — track it over time and the rates self-correct. (Estimate accuracy is a local signal, not one of the four DORA metrics; the pipeline computes those separately into `dora_summary.json`.) ## Rebuilding rates The calibration is produced by the dev-metrics pipeline: ```bash python3 /measure/devmetrics/pipeline.py rates # rebuild rates.json from the ledger ``` See `/measure/devmetrics/SCHEMA.md` for ledger + rates schema. ## Files in this bundle - [SKILL.md](./SKILL.md) — the verbatim agent-facing skill. Save it as `agent-effort-estimation/SKILL.md` wherever your agent loads skills.