harnessdoc 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. harnessdoc-0.1.0/.gitignore +13 -0
  2. harnessdoc-0.1.0/LICENSE +21 -0
  3. harnessdoc-0.1.0/PKG-INFO +331 -0
  4. harnessdoc-0.1.0/README.md +305 -0
  5. harnessdoc-0.1.0/docs/PRD.md +96 -0
  6. harnessdoc-0.1.0/docs/case-study.md +71 -0
  7. harnessdoc-0.1.0/examples/ci/harnessdoc.yml +37 -0
  8. harnessdoc-0.1.0/examples/demo_app/README.md +11 -0
  9. harnessdoc-0.1.0/examples/demo_app/WASTE.md +22 -0
  10. harnessdoc-0.1.0/examples/demo_app/acme_support/__init__.py +0 -0
  11. harnessdoc-0.1.0/examples/demo_app/acme_support/agent.py +45 -0
  12. harnessdoc-0.1.0/examples/demo_app/acme_support/extract.py +18 -0
  13. harnessdoc-0.1.0/examples/demo_app/acme_support/router.py +25 -0
  14. harnessdoc-0.1.0/examples/demo_app/acme_support/tools.py +37 -0
  15. harnessdoc-0.1.0/examples/demo_app/harnessdoc.experiment.minimax.yaml +24 -0
  16. harnessdoc-0.1.0/examples/demo_app/harnessdoc.experiment.yaml +17 -0
  17. harnessdoc-0.1.0/examples/demo_app/harnessdoc.goals.yaml +15 -0
  18. harnessdoc-0.1.0/examples/demo_app/jobs/nightly_summary.py +19 -0
  19. harnessdoc-0.1.0/examples/demo_app/logs/generate_logs.py +145 -0
  20. harnessdoc-0.1.0/examples/demo_app/prompts/policy.md +66 -0
  21. harnessdoc-0.1.0/examples/demo_app/tests/evals/classify_intent_cases.jsonl +24 -0
  22. harnessdoc-0.1.0/examples/demo_app/tests/evals/test_router.py +17 -0
  23. harnessdoc-0.1.0/pyproject.toml +55 -0
  24. harnessdoc-0.1.0/skills/harnessdoc/SKILL.md +97 -0
  25. harnessdoc-0.1.0/src/harnessdoc/__init__.py +3 -0
  26. harnessdoc-0.1.0/src/harnessdoc/check.py +89 -0
  27. harnessdoc-0.1.0/src/harnessdoc/cli.py +703 -0
  28. harnessdoc-0.1.0/src/harnessdoc/compare.py +192 -0
  29. harnessdoc-0.1.0/src/harnessdoc/data/__init__.py +0 -0
  30. harnessdoc-0.1.0/src/harnessdoc/data/pricing.json +148 -0
  31. harnessdoc-0.1.0/src/harnessdoc/experiment.py +404 -0
  32. harnessdoc-0.1.0/src/harnessdoc/goals.py +102 -0
  33. harnessdoc-0.1.0/src/harnessdoc/guard.py +106 -0
  34. harnessdoc-0.1.0/src/harnessdoc/interceptor.py +191 -0
  35. harnessdoc-0.1.0/src/harnessdoc/log.py +84 -0
  36. harnessdoc-0.1.0/src/harnessdoc/mcp_server.py +338 -0
  37. harnessdoc-0.1.0/src/harnessdoc/measure.py +127 -0
  38. harnessdoc-0.1.0/src/harnessdoc/pricing.py +285 -0
  39. harnessdoc-0.1.0/src/harnessdoc/report.py +239 -0
  40. harnessdoc-0.1.0/src/harnessdoc/rules.py +85 -0
  41. harnessdoc-0.1.0/src/harnessdoc/scan/__init__.py +152 -0
  42. harnessdoc-0.1.0/src/harnessdoc/scan/checks.py +274 -0
  43. harnessdoc-0.1.0/src/harnessdoc/scan/model.py +88 -0
  44. harnessdoc-0.1.0/src/harnessdoc/scan/pyast.py +628 -0
  45. harnessdoc-0.1.0/src/harnessdoc/sessions.py +317 -0
  46. harnessdoc-0.1.0/src/harnessdoc/skill/SKILL.md +97 -0
  47. harnessdoc-0.1.0/src/harnessdoc/usage/__init__.py +7 -0
  48. harnessdoc-0.1.0/src/harnessdoc/usage/analyze.py +243 -0
  49. harnessdoc-0.1.0/src/harnessdoc/usage/loader.py +120 -0
  50. harnessdoc-0.1.0/src/harnessdoc/usage/records.py +299 -0
  51. harnessdoc-0.1.0/tests/stdio_smoke.py +32 -0
  52. harnessdoc-0.1.0/tests/test_benchmark_demo.py +61 -0
  53. harnessdoc-0.1.0/tests/test_check.py +77 -0
  54. harnessdoc-0.1.0/tests/test_compare.py +119 -0
  55. harnessdoc-0.1.0/tests/test_coverage_guard.py +186 -0
  56. harnessdoc-0.1.0/tests/test_experiment.py +321 -0
  57. harnessdoc-0.1.0/tests/test_fmt_usd.py +26 -0
  58. harnessdoc-0.1.0/tests/test_goals_report_mcp.py +205 -0
  59. harnessdoc-0.1.0/tests/test_pricing.py +172 -0
  60. harnessdoc-0.1.0/tests/test_release.py +49 -0
  61. harnessdoc-0.1.0/tests/test_scanner.py +385 -0
  62. harnessdoc-0.1.0/tests/test_sessions.py +166 -0
  63. harnessdoc-0.1.0/tests/test_usage.py +276 -0
