harness-evaluator 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 (134) hide show
  1. harness_evaluator-0.1.0/.gitignore +42 -0
  2. harness_evaluator-0.1.0/LICENSE +21 -0
  3. harness_evaluator-0.1.0/PKG-INFO +333 -0
  4. harness_evaluator-0.1.0/README.md +295 -0
  5. harness_evaluator-0.1.0/pyproject.toml +89 -0
  6. harness_evaluator-0.1.0/runs/sample-minimal.yaml +32 -0
  7. harness_evaluator-0.1.0/runs/sample-run.yaml +59 -0
  8. harness_evaluator-0.1.0/runs/task-mix.yaml +92 -0
  9. harness_evaluator-0.1.0/src/harness_evaluator/__init__.py +3 -0
  10. harness_evaluator-0.1.0/src/harness_evaluator/adapters/__init__.py +1 -0
  11. harness_evaluator-0.1.0/src/harness_evaluator/adapters/base.py +232 -0
  12. harness_evaluator-0.1.0/src/harness_evaluator/adapters/claude_code.py +126 -0
  13. harness_evaluator-0.1.0/src/harness_evaluator/adapters/codex.py +102 -0
  14. harness_evaluator-0.1.0/src/harness_evaluator/adapters/omp.py +83 -0
  15. harness_evaluator-0.1.0/src/harness_evaluator/adapters/opencode.py +83 -0
  16. harness_evaluator-0.1.0/src/harness_evaluator/adapters/pi.py +83 -0
  17. harness_evaluator-0.1.0/src/harness_evaluator/adapters/registry.py +68 -0
  18. harness_evaluator-0.1.0/src/harness_evaluator/adapters/utils.py +88 -0
  19. harness_evaluator-0.1.0/src/harness_evaluator/cli.py +533 -0
  20. harness_evaluator-0.1.0/src/harness_evaluator/dashboard/__init__.py +1 -0
  21. harness_evaluator-0.1.0/src/harness_evaluator/dashboard/app.py +352 -0
  22. harness_evaluator-0.1.0/src/harness_evaluator/dashboard/templates/index.html +46 -0
  23. harness_evaluator-0.1.0/src/harness_evaluator/dashboard/templates/run_detail.html +161 -0
  24. harness_evaluator-0.1.0/src/harness_evaluator/evaluator/__init__.py +1 -0
  25. harness_evaluator-0.1.0/src/harness_evaluator/evaluator/open_ended.py +804 -0
  26. harness_evaluator-0.1.0/src/harness_evaluator/evaluator/swe.py +356 -0
  27. harness_evaluator-0.1.0/src/harness_evaluator/evaluator/utils.py +90 -0
  28. harness_evaluator-0.1.0/src/harness_evaluator/gateway/__init__.py +1 -0
  29. harness_evaluator-0.1.0/src/harness_evaluator/gateway/canary.py +164 -0
  30. harness_evaluator-0.1.0/src/harness_evaluator/gateway/models.py +182 -0
  31. harness_evaluator-0.1.0/src/harness_evaluator/gateway/parsers/__init__.py +1 -0
  32. harness_evaluator-0.1.0/src/harness_evaluator/gateway/parsers/anthropic.py +95 -0
  33. harness_evaluator-0.1.0/src/harness_evaluator/gateway/parsers/openai.py +108 -0
  34. harness_evaluator-0.1.0/src/harness_evaluator/gateway/proxy.py +663 -0
  35. harness_evaluator-0.1.0/src/harness_evaluator/gateway/reconcile.py +130 -0
  36. harness_evaluator-0.1.0/src/harness_evaluator/gateway/store.py +157 -0
  37. harness_evaluator-0.1.0/src/harness_evaluator/orchestrator/__init__.py +1 -0
  38. harness_evaluator-0.1.0/src/harness_evaluator/orchestrator/config.py +401 -0
  39. harness_evaluator-0.1.0/src/harness_evaluator/orchestrator/engine.py +434 -0
  40. harness_evaluator-0.1.0/src/harness_evaluator/orchestrator/results_store.py +249 -0
  41. harness_evaluator-0.1.0/src/harness_evaluator/reporting/__init__.py +1 -0
  42. harness_evaluator-0.1.0/src/harness_evaluator/reporting/static_report.py +362 -0
  43. harness_evaluator-0.1.0/src/harness_evaluator/runner/__init__.py +1 -0
  44. harness_evaluator-0.1.0/src/harness_evaluator/runner/docker.py +776 -0
  45. harness_evaluator-0.1.0/src/harness_evaluator/stats/__init__.py +604 -0
  46. harness_evaluator-0.1.0/tasks/open-design-001.yaml +43 -0
  47. harness_evaluator-0.1.0/tasks/open-design-002.yaml +51 -0
  48. harness_evaluator-0.1.0/tasks/open-design-003.yaml +59 -0
  49. harness_evaluator-0.1.0/tasks/open-design-004.yaml +57 -0
  50. harness_evaluator-0.1.0/tasks/open-design-005.yaml +61 -0
  51. harness_evaluator-0.1.0/tasks/open-design-006.yaml +72 -0
  52. harness_evaluator-0.1.0/tasks/open-design-007.yaml +74 -0
  53. harness_evaluator-0.1.0/tasks/open-design-008.yaml +82 -0
  54. harness_evaluator-0.1.0/tasks/repos/swe-bugfix-001/.gitignore +2 -0
  55. harness_evaluator-0.1.0/tasks/repos/swe-bugfix-001/requirements.txt +1 -0
  56. harness_evaluator-0.1.0/tasks/repos/swe-bugfix-001/src/__init__.py +0 -0
  57. harness_evaluator-0.1.0/tasks/repos/swe-bugfix-001/src/solution.py +18 -0
  58. harness_evaluator-0.1.0/tasks/repos/swe-bugfix-001/tests/__init__.py +0 -0
  59. harness_evaluator-0.1.0/tasks/repos/swe-bugfix-001/tests/test_solution.py +24 -0
  60. harness_evaluator-0.1.0/tasks/repos/swe-bugfix-002/.gitignore +2 -0
  61. harness_evaluator-0.1.0/tasks/repos/swe-bugfix-002/requirements.txt +1 -0
  62. harness_evaluator-0.1.0/tasks/repos/swe-bugfix-002/src/__init__.py +0 -0
  63. harness_evaluator-0.1.0/tasks/repos/swe-bugfix-002/src/solution.py +15 -0
  64. harness_evaluator-0.1.0/tasks/repos/swe-bugfix-002/tests/__init__.py +0 -0
  65. harness_evaluator-0.1.0/tasks/repos/swe-bugfix-002/tests/test_solution.py +23 -0
  66. harness_evaluator-0.1.0/tasks/repos/swe-bugfix-003/.gitignore +3 -0
  67. harness_evaluator-0.1.0/tasks/repos/swe-bugfix-003/requirements.txt +1 -0
  68. harness_evaluator-0.1.0/tasks/repos/swe-bugfix-003/src/__init__.py +0 -0
  69. harness_evaluator-0.1.0/tasks/repos/swe-bugfix-003/src/solution.py +21 -0
  70. harness_evaluator-0.1.0/tasks/repos/swe-bugfix-003/tests/__init__.py +0 -0
  71. harness_evaluator-0.1.0/tasks/repos/swe-bugfix-003/tests/test_solution.py +19 -0
  72. harness_evaluator-0.1.0/tasks/repos/swe-bugfix-004/.gitignore +3 -0
  73. harness_evaluator-0.1.0/tasks/repos/swe-bugfix-004/requirements.txt +2 -0
  74. harness_evaluator-0.1.0/tasks/repos/swe-bugfix-004/src/__init__.py +0 -0
  75. harness_evaluator-0.1.0/tasks/repos/swe-bugfix-004/src/solution.py +42 -0
  76. harness_evaluator-0.1.0/tasks/repos/swe-bugfix-004/tests/__init__.py +0 -0
  77. harness_evaluator-0.1.0/tasks/repos/swe-bugfix-004/tests/test_solution.py +26 -0
  78. harness_evaluator-0.1.0/tasks/repos/swe-bugfix-005/.gitignore +3 -0
  79. harness_evaluator-0.1.0/tasks/repos/swe-bugfix-005/package.json +7 -0
  80. harness_evaluator-0.1.0/tasks/repos/swe-bugfix-005/src/solution.ts +23 -0
  81. harness_evaluator-0.1.0/tasks/repos/swe-bugfix-005/tests/test_solution.test.ts +12 -0
  82. harness_evaluator-0.1.0/tasks/repos/swe-bugfix-005/tsconfig.json +11 -0
  83. harness_evaluator-0.1.0/tasks/repos/swe-feature-001/.gitignore +2 -0
  84. harness_evaluator-0.1.0/tasks/repos/swe-feature-001/requirements.txt +1 -0
  85. harness_evaluator-0.1.0/tasks/repos/swe-feature-001/src/__init__.py +0 -0
  86. harness_evaluator-0.1.0/tasks/repos/swe-feature-001/src/solution.py +35 -0
  87. harness_evaluator-0.1.0/tasks/repos/swe-feature-001/tests/__init__.py +0 -0
  88. harness_evaluator-0.1.0/tasks/repos/swe-feature-001/tests/test_solution.py +40 -0
  89. harness_evaluator-0.1.0/tasks/repos/swe-feature-002/.gitignore +3 -0
  90. harness_evaluator-0.1.0/tasks/repos/swe-feature-002/requirements.txt +2 -0
  91. harness_evaluator-0.1.0/tasks/repos/swe-feature-002/src/__init__.py +0 -0
  92. harness_evaluator-0.1.0/tasks/repos/swe-feature-002/src/solution.py +26 -0
  93. harness_evaluator-0.1.0/tasks/repos/swe-feature-002/tests/__init__.py +0 -0
  94. harness_evaluator-0.1.0/tasks/repos/swe-feature-002/tests/test_solution.py +27 -0
  95. harness_evaluator-0.1.0/tasks/repos/swe-feature-003/.gitignore +3 -0
  96. harness_evaluator-0.1.0/tasks/repos/swe-feature-003/package.json +7 -0
  97. harness_evaluator-0.1.0/tasks/repos/swe-feature-003/src/solution.ts +18 -0
  98. harness_evaluator-0.1.0/tasks/repos/swe-feature-003/tests/test_solution.test.ts +24 -0
  99. harness_evaluator-0.1.0/tasks/repos/swe-feature-003/tsconfig.json +11 -0
  100. harness_evaluator-0.1.0/tasks/repos/swe-perf-001/.gitignore +3 -0
  101. harness_evaluator-0.1.0/tasks/repos/swe-perf-001/requirements.txt +1 -0
  102. harness_evaluator-0.1.0/tasks/repos/swe-perf-001/src/__init__.py +0 -0
  103. harness_evaluator-0.1.0/tasks/repos/swe-perf-001/src/solution.py +23 -0
  104. harness_evaluator-0.1.0/tasks/repos/swe-perf-001/tests/__init__.py +0 -0
  105. harness_evaluator-0.1.0/tasks/repos/swe-perf-001/tests/test_solution.py +13 -0
  106. harness_evaluator-0.1.0/tasks/repos/swe-perf-002/.gitignore +3 -0
  107. harness_evaluator-0.1.0/tasks/repos/swe-perf-002/package.json +7 -0
  108. harness_evaluator-0.1.0/tasks/repos/swe-perf-002/src/solution.ts +28 -0
  109. harness_evaluator-0.1.0/tasks/repos/swe-perf-002/tests/test_solution.test.ts +12 -0
  110. harness_evaluator-0.1.0/tasks/repos/swe-perf-002/tsconfig.json +11 -0
  111. harness_evaluator-0.1.0/tasks/repos/swe-refactor-001/.gitignore +2 -0
  112. harness_evaluator-0.1.0/tasks/repos/swe-refactor-001/requirements.txt +1 -0
  113. harness_evaluator-0.1.0/tasks/repos/swe-refactor-001/src/__init__.py +0 -0
  114. harness_evaluator-0.1.0/tasks/repos/swe-refactor-001/src/solution.py +36 -0
  115. harness_evaluator-0.1.0/tasks/repos/swe-refactor-001/tests/__init__.py +0 -0
  116. harness_evaluator-0.1.0/tasks/repos/swe-refactor-001/tests/test_solution.py +30 -0
  117. harness_evaluator-0.1.0/tasks/repos/swe-refactor-002/.gitignore +3 -0
  118. harness_evaluator-0.1.0/tasks/repos/swe-refactor-002/requirements.txt +1 -0
  119. harness_evaluator-0.1.0/tasks/repos/swe-refactor-002/src/__init__.py +0 -0
  120. harness_evaluator-0.1.0/tasks/repos/swe-refactor-002/src/solution.py +27 -0
  121. harness_evaluator-0.1.0/tasks/repos/swe-refactor-002/tests/__init__.py +0 -0
  122. harness_evaluator-0.1.0/tasks/repos/swe-refactor-002/tests/test_solution.py +21 -0
  123. harness_evaluator-0.1.0/tasks/swe-bugfix-001.yaml +67 -0
  124. harness_evaluator-0.1.0/tasks/swe-bugfix-002.yaml +71 -0
  125. harness_evaluator-0.1.0/tasks/swe-bugfix-003.yaml +67 -0
  126. harness_evaluator-0.1.0/tasks/swe-bugfix-004.yaml +76 -0
  127. harness_evaluator-0.1.0/tasks/swe-bugfix-005.yaml +64 -0
  128. harness_evaluator-0.1.0/tasks/swe-feature-001.yaml +93 -0
  129. harness_evaluator-0.1.0/tasks/swe-feature-002.yaml +91 -0
  130. harness_evaluator-0.1.0/tasks/swe-feature-003.yaml +92 -0
  131. harness_evaluator-0.1.0/tasks/swe-perf-001.yaml +62 -0
  132. harness_evaluator-0.1.0/tasks/swe-perf-002.yaml +83 -0
  133. harness_evaluator-0.1.0/tasks/swe-refactor-001.yaml +96 -0
  134. harness_evaluator-0.1.0/tasks/swe-refactor-002.yaml +98 -0
