YAML frontmatter
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
- 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.
- Read our measured rates from the calibration built off the ledger (the
pipeline's versioned home is <your-repo>/measure/devmetrics/):
```
<your-repo>/measure/devmetrics/rates.json # rates by work_type (committed seed)
<your-repo>/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.
- 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).
- 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:
python3 <your-repo>/measure/devmetrics/pipeline.py rates # rebuild rates.json from the ledgerSee <your-repo>/measure/devmetrics/SCHEMA.md for ledger + rates schema.
Files in this bundle
- SKILL.md — the verbatim agent-facing skill. Save it as
agent-effort-estimation/SKILL.mdwherever your agent loads skills.