@@ -0,0 +1,13 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ dist/
5
+ build/
6
+ .pytest_cache/
7
+ .ruff_cache/
8
+ .DS_Store
9
+ .claude/
10
+ examples/demo_app/logs/usage.jsonl
11
+ examples/demo_app/logs/usage_after.jsonl
12
+ harnessdoc-report.md
13
+ harnessdoc-experiment-*.json
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Harsh Chhabra
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,331 @@
1
+ Metadata-Version: 2.5
2
+ Name: harnessdoc
3
+ Version: 0.1.0
4
+ Summary: Measure what each successful task of your AI app costs — provider-neutral, deterministic, and CI-ready.
5
+ Project-URL: Homepage, https://github.com/hchhabra1999/harnessdoc
6
+ Project-URL: Issues, https://github.com/hchhabra1999/harnessdoc/issues
7
+ Author: Harsh Chhabra
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: claude-code,cost,evals,harness,llm,mcp,prompt-caching,tokens
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Topic :: Software Development :: Quality Assurance
15
+ Requires-Python: >=3.10
16
+ Requires-Dist: pyyaml>=6
17
+ Requires-Dist: typer>=0.12
18
+ Provides-Extra: dev
19
+ Requires-Dist: anthropic>=2; extra == 'dev'
20
+ Requires-Dist: mcp<3,>=2.3; extra == 'dev'
21
+ Requires-Dist: pytest>=8; extra == 'dev'
22
+ Requires-Dist: ruff>=0.5; extra == 'dev'
23
+ Provides-Extra: mcp
24
+ Requires-Dist: mcp<3,>=2.3; extra == 'mcp'
25
+ Description-Content-Type: text/markdown
26
+
27
+ # harnessdoc
28
+
29
+ **Measure what each successful task of your AI app costs — and prove when a change makes it cheaper without
30
+ making it worse.**
31
+
32
+ harnessdoc scans your code for every LLM call, reads your usage logs, and tells you — with `file:line`
33
+ evidence, a dollar estimate and the eval that guards quality — where your harness is wasting tokens. Then it
34
+ re-measures after you change something, so the saving is proven, not claimed.
35
+
36
+ - **Provider-neutral:** Anthropic, OpenAI, Gemini, LiteLLM.
37
+ - **Deterministic:** the same code and logs give the same numbers, so it can gate CI.
38
+ - **No LLM calls of its own:** use it as a CLI, in CI, as an MCP server (Claude Code, Cursor, Codex…) or as a
39
+ Claude Code skill — your agent does the reasoning; harnessdoc does the measuring.
40
+
41
+ > **Status: v0.1.0, alpha.** Everything below works and is tested (180 tests, CI on Python 3.10–3.13), but
42
+ > it has been proven on a demo app we wrote and on scanner precision for two open-source apps — **not yet on
43
+ > measured savings in a real app** (see [limits](#known-limits) and the [case study](docs/case-study.md)).
44
+
45
+ ## Use it from your coding agent
46
+
47
+ harnessdoc ships an MCP server and a Claude Code skill. The agent interviews you about what the app is for,
48
+ then calls harnessdoc for every number — harnessdoc itself never calls an LLM.
49
+
50
+ ```bash
51
+ pip install "harnessdoc[mcp]"
52
+
53
+ # Claude Code
54
+ claude mcp add harnessdoc -- harnessdoc mcp
55
+ harnessdoc skill install # then type /harnessdoc in Claude Code
56
+
57
+ # Cursor / other MCP clients: add to mcp.json
58
+ # {"mcpServers": {"harnessdoc": {"command": "harnessdoc", "args": ["mcp"]}}}
59
+ ```
60
+
61
+ | MCP tool | Does | Writes files? |
62
+ |---|---|---|
63
+ | `scan` | LLM call sites, findings HD001–HD010, uncovered call sites | no |
64
+ | `measure` | Baseline from usage logs; each finding priced in $/month | no |
65
+ | `price` | Price token counts for any model in the price book | no |
66
+ | `explain_rule` | Why a rule matters, how to fix it, how its saving is estimated | no |
67
+ | `read_goals` / `write_goals` | The project's `harnessdoc.goals.yaml` | `write_goals` only |
68
+ | `write_report` | `harnessdoc-report.md` in the project | yes |
69
+ | `scaffold_guard` | Guard eval (pytest + cases file) for an uncovered call site; never overwrites | yes |
70
+ | `compare` | Before/after verdict: SHIP / REJECT / INCONCLUSIVE / NO CLEAR SAVING | no |
71
+ | `check` | What the CI gate would say: new vs fixed findings against the baseline | no |
72
+ | `plan_experiment` | Runs and estimated cost of an experiment; calls nothing | no |
73
+ | `run_experiment` | Runs it with your keys — needs `user_approved=true` and a budget | results file |
74
+
75
+ Paths are confined to the project directory you pass. The [skill](skills/harnessdoc/SKILL.md) adds the
76
+ workflow and its rules: quote only harnessdoc's numbers, label estimates, write a guard eval before
77
+ changing an unguarded call, and apply one approved change at a time.
78
+
79
+ ## Prove the saving
80
+
81
+ ```bash
82
+ harnessdoc guard scaffold extract_order_id # guard eval for a call site that has none
83
+ harnessdoc compare --before logs/week44.jsonl --after logs/week45.jsonl
84
+ ```
85
+
86
+ `compare` answers one question — *is each successful task cheaper now, without hurting quality?* — and is
87
+ built to say "not sure yet" when that's the truth:
88
+
89
+ - **Cost:** cost per successful task before vs after, with a 95% CI on the change (independent windows,
90
+ delta method). LOWER only if the whole interval is below zero.
91
+ - **Quality:** a non-inferiority test on success rate (Newcombe score interval) against the allowed drop in
92
+ `harnessdoc.goals.yaml`. PASS only if even the worst case stays within it.
93
+ - **Verdict:** SHIP (cheaper and quality held), REJECT (quality fell beyond the bar, or cost rose),
94
+ INCONCLUSIVE (with how many tasks per window would settle it), or NO CLEAR SAVING.
95
+
96
+ On the demo app, applying the top recommendations gives:
97
+
98
+ ```
99
+ Verdict: INCONCLUSIVE (quality INCONCLUSIVE vs allowed −1 pt · cost LOWER)
100
+ Cost per successful task $0.0366 → $0.0214 (−41.6%, 95% CI of the change −$0.0188 to −$0.0117)
101
+ Spend / 30 days $107.94 → $41.23 · cache hit rate 0% → 41.7%
102
+ Success rate +0.8 pt, but the CI (−3.7 to +5.3) still allows a drop beyond −1 pt.
103
+ About 3,060 tasks per window would settle it.
104
+ ```
105
+
106
+ The saving is real; whether quality held within one point isn't knowable from a week of 480 tickets — so it
107
+ doesn't claim it. With a 5-point bar the same data says SHIP.
108
+
109
+ Guard evals count toward coverage only once their cases file has at least one case, and a test covers an
110
+ LLM call when it calls any function that leads to it (handler → helper → LLM wrapper).
111
+
112
+ ## Try a cheaper setup before changing code
113
+
114
+ ```bash
115
+ harnessdoc experiment plan harnessdoc.experiment.yaml # runs + estimated cost; calls nothing
116
+ harnessdoc experiment run harnessdoc.experiment.yaml --budget 0.50
117
+ ```
118
+
119
+ An experiment replays a call site's guard-eval cases through **your real code** under several variants —
120
+ another model (`model_overrides`), a different output cap (`max_tokens`), extra request settings
121
+ (`kwargs`, e.g. `cache_control`), or environment flags your app reads (`env`) — with no code edits.
122
+ harnessdoc briefly intercepts the Anthropic / OpenAI / Gemini SDK call to apply the variant and record usage,
123
+ scores every answer, and gives each variant the same SHIP / REJECT / INCONCLUSIVE verdict as `compare`.
124
+
125
+ It is the only part of harnessdoc that costs money, so:
126
+
127
+ - it uses **your** API keys from the environment — harnessdoc never reads or stores them;
128
+ - `--budget` is a hard cap: no new LLM call starts once it is reached (one in-flight call can overshoot by its
129
+ own cost), and the stop can't be swallowed by an app's `except Exception` retry loop;
130
+ - `run` shows the plan and asks before spending (unless `--yes`); over MCP, `run_experiment` refuses
131
+ without `user_approved=true` and a budget;
132
+ - variants are interleaved case by case, so drift doesn't favour one of them; prompts and outputs are never
133
+ written to disk.
134
+
135
+ - if a call can't be priced (a model missing from the price book, or a response with no token usage) the run
136
+ **stops immediately** — a budget can't be enforced on spend it can't see. `--allow-unpriced` overrides it.
137
+
138
+ The demo app ships two: [`harnessdoc.experiment.yaml`](examples/demo_app/harnessdoc.experiment.yaml) tests
139
+ the intent router on Haiku against its 24 labelled cases (72 runs, about one cent), and
140
+ [`harnessdoc.experiment.minimax.yaml`](examples/demo_app/harnessdoc.experiment.minimax.yaml) runs the same
141
+ question on MiniMax's Anthropic-compatible API (`ANTHROPIC_BASE_URL=https://api.minimax.io/anthropic` plus your
142
+ MiniMax key as `ANTHROPIC_API_KEY`), comparing `MiniMax-M3` with the half-price `MiniMax-M3.1-Flash`.
143
+
144
+ **First real run** (2026-11-06, MiniMax's API, 72 runs, $0.0023 of a $0.10 cap):
145
+
146
+ | Variant | Passed | Cost per successful task | Verdict |
147
+ |---|---|---|---|
148
+ | `MiniMax-M3` (baseline) | 22/24 | $0.0000417 | — |
149
+ | `MiniMax-M3.1-Flash` | 23/24 | $0.0000345 (−17%, CI crosses zero) | INCONCLUSIVE — ~118 tasks/window would settle it |
150
+ | Flash, `max_tokens: 16` | 14/24 | $0.0000409 | **REJECT** — success rate −33 pt |
151
+
152
+ Flash costs half as much per token but always thinks, and those tokens bill as output, so the saving was
153
+ ~17% rather than 50%. The 16-token cap broke it: thinking used the cap before the label was written — a
154
+ regression a cost-only comparison would have missed. 24 cases is a small sample; one case's difference between
155
+ the first two rows is noise.
156
+
157
+ ## What your coding agent costs (Claude Code logs)
158
+
159
+ ```bash
160
+ harnessdoc sessions # every session under ~/.claude/projects
161
+ harnessdoc sessions --since 14d --cwd ~/code/my-app --top 5
162
+ ```
163
+
164
+ Claude Code writes every session — and every subagent — to JSONL logs. `sessions` reads **only token counts,
165
+ model ids, timestamps and session ids (never your prompts, replies or tool output)** and prices them from the
166
+ price book: where the money goes (cache reads, output, 1-hour vs 5-minute cache writes, uncached input), cache
167
+ hit rate, cost by model and by day, subagents' share, and the costliest sessions.
168
+
169
+ Three details of the log format that a naive reader gets wrong, all handled and tested:
170
+
171
+ - **Each message is logged once per content block**, with the same usage every time. Summing records
172
+ over-counts; in the logs checked it inflated tokens by 1.8–2.95×. Records are de-duplicated by message id.
173
+ - **Cache writes have a TTL split** and the 1-hour write costs 2× input (5-minute: 1.25×). Most writes were
174
+ 1-hour, so the TTL is priced, not assumed.
175
+ - **Subagents are separate files** (`<session>/subagents/agent-*.jsonl`); skipping them hides a large share of
176
+ the spend, so they are read and reported separately.
177
+
178
+ Dollars are **API-equivalent at list prices** — a subscription plan doesn't bill per token — so treat the
179
+ breakdowns (what dominates, which sessions, how much subagents) as the actionable part.
180
+
181
+ ## CI gate
182
+
183
+ ```bash
184
+ harnessdoc check . --write-baseline harnessdoc-baseline.json # once: today's findings become accepted debt
185
+ harnessdoc check . --baseline harnessdoc-baseline.json --fail-on medium # every PR
186
+ ```
187
+
188
+ Findings are matched by rule + file + enclosing function, not line number, so unrelated edits don't make
189
+ old findings look new; fixed findings are listed. A ready-made GitHub Actions workflow that also uploads the
190
+ report is in [`examples/ci/harnessdoc.yml`](examples/ci/harnessdoc.yml).
191
+
192
+ ## Goals and the report
193
+
194
+ ```bash
195
+ harnessdoc goals init . # what the app does, what a successful task is, target, constraints, logs
196
+ harnessdoc report . # → harnessdoc-report.md
197
+ ```
198
+
199
+ The report states the goal, the baseline (cost per successful task vs target), recommendations ranked by
200
+ measured $/month with what to do first, which call sites need a guard eval before any change, every
201
+ finding in detail, and how each number was computed. It has no timestamp, so the same inputs produce the
202
+ same file and it can be diffed in a PR.
203
+
204
+ ## Scan a project
205
+
206
+ ```bash
207
+ uv venv .venv && uv pip install -p .venv -e ".[dev]"
208
+ .venv/bin/harnessdoc scan path/to/your/app # readable report
209
+ .venv/bin/harnessdoc scan path/to/your/app --json # stable JSON for tooling
210
+ .venv/bin/harnessdoc scan path/to/your/app --fail-on high # exit 1 in CI if any high finding
211
+ ```
212
+
213
+ The scanner parses your Python (it never imports or runs it), finds every LLM call made through the
214
+ Anthropic, OpenAI, Gemini or LiteLLM SDKs, resolves what goes into each call — model, output cap, system
215
+ prompt (followed across functions, constants, files and modules), tools, caching — and runs these checks:
216
+
217
+ | Rule | Finds | Saving estimate from |
218
+ |---|---|---|
219
+ | HD001 | Volatile values (`datetime.now()`, UUIDs…) at the start of a prompt that bust the cache | logs (later) |
220
+ | HD002 | A large static prefix sent to Anthropic without `cache_control` | price book: cache-read vs input |
221
+ | HD003 | Tool lists built by iterating a `set` (order differs per process → no shared cache) | logs (later) |
222
+ | HD004 | `while True` + `except: continue` retrying forever | — |
223
+ | HD005 | Agent loops with no iteration cap | — |
224
+ | HD006 | A premium model doing classification / extraction / short summaries | price book: small vs premium model |
225
+ | HD007 | `max_tokens` in the thousands for a one-word answer | — |
226
+ | HD008 | An LLM extracting something a regex can (IDs, emails, URLs) | 100% of the call when the regex matches |
227
+ | HD009 | Offline jobs calling the real-time API in a loop | price book: batch multiplier |
228
+ | HD010 | LLM calls no test/eval references (no quality guard) | — |
229
+
230
+ Every finding names the eval that guards the affected code, or says there is none. Savings are labelled
231
+ *estimates* until usage logs are joined (weeks 4–5).
232
+
233
+ **Benchmark:** on the [deliberately wasteful demo app](examples/demo_app) the scanner finds 10/10 planted
234
+ issues with 0 false positives (`tests/test_benchmark_demo.py`). That app was written by us, so we also ran it
235
+ on two real open-source apps — it found every SDK call in both, and the [case study](docs/case-study.md)
236
+ records the three bugs that exposed, what it can't see (models set at runtime), and what it didn't measure.
237
+
238
+ ## Measure real usage
239
+
240
+ ```bash
241
+ .venv/bin/harnessdoc measure logs/ # spend, cache hit rate, cost per successful task
242
+ .venv/bin/harnessdoc measure logs/ --scan . # + every finding priced in measured $/month
243
+ .venv/bin/harnessdoc measure logs/ --outcomes outcomes.csv # outcomes kept elsewhere (task_id,success)
244
+ ```
245
+
246
+ `measure` reads the logs you already have, auto-detected line by line: raw Anthropic / OpenAI (Chat and
247
+ Responses) / Gemini responses dumped as JSONL (optionally wrapped with `task_id`, `site`, `ts`),
248
+ OpenTelemetry GenAI spans (flat or OTLP JSON), gzip files and whole folders. No logs yet? Drop in the
249
+ dependency-free logger — it writes token counts and ids only, never prompts or completions:
250
+
251
+ ```python
252
+ from harnessdoc.log import UsageLogger
253
+ usage_log = UsageLogger("logs/llm_usage.jsonl")
254
+
255
+ response = client.messages.create(...)
256
+ usage_log.record(response, task_id=ticket.id) # site defaults to the calling function
257
+ usage_log.outcome(ticket.id, success=ticket.resolved) # once the task is over
258
+ ```
259
+
260
+ **Cost per successful task** = everything spent on tasks with a known outcome ÷ number of successes, so the
261
+ spend on failed tasks counts against the successes that carry it. It comes with a 95% confidence interval
262
+ (delta method over tasks), next to the success rate (Wilson interval) and per-task mean/median/p90. Spend
263
+ that belongs to no task outcome (e.g. a nightly batch job) is reported separately, not hidden.
264
+
265
+ With `--scan`, each finding is priced from its call site's measured spend, and the combined total does not
266
+ double count: savings on the same call site combine as `1 − Π(1 − share)`.
267
+
268
+ Try it on the demo app with a synthetic week of traffic:
269
+
270
+ ```bash
271
+ .venv/bin/python examples/demo_app/logs/generate_logs.py
272
+ .venv/bin/harnessdoc measure examples/demo_app/logs/usage.jsonl --scan examples/demo_app
273
+ # → cost per successful task $0.0366 (95% CI $0.0333–$0.0399) · combined potential ~$65 of ~$108 / month
274
+ ```
275
+
276
+ ## Try the cost engine
277
+
278
+ ```bash
279
+ .venv/bin/harnessdoc models --provider anthropic
280
+ .venv/bin/harnessdoc cost anthropic.claude-opus-5-5 --in 3000 --cache-read 6000 --out 400
281
+ .venv/bin/harnessdoc cost gpt-6.1-sol --cache-write 2000 --ttl 12h --json
282
+ ```
283
+
284
+ Model ids are resolved the way they appear in real code: Bedrock/LiteLLM/Vertex prefixes, dated snapshots and
285
+ `-latest` suffixes all map to the right price.
286
+
287
+ ## Prices
288
+
289
+ `src/harnessdoc/data/pricing.json` holds prices (USD per 1M tokens) and caching rules for Anthropic, OpenAI
290
+ and Gemini, each with its source URL and an `as_of` date, plus scheduled price changes and long-context
291
+ tiers. Providers change these often — override or add models per project with a `harnessdoc.pricing.json`
292
+ in your repo root (deep-merged over the defaults).
293
+
294
+ ## Benchmark: the wasteful demo app
295
+
296
+ [`examples/demo_app`](examples/demo_app) is a small support bot with 11 planted kinds of harness waste
297
+ (cache-busting timestamps, non-deterministic tool order, an Opus-tier model for classification, an LLM doing
298
+ a regex's job, a real-time API for a nightly batch, uncovered call sites…). The [answer key](examples/demo_app/WASTE.md)
299
+ is how the scanner's recall and false-positive rate are measured.
300
+
301
+ ## Known limits
302
+
303
+ - **Python only.** TypeScript/JS apps aren't scanned, and neither are calls made through frameworks such as
304
+ LangChain (the scanner follows the Anthropic, OpenAI, Gemini and LiteLLM SDKs directly).
305
+ - **Models set at runtime are invisible to the static scanner.** On two real open-source apps only 4 of 136 call
306
+ sites had a statically readable model, so cost rules like HD006 couldn't fire there. Usage logs (where the model
307
+ is recorded per request) cover this gap — that's why `measure` and `compare` exist.
308
+ - **Savings are estimates until `compare` says SHIP**, and `compare` says INCONCLUSIVE when the data can't
309
+ support a verdict (small samples) instead of declaring a winner.
310
+ - **No measured saving on a real app yet.** The target of ≥25% lower cost per successful task on an open-source
311
+ app needs one with real traffic logs; the demo-app numbers are synthetic.
312
+ - **`harnessdoc sessions` dollars are API-equivalent** list prices; a subscription plan isn't billed per token.
313
+ - **Prices change.** The price book is dated and each entry has its source; override per project with
314
+ `harnessdoc.pricing.json`.
315
+
316
+ ## Roadmap (what v0.1.0 contains)
317
+
318
+ | Area | Status |
319
+ |---|---|
320
+ | Price book + cost engine (Anthropic, OpenAI, Gemini, MiniMax) | ✅ |
321
+ | Python scanner, checks HD001–HD010, eval coverage, CI exit codes | ✅ |
322
+ | Usage analyzer: cost per successful task, findings priced in measured $/month | ✅ |
323
+ | MCP server (12 tools) + `/harnessdoc` skill + goals file + Markdown report | ✅ |
324
+ | `compare` (SHIP / REJECT / INCONCLUSIVE), guard-eval scaffolds, CI gate | ✅ |
325
+ | Experiment runner with a hard budget, tested on a real API | ✅ |
326
+ | Claude Code session-log analysis (`sessions`) | ✅ |
327
+ | Real-app measured saving, TypeScript scanning, resolving models from config | next |
328
+
329
+ ## Licence
330
+
331
+ MIT