@@ -0,0 +1,42 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.egg-info/
6
+ *.egg
7
+ build/
8
+
9
+ # Virtual environments
10
+ .venv/
11
+ venv/
12
+
13
+ # Distribution
14
+ dist/
15
+
16
+ # IDE
17
+ .idea/
18
+ .vscode/
19
+ *.swp
20
+ *.swo
21
+
22
+ # Test/coverage
23
+ .pytest_cache/
24
+ .coverage
25
+ htmlcov/
26
+ .mypy_cache/
27
+ .ruff_cache/
28
+
29
+ # Runtime data
30
+ heval_workdir/
31
+ heval_results.db
32
+ heval_gateway.db
33
+ *.db
34
+
35
+ # OS
36
+ .DS_Store
37
+ Thumbs.db
38
+
39
+ # Astro site
40
+ site/node_modules/
41
+ site/dist/
42
+ site/.astro/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 yorch
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,333 @@
1
+ Metadata-Version: 2.5
2
+ Name: harness-evaluator
3
+ Version: 0.1.0
4
+ Summary: Harness evaluator: compare agentic coding harnesses on token/time/cost effectiveness
5
+ Project-URL: Homepage, https://github.com/yorch/harness-evaluator
6
+ Project-URL: Documentation, https://yorch.github.io/harness-evaluator/
7
+ Project-URL: Source, https://github.com/yorch/harness-evaluator
8
+ Project-URL: Issues, https://github.com/yorch/harness-evaluator/issues
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Software Development :: Testing
18
+ Requires-Python: >=3.11
19
+ Requires-Dist: aiohttp>=3.9
20
+ Requires-Dist: fastapi>=0.110
21
+ Requires-Dist: httpx>=0.27
22
+ Requires-Dist: jinja2>=3.1
23
+ Requires-Dist: pandas>=2.0
24
+ Requires-Dist: pydantic>=2.0
25
+ Requires-Dist: pyyaml>=6.0
26
+ Requires-Dist: rich>=13.0
27
+ Requires-Dist: statsmodels>=0.14
28
+ Requires-Dist: typer>=0.12
29
+ Requires-Dist: uvicorn>=0.29
30
+ Provides-Extra: dev
31
+ Requires-Dist: mypy>=1.10; extra == 'dev'
32
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
33
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
34
+ Requires-Dist: pytest>=8.0; extra == 'dev'
35
+ Requires-Dist: ruff>=0.5; extra == 'dev'
36
+ Requires-Dist: types-pyyaml>=6.0; extra == 'dev'
37
+ Description-Content-Type: text/markdown
38
+
39
+ # harness-evaluator
40
+
41
+ [![CI](https://github.com/yorch/harness-evaluator/actions/workflows/ci.yml/badge.svg)](https://github.com/yorch/harness-evaluator/actions/workflows/ci.yml)
42
+ [![Docker](https://github.com/yorch/harness-evaluator/actions/workflows/docker.yml/badge.svg)](https://github.com/yorch/harness-evaluator/actions/workflows/docker.yml)
43
+ [![Docs Site](https://github.com/yorch/harness-evaluator/actions/workflows/astro.yml/badge.svg)](https://yorch.github.io/harness-evaluator/)
44
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
45
+
46
+ Harness evaluator: compare agentic coding harnesses (Claude Code, Codex, Pi,
47
+ OpenCode, OMP) on token efficiency, task effectiveness, and time efficiency.
48
+
49
+ See [DESIGN.md](DESIGN.md) for the full design specification and
50
+ [the docs site](https://yorch.github.io/harness-evaluator/) for comprehensive documentation.
51
+
52
+ ## Quick start
53
+
54
+ No clone required — harness-evaluator bundles its task library and publishes to PyPI as `harness-evaluator`:
55
+
56
+ ```bash
57
+ uvx harness-evaluator init # scaffold harness-evaluator.yaml
58
+ docker pull ghcr.io/yorch/harness-evaluator-runner:latest # pull the runner image
59
+ export ANTHROPIC_API_KEY=sk-ant-...
60
+ uvx harness-evaluator gateway --port 8877 # separate terminal
61
+ uvx harness-evaluator run harness-evaluator.yaml
62
+ ```
63
+
64
+ ### From source
65
+
66
+ ```bash
67
+ # 1. Install dependencies
68
+ uv sync --extra dev
69
+
70
+ # 2. Pull the pre-built Docker image (or build locally with: docker build -t harness-evaluator-runner:latest .)
71
+ docker pull ghcr.io/yorch/harness-evaluator-runner:latest
72
+
73
+ # 3. Set API keys
74
+ export ANTHROPIC_API_KEY=sk-ant-...
75
+ export OPENAI_API_KEY=sk-...
76
+
77
+ # 4. Start the gateway proxy (in a separate terminal)
78
+ harness-evaluator gateway --port 8877
79
+
80
+ # 5. Run a minimal evaluation (1 harness, 1 model, 1 task, 1 repeat)
81
+ harness-evaluator run runs/sample-minimal.yaml
82
+
83
+ # Or dry-run to see the matrix without executing
84
+ harness-evaluator run runs/sample-minimal.yaml --dry-run
85
+
86
+ # Curated 20-task mix (5 harnesses × 2 models × 20 tasks × 3 repeats = 600 cells)
87
+ harness-evaluator run runs/task-mix.yaml --dry-run
88
+ ```
89
+
90
+ ## Docker image
91
+
92
+ The runner executes harnesses inside an isolated Docker container. A pre-built
93
+ image is available on GHCR (recommended), or you can build locally:
94
+
95
+ ```bash
96
+ # Pull the pre-built image (recommended)
97
+ docker pull ghcr.io/yorch/harness-evaluator-runner:latest
98
+
99
+ # Or build locally
100
+ docker build -t harness-evaluator-runner:latest .
101
+ ```
102
+
103
+ ## M1: Gateway Proxy (completed)
104
+
105
+ The gateway proxy is a custom HTTP/SSE server that sits between a harness and
106
+ the provider API, capturing every call's token usage, cost, and latency.
107
+
108
+ See [docs/gateway-proxy.md](docs/gateway-proxy.md) for a detailed explanation
109
+ with architecture diagram, token parsing, storage schema, and configuration
110
+ reference.
111
+
112
+ ### Running the proxy
113
+
114
+ ```bash
115
+ harness-evaluator gateway --port 8877
116
+ ```
117
+
118
+ Configure harnesses to route through the proxy:
119
+ ```bash
120
+ export ANTHROPIC_BASE_URL=http://127.0.0.1:8877
121
+ export OPENAI_BASE_URL=http://127.0.0.1:8877
122
+ ```
123
+
124
+ ### Running the canary
125
+
126
+ After sending a request through the proxy, verify token capture accuracy:
127
+ ```bash
128
+ harness-evaluator canary --tolerance 1.0
129
+ ```
130
+
131
+ ### What the proxy captures
132
+
133
+ - **Token usage**: input, output, cache-read, cache-write, reasoning tokens
134
+ - **Cost**: calculated from a pricing table per model
135
+ - **Latency**: wall-clock time per API call
136
+ - **Full request/response**: headers and bodies stored in SQLite
137
+ - **Streaming support**: real-time SSE parsing for both Anthropic and OpenAI
138
+
139
+ ### Observability tiers
140
+
141
+ - **full**: open harness, all metadata captured
142
+ - **partial**: closed harness, provider traffic captured via proxy
143
+ - **minimal**: closed harness, only total spend via billing API
144
+
145
+ ### Reconciliation
146
+
147
+ Token usage from proxy, billing API, and harness self-report are reconciled
148
+ with per-harness tolerance bands. Discrepancies are flagged as a transparency
149
+ metric.
150
+
151
+ ## M2: Core Pipeline (completed)
152
+
153
+ The core pipeline handles eval matrix building, execution, and reporting.
154
+
155
+ ### Running an eval
156
+
157
+ ```bash
158
+ # Dry run (print the matrix without executing)
159
+ harness-evaluator run runs/sample-run.yaml --dry-run
160
+
161
+ # Execute the eval
162
+ harness-evaluator run runs/sample-run.yaml
163
+
164
+ # Generate reports
165
+ harness-evaluator report broad-first-pass --output ./reports
166
+
167
+ # View results in console
168
+ harness-evaluator results broad-first-pass
169
+ ```
170
+
171
+ ### Run configuration
172
+
173
+ Eval runs are configured via YAML files (see `runs/sample-run.yaml`):
174
+ - `harnesses`: list of harness specs (name, adapter, observability tier)
175
+ - `models`: list of model specs (name, provider, API key env var)
176
+ - `tasks`: list of task IDs or `*` for all tasks in the library
177
+ - `repeats`: number of repeats per cell (default 5)
178
+ - `budget_usd`: maximum total spend (optional)
179
+ - `parallel_runs`: number of parallel container runs (default 1)
180
+
181
+ ### Task definitions
182
+
183
+ Tasks are defined as YAML files in a task library directory (see `tasks/`):
184
+ - `track`: `swe` (hidden tests) or `open_ended` (LLM judge)
185
+ - `task_prompt`: the prompt given to the harness
186
+ - `test_command`: command to run tests
187
+ - `test_patch`: hidden test patch applied before evaluation
188
+ - `timeout_seconds`: per-task timeout
189
+
190
+ ### Features
191
+
192
+ - **Matrix building**: harness × model × task × repeat
193
+ - **Budget caps**: stops when $ budget exhausted
194
+ - **Cell-level resumability**: skips completed cells on re-run
195
+ - **Retry logic**: transient failures retried with exponential backoff
196
+ - **Exit classes**: PASS, FAIL, RETRYABLE_KILL, NON_RETRYABLE_KILL
197
+ - **Partial credit**: fraction of tests passing
198
+ - **Error classification**: success, partial, overfit, timeout, refusal, wrong_approach, crash, no_change
199
+ - **Reports**: HTML, JSON, CSV with within-model leaderboards
200
+
201
+ ## M3: Harness Adapters (completed)
202
+
203
+ Adapters wrap each coding harness with a uniform interface for the runner.
204
+
205
+ ### Supported harnesses
206
+
207
+ | Harness | Adapter | Observability | Notes |
208
+ |---------|---------|--------------|-------|
209
+ | OpenCode | `opencode` | full | Open-source, system prompt visible |
210
+ | Claude Code | `claude-code` | partial | Closed, proxy captures traffic |
211
+ | Codex | `codex` | partial | Closed, proxy captures traffic |
212
+ | Pi | `pi` | minimal | May bypass proxy |
213
+ | OMP | `omp` | minimal | May bypass proxy |
214
+
215
+ ### Listing adapters
216
+
217
+ ```bash
218
+ harness-evaluator adapters
219
+ ```
220
+
221
+ ### Observability tiers
222
+
223
+ - **full**: Open/cooperating harness. System prompts, tool definitions, context
224
+ strategy, and turn-level metadata are available.
225
+ - **partial**: Closed harness but provider traffic is captured through the
226
+ gateway proxy. Token usage and cost are accurately attributed.
227
+ - **minimal**: Only total spend or billing data is available. Traffic may
228
+ bypass the proxy. Cost accounting relies on billing reconciliation.
229
+
230
+ ### Adapter design
231
+
232
+ Each adapter implements:
233
+ - `prepare()`: Check/install the harness
234
+ - `run(task_prompt, timeout)`: Execute the harness non-interactively
235
+ - `cleanup()`: Clean up after the run
236
+ - `get_env()`: Set gateway proxy env vars and API keys
237
+
238
+ The adapter registry (`harness_evaluator.adapters.registry`) loads adapters by name and
239
+ the Docker runner uses it to dispatch to the correct adapter based on the
240
+ run config's `harness.adapter` field.
241
+
242
+ ## M4: Open-Ended Track (completed)
243
+
244
+ The open-ended track evaluates tasks without a single correct answer using a
245
+ frozen LLM judge, structured rubric, and structural checks.
246
+
247
+ ### Components
248
+
249
+ - **Frozen Judge** (`FrozenJudge`): Versioned LLM judge with an immutable prompt.
250
+ Version `v1.0` is the initial frozen prompt. Changing the prompt requires
251
+ bumping the version, which invalidates prior calibration data.
252
+ - **Rubric** (`Rubric`): Weighted criteria with 0-5 scoring scale. Default
253
+ rubric includes correctness (3x), completeness (2x), code_quality (1.5x),
254
+ test_quality (1.5x), documentation (1x).
255
+ - **Structural Checks** (`StructuralChecker`): Verifies file existence, Python
256
+ syntax, and test command execution. Structural failures cap the composite
257
+ success at 0.5.
258
+ - **Calibration** (`CalibrationSet`): Anchor submissions with known expected
259
+ scores for drift detection. Mean absolute error > 0.15 flags drift.
260
+
261
+ ### Running calibration
262
+
263
+ ```bash
264
+ export ANTHROPIC_API_KEY=sk-ant-...
265
+ harness-evaluator calibrate --model claude-sonnet-4-20250514
266
+ ```
267
+
268
+ ### Evaluation flow
269
+
270
+ 1. Get git diff of changes
271
+ 2. Run structural checks (file existence, syntax, tests)
272
+ 3. Run LLM judge against rubric
273
+ 4. Composite score: judge score, capped at 0.5 if structural checks fail
274
+ 5. Pass threshold: 0.7
275
+
276
+ ## M5: Dashboard (completed)
277
+
278
+ Interactive FastAPI web dashboard for exploring eval results.
279
+
280
+ ### Starting the dashboard
281
+
282
+ ```bash
283
+ harness-evaluator dashboard --port 8080
284
+ ```
285
+
286
+ Then open http://127.0.0.1:8080 in your browser.
287
+
288
+ ### Features
289
+
290
+ - **Run overview**: List all runs with summary stats (cells, passed, failed, cost)
291
+ - **Run detail**: Per-run view with leaderboards and filtered results table
292
+ - **Filtering**: Filter by model, harness, task track, and minimum success rate
293
+ - **Leaderboards**: Within-model harness comparison sorted by success rate
294
+ - **REST API**: JSON endpoints for programmatic access:
295
+ - `GET /api/runs` — list all runs
296
+ - `GET /api/run/{name}` — get filtered results
297
+ - `GET /api/run/{name}/leaderboard` — get leaderboard data
298
+
299
+ ## M6: Statistics (completed)
300
+
301
+ Statistical analysis of evaluation results, including mixed-effects modeling,
302
+ variance decomposition, bootstrap confidence intervals, and consistency analysis.
303
+
304
+ ### Running stats
305
+
306
+ ```bash
307
+ harness-evaluator stats my-run --db harness_evaluator_results.db
308
+ ```
309
+
310
+ ### Components
311
+
312
+ - **Mixed-Effects Model**: `success ~ C(harness) + C(model) + (1|task)`
313
+ Treats harness and model as fixed effects, task as a random effect.
314
+ Reports coefficients, standard errors, p-values, and confidence intervals.
315
+ - **Variance Decomposition**: Partitions variance into harness, model, task,
316
+ and residual components. Reports percentage of total variance explained.
317
+ - **Bootstrap CIs**: Non-parametric bootstrap confidence intervals (default
318
+ 1000 resamples, 95% CI) for success rate by harness.
319
+ - **Consistency Analysis**: Per harness × model combination, reports mean,
320
+ std, coefficient of variation, min/max success, and bootstrap CI.
321
+ - **Warnings**: Automatically warns when sample size is too small (<30)
322
+ or when the mixed-effects model fails to converge.
323
+
324
+ ## Architecture
325
+
326
+ - **Gateway**: custom HTTP/SSE proxy that intercepts provider calls and captures
327
+ token usage, cost, and latency with full request/response logging.
328
+ - **Orchestrator**: builds the eval matrix (harness × model × task × repeats),
329
+ manages budget caps, and handles cell-level resumability.
330
+ - **Runner**: Docker-based isolation, one container per eval cell.
331
+ - **Adapters**: per-harness integration (Python core + TS shims where needed).
332
+ - **Evaluator**: SWE-bench-style (hidden tests) and open-ended (LLM judge) tracks.
333
+ - **Reporting**: CLI reports + static HTML + interactive web dashboard.
@@ -0,0 +1,295 @@
1
+ # harness-evaluator
2
+
3
+ [![CI](https://github.com/yorch/harness-evaluator/actions/workflows/ci.yml/badge.svg)](https://github.com/yorch/harness-evaluator/actions/workflows/ci.yml)
4
+ [![Docker](https://github.com/yorch/harness-evaluator/actions/workflows/docker.yml/badge.svg)](https://github.com/yorch/harness-evaluator/actions/workflows/docker.yml)
5
+ [![Docs Site](https://github.com/yorch/harness-evaluator/actions/workflows/astro.yml/badge.svg)](https://yorch.github.io/harness-evaluator/)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ Harness evaluator: compare agentic coding harnesses (Claude Code, Codex, Pi,
9
+ OpenCode, OMP) on token efficiency, task effectiveness, and time efficiency.
10
+
11
+ See [DESIGN.md](DESIGN.md) for the full design specification and
12
+ [the docs site](https://yorch.github.io/harness-evaluator/) for comprehensive documentation.
13
+
14
+ ## Quick start
15
+
16
+ No clone required — harness-evaluator bundles its task library and publishes to PyPI as `harness-evaluator`:
17
+
18
+ ```bash
19
+ uvx harness-evaluator init # scaffold harness-evaluator.yaml
20
+ docker pull ghcr.io/yorch/harness-evaluator-runner:latest # pull the runner image
21
+ export ANTHROPIC_API_KEY=sk-ant-...
22
+ uvx harness-evaluator gateway --port 8877 # separate terminal
23
+ uvx harness-evaluator run harness-evaluator.yaml
24
+ ```
25
+
26
+ ### From source
27
+
28
+ ```bash
29
+ # 1. Install dependencies
30
+ uv sync --extra dev
31
+
32
+ # 2. Pull the pre-built Docker image (or build locally with: docker build -t harness-evaluator-runner:latest .)
33
+ docker pull ghcr.io/yorch/harness-evaluator-runner:latest
34
+
35
+ # 3. Set API keys
36
+ export ANTHROPIC_API_KEY=sk-ant-...
37
+ export OPENAI_API_KEY=sk-...
38
+
39
+ # 4. Start the gateway proxy (in a separate terminal)
40
+ harness-evaluator gateway --port 8877
41
+
42
+ # 5. Run a minimal evaluation (1 harness, 1 model, 1 task, 1 repeat)
43
+ harness-evaluator run runs/sample-minimal.yaml
44
+
45
+ # Or dry-run to see the matrix without executing
46
+ harness-evaluator run runs/sample-minimal.yaml --dry-run
47
+
48
+ # Curated 20-task mix (5 harnesses × 2 models × 20 tasks × 3 repeats = 600 cells)
49
+ harness-evaluator run runs/task-mix.yaml --dry-run
50
+ ```
51
+
52
+ ## Docker image
53
+
54
+ The runner executes harnesses inside an isolated Docker container. A pre-built
55
+ image is available on GHCR (recommended), or you can build locally:
56
+
57
+ ```bash
58
+ # Pull the pre-built image (recommended)
59
+ docker pull ghcr.io/yorch/harness-evaluator-runner:latest
60
+
61
+ # Or build locally
62
+ docker build -t harness-evaluator-runner:latest .
63
+ ```
64
+
65
+ ## M1: Gateway Proxy (completed)
66
+
67
+ The gateway proxy is a custom HTTP/SSE server that sits between a harness and
68
+ the provider API, capturing every call's token usage, cost, and latency.
69
+
70
+ See [docs/gateway-proxy.md](docs/gateway-proxy.md) for a detailed explanation
71
+ with architecture diagram, token parsing, storage schema, and configuration
72
+ reference.
73
+
74
+ ### Running the proxy
75
+
76
+ ```bash
77
+ harness-evaluator gateway --port 8877
78
+ ```
79
+
80
+ Configure harnesses to route through the proxy:
81
+ ```bash
82
+ export ANTHROPIC_BASE_URL=http://127.0.0.1:8877
83
+ export OPENAI_BASE_URL=http://127.0.0.1:8877
84
+ ```
85
+
86
+ ### Running the canary
87
+
88
+ After sending a request through the proxy, verify token capture accuracy:
89
+ ```bash
90
+ harness-evaluator canary --tolerance 1.0
91
+ ```
92
+
93
+ ### What the proxy captures
94
+
95
+ - **Token usage**: input, output, cache-read, cache-write, reasoning tokens
96
+ - **Cost**: calculated from a pricing table per model
97
+ - **Latency**: wall-clock time per API call
98
+ - **Full request/response**: headers and bodies stored in SQLite
99
+ - **Streaming support**: real-time SSE parsing for both Anthropic and OpenAI
100
+
101
+ ### Observability tiers
102
+
103
+ - **full**: open harness, all metadata captured
104
+ - **partial**: closed harness, provider traffic captured via proxy
105
+ - **minimal**: closed harness, only total spend via billing API
106
+
107
+ ### Reconciliation
108
+
109
+ Token usage from proxy, billing API, and harness self-report are reconciled
110
+ with per-harness tolerance bands. Discrepancies are flagged as a transparency
111
+ metric.
112
+
113
+ ## M2: Core Pipeline (completed)
114
+
115
+ The core pipeline handles eval matrix building, execution, and reporting.
116
+
117
+ ### Running an eval
118
+
119
+ ```bash
120
+ # Dry run (print the matrix without executing)
121
+ harness-evaluator run runs/sample-run.yaml --dry-run
122
+
123
+ # Execute the eval
124
+ harness-evaluator run runs/sample-run.yaml
125
+
126
+ # Generate reports
127
+ harness-evaluator report broad-first-pass --output ./reports
128
+
129
+ # View results in console
130
+ harness-evaluator results broad-first-pass
131
+ ```
132
+
133
+ ### Run configuration
134
+
135
+ Eval runs are configured via YAML files (see `runs/sample-run.yaml`):
136
+ - `harnesses`: list of harness specs (name, adapter, observability tier)
137
+ - `models`: list of model specs (name, provider, API key env var)
138
+ - `tasks`: list of task IDs or `*` for all tasks in the library
139
+ - `repeats`: number of repeats per cell (default 5)
140
+ - `budget_usd`: maximum total spend (optional)
141
+ - `parallel_runs`: number of parallel container runs (default 1)
142
+
143
+ ### Task definitions
144
+
145
+ Tasks are defined as YAML files in a task library directory (see `tasks/`):
146
+ - `track`: `swe` (hidden tests) or `open_ended` (LLM judge)
147
+ - `task_prompt`: the prompt given to the harness
148
+ - `test_command`: command to run tests
149
+ - `test_patch`: hidden test patch applied before evaluation
150
+ - `timeout_seconds`: per-task timeout
151
+
152
+ ### Features
153
+
154
+ - **Matrix building**: harness × model × task × repeat
155
+ - **Budget caps**: stops when $ budget exhausted
156
+ - **Cell-level resumability**: skips completed cells on re-run
157
+ - **Retry logic**: transient failures retried with exponential backoff
158
+ - **Exit classes**: PASS, FAIL, RETRYABLE_KILL, NON_RETRYABLE_KILL
159
+ - **Partial credit**: fraction of tests passing
160
+ - **Error classification**: success, partial, overfit, timeout, refusal, wrong_approach, crash, no_change
161
+ - **Reports**: HTML, JSON, CSV with within-model leaderboards
162
+
163
+ ## M3: Harness Adapters (completed)
164
+
165
+ Adapters wrap each coding harness with a uniform interface for the runner.
166
+
167
+ ### Supported harnesses
168
+
169
+ | Harness | Adapter | Observability | Notes |
170
+ |---------|---------|--------------|-------|
171
+ | OpenCode | `opencode` | full | Open-source, system prompt visible |
172
+ | Claude Code | `claude-code` | partial | Closed, proxy captures traffic |
173
+ | Codex | `codex` | partial | Closed, proxy captures traffic |
174
+ | Pi | `pi` | minimal | May bypass proxy |
175
+ | OMP | `omp` | minimal | May bypass proxy |
176
+
177
+ ### Listing adapters
178
+
179
+ ```bash
180
+ harness-evaluator adapters
181
+ ```
182
+
183
+ ### Observability tiers
184
+
185
+ - **full**: Open/cooperating harness. System prompts, tool definitions, context
186
+ strategy, and turn-level metadata are available.
187
+ - **partial**: Closed harness but provider traffic is captured through the
188
+ gateway proxy. Token usage and cost are accurately attributed.
189
+ - **minimal**: Only total spend or billing data is available. Traffic may
190
+ bypass the proxy. Cost accounting relies on billing reconciliation.
191
+
192
+ ### Adapter design
193
+
194
+ Each adapter implements:
195
+ - `prepare()`: Check/install the harness
196
+ - `run(task_prompt, timeout)`: Execute the harness non-interactively
197
+ - `cleanup()`: Clean up after the run
198
+ - `get_env()`: Set gateway proxy env vars and API keys
199
+
200
+ The adapter registry (`harness_evaluator.adapters.registry`) loads adapters by name and
201
+ the Docker runner uses it to dispatch to the correct adapter based on the
202
+ run config's `harness.adapter` field.
203
+
204
+ ## M4: Open-Ended Track (completed)
205
+
206
+ The open-ended track evaluates tasks without a single correct answer using a
207
+ frozen LLM judge, structured rubric, and structural checks.
208
+
209
+ ### Components
210
+
211
+ - **Frozen Judge** (`FrozenJudge`): Versioned LLM judge with an immutable prompt.
212
+ Version `v1.0` is the initial frozen prompt. Changing the prompt requires
213
+ bumping the version, which invalidates prior calibration data.
214
+ - **Rubric** (`Rubric`): Weighted criteria with 0-5 scoring scale. Default
215
+ rubric includes correctness (3x), completeness (2x), code_quality (1.5x),
216
+ test_quality (1.5x), documentation (1x).
217
+ - **Structural Checks** (`StructuralChecker`): Verifies file existence, Python
218
+ syntax, and test command execution. Structural failures cap the composite
219
+ success at 0.5.
220
+ - **Calibration** (`CalibrationSet`): Anchor submissions with known expected
221
+ scores for drift detection. Mean absolute error > 0.15 flags drift.
222
+
223
+ ### Running calibration
224
+
225
+ ```bash
226
+ export ANTHROPIC_API_KEY=sk-ant-...
227
+ harness-evaluator calibrate --model claude-sonnet-4-20250514
228
+ ```
229
+
230
+ ### Evaluation flow
231
+
232
+ 1. Get git diff of changes
233
+ 2. Run structural checks (file existence, syntax, tests)
234
+ 3. Run LLM judge against rubric
235
+ 4. Composite score: judge score, capped at 0.5 if structural checks fail
236
+ 5. Pass threshold: 0.7
237
+
238
+ ## M5: Dashboard (completed)
239
+
240
+ Interactive FastAPI web dashboard for exploring eval results.
241
+
242
+ ### Starting the dashboard
243
+
244
+ ```bash
245
+ harness-evaluator dashboard --port 8080
246
+ ```
247
+
248
+ Then open http://127.0.0.1:8080 in your browser.
249
+
250
+ ### Features
251
+
252
+ - **Run overview**: List all runs with summary stats (cells, passed, failed, cost)
253
+ - **Run detail**: Per-run view with leaderboards and filtered results table
254
+ - **Filtering**: Filter by model, harness, task track, and minimum success rate
255
+ - **Leaderboards**: Within-model harness comparison sorted by success rate
256
+ - **REST API**: JSON endpoints for programmatic access:
257
+ - `GET /api/runs` — list all runs
258
+ - `GET /api/run/{name}` — get filtered results
259
+ - `GET /api/run/{name}/leaderboard` — get leaderboard data
260
+
261
+ ## M6: Statistics (completed)
262
+
263
+ Statistical analysis of evaluation results, including mixed-effects modeling,
264
+ variance decomposition, bootstrap confidence intervals, and consistency analysis.
265
+
266
+ ### Running stats
267
+
268
+ ```bash
269
+ harness-evaluator stats my-run --db harness_evaluator_results.db
270
+ ```
271
+
272
+ ### Components
273
+
274
+ - **Mixed-Effects Model**: `success ~ C(harness) + C(model) + (1|task)`
275
+ Treats harness and model as fixed effects, task as a random effect.
276
+ Reports coefficients, standard errors, p-values, and confidence intervals.
277
+ - **Variance Decomposition**: Partitions variance into harness, model, task,
278
+ and residual components. Reports percentage of total variance explained.
279
+ - **Bootstrap CIs**: Non-parametric bootstrap confidence intervals (default
280
+ 1000 resamples, 95% CI) for success rate by harness.
281
+ - **Consistency Analysis**: Per harness × model combination, reports mean,
282
+ std, coefficient of variation, min/max success, and bootstrap CI.
283
+ - **Warnings**: Automatically warns when sample size is too small (<30)
284
+ or when the mixed-effects model fails to converge.
285
+
286
+ ## Architecture
287
+
288
+ - **Gateway**: custom HTTP/SSE proxy that intercepts provider calls and captures
289
+ token usage, cost, and latency with full request/response logging.
290
+ - **Orchestrator**: builds the eval matrix (harness × model × task × repeats),
291
+ manages budget caps, and handles cell-level resumability.
292
+ - **Runner**: Docker-based isolation, one container per eval cell.
293
+ - **Adapters**: per-harness integration (Python core + TS shims where needed).
294
+ - **Evaluator**: SWE-bench-style (hidden tests) and open-ended (LLM judge) tracks.
295
+ - **Reporting**: CLI reports + static HTML + interactive web dashboard.