coder-eval 0.8.2__py3-none-any.whl

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 (127) hide show
  1. coder_eval/.gitattributes +2 -0
  2. coder_eval/__init__.py +3 -0
  3. coder_eval/agent.py +302 -0
  4. coder_eval/agents/__init__.py +41 -0
  5. coder_eval/agents/_logging.py +89 -0
  6. coder_eval/agents/antigravity_agent.py +811 -0
  7. coder_eval/agents/claude_code_agent.py +1701 -0
  8. coder_eval/agents/codex_agent.py +2055 -0
  9. coder_eval/agents/noop_agent.py +100 -0
  10. coder_eval/agents/registry.py +163 -0
  11. coder_eval/agents/watchdog.py +116 -0
  12. coder_eval/analysis.py +88 -0
  13. coder_eval/cli/__init__.py +87 -0
  14. coder_eval/cli/aggregate_command.py +153 -0
  15. coder_eval/cli/console.py +7 -0
  16. coder_eval/cli/evaluate_command.py +164 -0
  17. coder_eval/cli/plan_command.py +152 -0
  18. coder_eval/cli/report_command.py +121 -0
  19. coder_eval/cli/run_command.py +686 -0
  20. coder_eval/cli/run_helpers.py +137 -0
  21. coder_eval/cli/run_task_internal_command.py +210 -0
  22. coder_eval/cli/utils.py +37 -0
  23. coder_eval/config.py +212 -0
  24. coder_eval/criteria/__init__.py +163 -0
  25. coder_eval/criteria/_classification_aggregate.py +106 -0
  26. coder_eval/criteria/agent_judge.py +425 -0
  27. coder_eval/criteria/base.py +248 -0
  28. coder_eval/criteria/classification_match.py +107 -0
  29. coder_eval/criteria/command_executed.py +181 -0
  30. coder_eval/criteria/commands_efficiency.py +73 -0
  31. coder_eval/criteria/file_check.py +119 -0
  32. coder_eval/criteria/file_contains.py +85 -0
  33. coder_eval/criteria/file_exists.py +45 -0
  34. coder_eval/criteria/file_matches_regex.py +86 -0
  35. coder_eval/criteria/json_check.py +184 -0
  36. coder_eval/criteria/llm_judge.py +260 -0
  37. coder_eval/criteria/reference_comparison.py +130 -0
  38. coder_eval/criteria/run_command.py +220 -0
  39. coder_eval/criteria/skill_triggered.py +111 -0
  40. coder_eval/criteria/uipath_eval.py +223 -0
  41. coder_eval/errors/__init__.py +26 -0
  42. coder_eval/errors/agent.py +26 -0
  43. coder_eval/errors/budget.py +28 -0
  44. coder_eval/errors/categories.py +205 -0
  45. coder_eval/errors/categorization.py +240 -0
  46. coder_eval/errors/executor.py +124 -0
  47. coder_eval/errors/judge.py +12 -0
  48. coder_eval/errors/retry.py +211 -0
  49. coder_eval/errors/timeout.py +63 -0
  50. coder_eval/evaluation/__init__.py +10 -0
  51. coder_eval/evaluation/checker.py +260 -0
  52. coder_eval/evaluation/judge_anthropic.py +55 -0
  53. coder_eval/evaluation/judge_bedrock.py +114 -0
  54. coder_eval/evaluation/judge_context.py +497 -0
  55. coder_eval/evaluation/judge_models.py +53 -0
  56. coder_eval/evaluation/judge_persistence.py +291 -0
  57. coder_eval/evaluation/judge_usage.py +50 -0
  58. coder_eval/evaluation/sub_agent.py +231 -0
  59. coder_eval/evaluation/summaries.py +48 -0
  60. coder_eval/evaluation/verdict_tool.py +213 -0
  61. coder_eval/formatting.py +191 -0
  62. coder_eval/isolation/__init__.py +15 -0
  63. coder_eval/isolation/docker_runner.py +1253 -0
  64. coder_eval/logging_config.py +399 -0
  65. coder_eval/models/__init__.py +337 -0
  66. coder_eval/models/agent_config.py +373 -0
  67. coder_eval/models/container_paths.py +26 -0
  68. coder_eval/models/criteria.py +942 -0
  69. coder_eval/models/enums.py +121 -0
  70. coder_eval/models/experiment.py +314 -0
  71. coder_eval/models/judge.py +90 -0
  72. coder_eval/models/judge_defaults.py +11 -0
  73. coder_eval/models/limits.py +89 -0
  74. coder_eval/models/merge_strategy.py +132 -0
  75. coder_eval/models/mutations.py +95 -0
  76. coder_eval/models/results.py +787 -0
  77. coder_eval/models/routing.py +160 -0
  78. coder_eval/models/sandbox.py +371 -0
  79. coder_eval/models/tasks.py +631 -0
  80. coder_eval/models/telemetry.py +454 -0
  81. coder_eval/models/templates.py +108 -0
  82. coder_eval/orchestration/__init__.py +13 -0
  83. coder_eval/orchestration/batch.py +690 -0
  84. coder_eval/orchestration/config.py +111 -0
  85. coder_eval/orchestration/config_merge.py +431 -0
  86. coder_eval/orchestration/evaluation.py +94 -0
  87. coder_eval/orchestration/experiment.py +837 -0
  88. coder_eval/orchestration/overrides.py +206 -0
  89. coder_eval/orchestration/task_loader.py +437 -0
  90. coder_eval/orchestrator.py +2078 -0
  91. coder_eval/path_utils.py +79 -0
  92. coder_eval/plugins.py +81 -0
  93. coder_eval/pricing.py +169 -0
  94. coder_eval/py.typed +0 -0
  95. coder_eval/reports.py +1066 -0
  96. coder_eval/reports_experiment.py +761 -0
  97. coder_eval/reports_html.py +1659 -0
  98. coder_eval/reports_stats.py +250 -0
  99. coder_eval/resources/__init__.py +137 -0
  100. coder_eval/resources/default_experiment.yaml +85 -0
  101. coder_eval/resources/default_ignore_patterns.yaml +60 -0
  102. coder_eval/resources/tags.yaml +55 -0
  103. coder_eval/sandbox.py +1127 -0
  104. coder_eval/scoring/__init__.py +11 -0
  105. coder_eval/scoring/ast_similarity.py +38 -0
  106. coder_eval/scoring/complexity.py +115 -0
  107. coder_eval/scoring/quality.py +154 -0
  108. coder_eval/scoring/signature_similarity.py +57 -0
  109. coder_eval/scoring/similarity.py +103 -0
  110. coder_eval/scoring/token_similarity.py +44 -0
  111. coder_eval/simulation/__init__.py +27 -0
  112. coder_eval/simulation/termination.py +88 -0
  113. coder_eval/simulation/user_simulator.py +324 -0
  114. coder_eval/streaming/__init__.py +44 -0
  115. coder_eval/streaming/callbacks.py +57 -0
  116. coder_eval/streaming/collector.py +193 -0
  117. coder_eval/streaming/events.py +198 -0
  118. coder_eval/streaming/renderers.py +248 -0
  119. coder_eval/streaming/wire.py +115 -0
  120. coder_eval/telemetry.py +407 -0
  121. coder_eval/utils.py +517 -0
  122. coder_eval-0.8.2.dist-info/METADATA +242 -0
  123. coder_eval-0.8.2.dist-info/RECORD +127 -0
  124. coder_eval-0.8.2.dist-info/WHEEL +4 -0
  125. coder_eval-0.8.2.dist-info/entry_points.txt +5 -0
  126. coder_eval-0.8.2.dist-info/licenses/LICENSE +201 -0
  127. coder_eval-0.8.2.dist-info/licenses/NOTICE +18 -0
@@ -0,0 +1,250 @@
1
+ """Shared statistical + prompt-config helpers for the markdown and HTML reporters.
2
+
3
+ Kept in a standalone module so ``reports_html`` can consume them without
4
+ importing ``reports_experiment`` (which in turn imports ``reports_html``
5
+ for its HTML-write helpers, and would otherwise form a cycle).
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import logging
11
+ import math
12
+ import random
13
+ import statistics as _stats
14
+ from pathlib import Path
15
+
16
+ from coder_eval.models import EvaluationResult, ExperimentVariant, TaskExperimentSummary
17
+
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ # ---------------------------------------------------------------------------
23
+ # Statistical helpers (stdlib statistics module)
24
+ # ---------------------------------------------------------------------------
25
+
26
+
27
+ def mean(values: list[float]) -> float:
28
+ return _stats.mean(values) if values else 0.0
29
+
30
+
31
+ def stddev(values: list[float]) -> float:
32
+ """Sample standard deviation (Bessel-corrected). Returns 0.0 for n < 2."""
33
+ return _stats.stdev(values) if len(values) >= 2 else 0.0
34
+
35
+
36
+ def welch_t_test(a: list[float], b: list[float]) -> float | None:
37
+ """Two-tailed p-value using Welch's t-test via stdlib NormalDist approximation.
38
+
39
+ Uses the normal approximation for the t-distribution when df is large (>30),
40
+ and falls back to exact Welch-Satterthwaite otherwise. Returns None if either
41
+ group has fewer than 2 observations.
42
+ """
43
+ n_a, n_b = len(a), len(b)
44
+ if n_a < 2 or n_b < 2:
45
+ return None
46
+
47
+ mean_a, mean_b = _stats.mean(a), _stats.mean(b)
48
+ var_a = _stats.variance(a)
49
+ var_b = _stats.variance(b)
50
+
51
+ se_sq = var_a / n_a + var_b / n_b
52
+ if se_sq == 0:
53
+ return 1.0
54
+
55
+ t_stat = abs(mean_a - mean_b) / math.sqrt(se_sq)
56
+
57
+ # Conservative normal approximation: treat t as z-score.
58
+ # Overestimates p slightly for small df (heavier tails), but correct in
59
+ # direction and sufficient for display purposes (no incomplete-beta needed).
60
+ return 2.0 * _stats.NormalDist().cdf(-t_stat)
61
+
62
+
63
+ def fmt_mean_sd(values: list[float], fmt: str = ".3f") -> str:
64
+ """Format mean ± stddev string. Omits ± when n < 2 (stddev undefined)."""
65
+ if not values:
66
+ return "N/A"
67
+ m = mean(values)
68
+ if len(values) < 2:
69
+ return f"{m:{fmt}}"
70
+ sd = stddev(values)
71
+ return f"{m:{fmt}} ± {sd:{fmt}}"
72
+
73
+
74
+ def fmt_p(p: float | None) -> str:
75
+ """Format p-value for display."""
76
+ if p is None:
77
+ return "—"
78
+ if p < 0.001:
79
+ return "<0.001"
80
+ return f"{p:.3f}"
81
+
82
+
83
+ # ---------------------------------------------------------------------------
84
+ # Replicate statistics helpers (stdlib random + statistics)
85
+ # ---------------------------------------------------------------------------
86
+
87
+
88
+ def bootstrap_mean_ci(
89
+ values: list[float],
90
+ n_resamples: int = 1000,
91
+ confidence: float = 0.95,
92
+ seed: int = 0,
93
+ ) -> tuple[float, float, float]:
94
+ """Percentile-bootstrap confidence interval for the mean.
95
+
96
+ Returns (mean, ci_low, ci_high). When ``len(values) < 2``, returns
97
+ (values[0], values[0], values[0]) or (0, 0, 0) for empty input.
98
+ Uses ``random.Random(seed)`` for determinism.
99
+ """
100
+ if not values:
101
+ return (0.0, 0.0, 0.0)
102
+ m = sum(values) / len(values)
103
+ if len(values) < 2:
104
+ return (m, m, m)
105
+ rng = random.Random(seed)
106
+ n = len(values)
107
+ resampled_means = sorted(sum(rng.choice(values) for _ in range(n)) / n for _ in range(n_resamples))
108
+ alpha = (1.0 - confidence) / 2.0
109
+ lo = resampled_means[int(alpha * n_resamples)]
110
+ hi = resampled_means[int((1.0 - alpha) * n_resamples) - 1]
111
+ return (m, lo, hi)
112
+
113
+
114
+ def wilson_interval(successes: int, n: int, confidence: float = 0.95) -> tuple[float, float]:
115
+ """Wilson score interval for a binomial proportion. Returns (low, high).
116
+
117
+ More reliable than the normal approximation at small N and near 0/1.
118
+ """
119
+ if n <= 0:
120
+ return (0.0, 0.0)
121
+ z = _stats.NormalDist().inv_cdf((1.0 + confidence) / 2.0)
122
+ p_hat = successes / n
123
+ denom = 1.0 + z * z / n
124
+ center = (p_hat + z * z / (2.0 * n)) / denom
125
+ half = (z * math.sqrt(p_hat * (1.0 - p_hat) / n + z * z / (4.0 * n * n))) / denom
126
+ return (max(0.0, center - half), min(1.0, center + half))
127
+
128
+
129
+ def paired_bootstrap_diff_ci(
130
+ a: list[float],
131
+ b: list[float],
132
+ n_resamples: int = 1000,
133
+ confidence: float = 0.95,
134
+ seed: int = 0,
135
+ ) -> tuple[float, float, float] | None:
136
+ """Paired bootstrap of mean(a_i - b_i).
137
+
138
+ Returns (mean_diff, ci_low, ci_high) or None if lengths differ or < 2.
139
+ """
140
+ if len(a) != len(b) or len(a) < 2:
141
+ return None
142
+ diffs = [ai - bi for ai, bi in zip(a, b, strict=True)]
143
+ return bootstrap_mean_ci(diffs, n_resamples=n_resamples, confidence=confidence, seed=seed)
144
+
145
+
146
+ def cohens_d(a: list[float], b: list[float]) -> float | None:
147
+ """Paired Cohen's d = mean(a_i - b_i) / stddev(a_i - b_i)."""
148
+ if len(a) != len(b) or len(a) < 2:
149
+ return None
150
+ diffs = [ai - bi for ai, bi in zip(a, b, strict=True)]
151
+ s = stddev(diffs)
152
+ return (sum(diffs) / len(diffs)) / s if s > 0 else None
153
+
154
+
155
+ # ---------------------------------------------------------------------------
156
+ # Prompt config + variant-result loaders
157
+ # ---------------------------------------------------------------------------
158
+
159
+
160
+ def has_final_reply(result: EvaluationResult) -> bool:
161
+ """True iff any iteration emitted a non-empty ResultMessage.result.
162
+
163
+ Mirrors the evalboard rendering: a "final reply" is a text answer the
164
+ agent produced that becomes the trailing entry in the Turn timeline.
165
+ """
166
+ for t in result.iterations:
167
+ if t.result_summary is not None:
168
+ r = t.result_summary.result
169
+ if isinstance(r, str) and r.strip():
170
+ return True
171
+ return False
172
+
173
+
174
+ def visible_turn_count(result: EvaluationResult) -> int:
175
+ """Count of agent actions visible in the timeline so far.
176
+
177
+ A "turn" here is one entry rendered in the Turn timeline: each tool
178
+ invocation contributes 1, plus 1 for the final assistant reply when
179
+ present. This is the canonical metric — distinct from the SDK's
180
+ ``num_turns`` which counts assistant *messages* and can bundle tool
181
+ use with trailing text into a single turn.
182
+ """
183
+ commands = sum(len(t.commands) for t in result.iterations)
184
+ return commands + (1 if has_final_reply(result) else 0)
185
+
186
+
187
+ def expected_turns_overage(result: EvaluationResult) -> tuple[int, int] | None:
188
+ """Return ``(visible_turns, expected)`` when the visible-events turn
189
+ count strictly exceeds ``run_limits.expected_turns``; else ``None``.
190
+
191
+ Safe against missing ``task_config``, missing ``run_limits``, and
192
+ non-int ``expected_turns`` values.
193
+ """
194
+ task_cfg = result.task_config
195
+ if task_cfg is None:
196
+ return None
197
+ run_limits = (task_cfg.resolved or {}).get("run_limits") or {}
198
+ if not isinstance(run_limits, dict):
199
+ return None
200
+ expected = run_limits.get("expected_turns")
201
+ if not isinstance(expected, int) or expected < 1:
202
+ return None
203
+ actual = visible_turn_count(result)
204
+ if actual > expected:
205
+ return actual, expected
206
+ return None
207
+
208
+
209
+ def describe_prompt_config(variant: ExperimentVariant) -> str:
210
+ """Return a short description of the variant's prompt configuration.
211
+
212
+ Returns strings like ``"(base prompt)"``, ``"(prompt override)"``, or
213
+ ``"(2 mutations: prefix, suffix)"``.
214
+ """
215
+ if variant.initial_prompt is not None or variant.initial_prompt_file is not None:
216
+ return "(prompt override)"
217
+ if variant.prompt_mutations:
218
+ type_names = [m.type for m in variant.prompt_mutations]
219
+ return f"({len(type_names)} mutations: {', '.join(type_names)})"
220
+ return "(base prompt)"
221
+
222
+
223
+ def load_variant_eval_results(
224
+ run_dir: Path, variant_id: str, task_summaries: list[TaskExperimentSummary]
225
+ ) -> list[EvaluationResult]:
226
+ """Load EvaluationResult objects for a variant from disk.
227
+
228
+ Walks all ``<run_dir>/<variant_id>/<task_id>/NN/task.json`` replicate
229
+ subdirs for each task in ``task_summaries`` and returns every result that
230
+ loads successfully.
231
+ """
232
+ variant_dir = run_dir / variant_id
233
+ results: list[EvaluationResult] = []
234
+
235
+ if not variant_dir.is_dir():
236
+ return results
237
+
238
+ for ts in task_summaries:
239
+ task_dir = variant_dir / ts.task_id
240
+ if not task_dir.is_dir():
241
+ continue
242
+ for rep_subdir in sorted(task_dir.glob("[0-9][0-9]")):
243
+ task_json = rep_subdir / "task.json"
244
+ if task_json.exists():
245
+ try:
246
+ results.append(EvaluationResult.model_validate_json(task_json.read_text(encoding="utf-8")))
247
+ except Exception:
248
+ logger.warning("Failed to load %s for variant report", task_json, exc_info=True)
249
+
250
+ return results
@@ -0,0 +1,137 @@
1
+ """Resource management for default configurations."""
2
+
3
+ from functools import lru_cache
4
+ from pathlib import Path
5
+
6
+ import yaml
7
+
8
+
9
+ @lru_cache(maxsize=1)
10
+ def load_default_ignore_patterns() -> set[str]:
11
+ """Load default ignore patterns from YAML resource file.
12
+
13
+ Returns:
14
+ Set of pattern strings to ignore during file operations.
15
+
16
+ Note:
17
+ Result is cached for performance. Changes to the YAML file
18
+ require process restart to take effect.
19
+ """
20
+ resource_dir = Path(__file__).parent
21
+ patterns_file = resource_dir / "default_ignore_patterns.yaml"
22
+
23
+ if not patterns_file.exists():
24
+ # Fallback to hardcoded patterns if file missing
25
+ return {
26
+ ".venv",
27
+ "venv",
28
+ "ENV",
29
+ "env",
30
+ ".git",
31
+ ".svn",
32
+ "__pycache__",
33
+ ".pytest_cache",
34
+ "*.pyc",
35
+ "*.egg-info",
36
+ "dist",
37
+ "build",
38
+ "node_modules",
39
+ ".DS_Store",
40
+ "Thumbs.db",
41
+ }
42
+
43
+ with open(patterns_file, encoding="utf-8") as f:
44
+ data = yaml.safe_load(f)
45
+
46
+ # Flatten all categories into a single set
47
+ patterns = set()
48
+ for category in data.values():
49
+ if isinstance(category, list):
50
+ patterns.update(category)
51
+
52
+ return patterns
53
+
54
+
55
+ def normalize_ignore_pattern_entry(raw: str) -> str:
56
+ """Strip whitespace from an ignore-pattern entry and validate its shape.
57
+
58
+ Returns the cleaned entry. Raises ``ValueError`` on empty / whitespace-only
59
+ entries and on bare ``"!"`` (negation with no target). Shared by the
60
+ runtime resolver (:func:`get_ignore_patterns`) and Pydantic
61
+ ``field_validator`` hooks on ``SandboxConfig.ignore_patterns`` and
62
+ ``AgentConfig.ignore_patterns`` so malformed YAML fails at load time
63
+ rather than mid-run.
64
+ """
65
+ entry = raw.strip()
66
+ if not entry:
67
+ raise ValueError(f"ignore pattern entry is empty or whitespace-only: {raw!r}")
68
+ if entry == "!":
69
+ raise ValueError("ignore pattern entry '!' is bare negation with no target")
70
+ return entry
71
+
72
+
73
+ def get_ignore_patterns(additional_patterns: list[str] | None = None) -> set[str]:
74
+ """Get ignore patterns with optional additions and negations.
75
+
76
+ Args:
77
+ additional_patterns: Optional list of pattern overrides. Plain entries
78
+ are added on top of the defaults; entries prefixed with ``!`` are
79
+ removed from the defaults (gitignore-style negation). Example:
80
+ ``["!dist", "!node_modules", "*.bak"]`` un-ignores ``dist`` and
81
+ ``node_modules`` (so vendored JS build outputs survive sandbox
82
+ copy) and additionally ignores ``*.bak`` files.
83
+
84
+ Returns:
85
+ Combined set of default and additional patterns, with negations
86
+ applied to the defaults.
87
+
88
+ Raises:
89
+ ValueError: If any entry fails :func:`normalize_ignore_pattern_entry`.
90
+ """
91
+ patterns = load_default_ignore_patterns().copy()
92
+
93
+ if additional_patterns:
94
+ for raw in additional_patterns:
95
+ entry = normalize_ignore_pattern_entry(raw)
96
+ if entry.startswith("!"):
97
+ patterns.discard(entry[1:])
98
+ else:
99
+ patterns.add(entry)
100
+
101
+ return patterns
102
+
103
+
104
+ def matches_pattern(path_part: str, pattern: str) -> bool:
105
+ """Check if a path part matches an ignore pattern.
106
+
107
+ Supports:
108
+ - Exact match: "node_modules"
109
+ - Prefix wildcard: "*.pyc" (matches "file.pyc")
110
+ - Suffix wildcard: "test_*" (matches "test_utils.py")
111
+
112
+ Args:
113
+ path_part: Single path component (e.g., "file.pyc")
114
+ pattern: Pattern to match against
115
+
116
+ Returns:
117
+ True if path_part matches pattern
118
+ """
119
+ if pattern.startswith("*"):
120
+ return path_part.endswith(pattern[1:])
121
+ elif pattern.endswith("*"):
122
+ return path_part.startswith(pattern[:-1])
123
+ else:
124
+ return path_part == pattern
125
+
126
+
127
+ def should_ignore_path(path: Path, patterns: set[str]) -> bool:
128
+ """Check if any part of a path matches ignore patterns.
129
+
130
+ Args:
131
+ path: Path to check
132
+ patterns: Set of patterns to match against
133
+
134
+ Returns:
135
+ True if any part of the path matches any pattern
136
+ """
137
+ return any(any(matches_pattern(str(part), pattern) for pattern in patterns) for part in path.parts)
@@ -0,0 +1,85 @@
1
+ # Default experiment — provides baseline agent configuration.
2
+ #
3
+ # Tasks that omit the `agent` field inherit these defaults via the
4
+ # 4-layer merge chain: default.yaml → experiment defaults → task YAML → variant.
5
+ # All agent properties are listed explicitly so users know the baseline.
6
+
7
+ experiment_id: default
8
+ description: "Default experiment - provides baseline agent configuration"
9
+
10
+ defaults:
11
+ # Number of replicates for each task (repeats the same task N times per variant).
12
+ # Useful for quantifying variance in non-deterministic agent behavior.
13
+ # Overridable per-variant or via --repeats on the CLI.
14
+ # repeats: 1
15
+
16
+ # Run-time caps (turns, wall-clock, tokens, USD). Override per-variant or per-task via
17
+ # field-merge — a task or variant can set a single key without replacing the whole block.
18
+ # Per-row dataset multiplication: a 100-row task with max_usd: 0.10 permits up to
19
+ # $10 cumulative spend.
20
+ run_limits:
21
+ # Maximum agent inner-loop turns per iteration (null = SDK default).
22
+ # Typical range in tasks: 3-18 depending on complexity.
23
+ max_turns: 20
24
+ # Soft target: when cumulative SDK turns across all iterations of a task
25
+ # exceed this, the orchestrator logs a one-shot warning and the report
26
+ # surfaces a badge. Does NOT abort the run (max_turns is the hard cap).
27
+ # expected_turns: 15
28
+ # Maximum total seconds for the entire task evaluation (all iterations combined).
29
+ task_timeout: 600
30
+ # Maximum seconds per agent turn / communicate call (null = no limit).
31
+ turn_timeout: 300
32
+ # Optional token / cost caps (leave commented to disable).
33
+ # max_input_tokens: 100000
34
+ # max_output_tokens: 20000
35
+ # max_total_tokens: 120000
36
+ # max_usd: 0.50
37
+ # count_cached_input: false
38
+
39
+ agent:
40
+ # Agent type (currently only "claude-code" is supported)
41
+ type: claude-code
42
+
43
+ # Permission mode for agent actions:
44
+ # default - ask for confirmation on each action
45
+ # acceptEdits - auto-accept file edits, confirm other actions
46
+ # plan - read-only, no file modifications allowed
47
+ # bypassPermissions - auto-accept all actions (use with caution)
48
+ permission_mode: acceptEdits
49
+
50
+ # Specific model to use (null = defer to Claude Code CLI default)
51
+ # Examples: claude-sonnet-4-6, claude-opus-4-6
52
+ model: claude-sonnet-4-6
53
+
54
+ # Allowed tools — restrict which tools the agent can use (null = all tools)
55
+ # Most tasks use ["Bash"] or ["Bash", "Read", "Write"]. This default
56
+ # provides the common set needed for general-purpose coding tasks.
57
+ allowed_tools: ["Bash", "Read", "Write", "Edit", "Glob", "Grep", "Skill"]
58
+
59
+ # Claude Code plugins to load (null = none)
60
+ # Example: [{"type": "local", "path": "/path/to/skills"}]
61
+ plugins: null
62
+
63
+ # Additional patterns to ignore during file change detection (beyond defaults)
64
+ # Example: ["*.log", "__pycache__"]
65
+ ignore_patterns: []
66
+
67
+ # Claude Code SDK pass-through options — anything coder_eval doesn't own
68
+ # directly (e.g. `effort`, `max_thinking_tokens`). Keys must be
69
+ # ClaudeAgentOptions fields and must not be framework-managed (model,
70
+ # allowed_tools, permission_mode, hooks, mcp_servers, ...). Deep-merged
71
+ # across experiment / task / variant / --sdk-option layers, so a higher
72
+ # layer adding one key doesn't wipe the rest.
73
+ # Example: sdk_options: {effort: high}
74
+ # sdk_options: {}
75
+
76
+ # Sandbox-internal cleanup appended after each task's own post_run (MST-9674).
77
+ # PR #250 sandbox-scopes Node/npm installs to <sandbox>/{node_modules,.npm-prefix};
78
+ # removing them keeps preserved-sandbox artifacts small and is a no-op in normal
79
+ # (non-preserved) runs where the tempdir is rmtree'd anyway.
80
+ post_run:
81
+ - command: "rm -rf node_modules .npm-prefix"
82
+ timeout: 30
83
+
84
+ variants:
85
+ - variant_id: default
@@ -0,0 +1,60 @@
1
+ # Default file/directory patterns to ignore during file operations
2
+ # These patterns apply to:
3
+ # - Template directory copying (sandbox setup)
4
+ # - Workspace copying into an agent_judge sub-agent sandbox
5
+
6
+ # Virtual environments
7
+ virtual_environments:
8
+ - ".venv"
9
+ - "venv"
10
+ - "ENV"
11
+ - "env"
12
+ - "virtualenv"
13
+
14
+ # Version control
15
+ version_control:
16
+ - ".git"
17
+ - ".svn"
18
+ - ".hg"
19
+
20
+ # Python artifacts
21
+ python_artifacts:
22
+ - "__pycache__"
23
+ - "*.pyc"
24
+ - "*.pyo"
25
+ - "*.pyd"
26
+ - ".pytest_cache"
27
+ - ".ruff_cache"
28
+ - "*.egg-info"
29
+ - ".eggs"
30
+ - "dist"
31
+ - "build"
32
+
33
+ # IDE and editor files
34
+ ide_files:
35
+ - ".vscode"
36
+ - ".idea"
37
+ - "*.swp"
38
+ - "*.swo"
39
+ - "*~"
40
+ - ".DS_Store"
41
+ - "Thumbs.db"
42
+
43
+ # Package managers
44
+ package_managers:
45
+ - "node_modules"
46
+ - ".npm"
47
+ - "bower_components"
48
+
49
+ # Test and coverage
50
+ test_coverage:
51
+ - ".coverage"
52
+ - "htmlcov"
53
+ - ".tox"
54
+ - ".nox"
55
+
56
+ # Compiled files
57
+ compiled:
58
+ - "*.so"
59
+ - "*.dylib"
60
+ - "*.dll"
@@ -0,0 +1,55 @@
1
+ # Suggested vocabulary of tags for post-run task reviews.
2
+ #
3
+ # This file is a *suggested* vocabulary, not a strict allowlist. The
4
+ # /coder-eval-review skill prefers names from this list but emits whatever
5
+ # fits. A periodic vocabulary lint reports tags appearing in review.json
6
+ # files that aren't here, so drift is visible
7
+ # without blocking new categories from emerging.
8
+ #
9
+ # Tags can describe failure modes, agent behaviors, or positive traits
10
+ # (e.g. optimal-path, called-tools). To add a vocabulary entry, open a PR.
11
+ #
12
+ # Each entry:
13
+ # name - kebab-case canonical slug (required)
14
+ # definition - one short sentence describing what the tag means (required)
15
+ # examples - optional list of short, illustrative snippets
16
+ tags:
17
+ - name: validation-error
18
+ definition: Schema or CLI validation failed for a generated artifact (e.g. .flow, .xaml, JSON).
19
+ examples:
20
+ - "Schema validation failed: expected string, received undefined"
21
+ - "Invalid .flow: missing required field 'startNode'"
22
+ - name: sandbox-leak
23
+ definition: Agent read or wrote files outside its assigned task directory.
24
+ examples:
25
+ - "Read sibling runs/<other_run>/artifacts/..."
26
+ - name: agent-loop
27
+ definition: Agent retried the same failing action repeatedly without adapting.
28
+ examples:
29
+ - "Same Bash command failed 5 times in a row with identical error"
30
+ - name: tool-error
31
+ definition: Bash/Read/Edit/SDK tool failed for reasons unrelated to task logic.
32
+ examples:
33
+ - "Edit failed: file not found"
34
+ - "Bash: command not found"
35
+ - name: model-nondeterminism
36
+ definition: Same task flips pass/fail across replicates with no code change.
37
+ - name: judge-disagreement
38
+ definition: LLM-judge verdict inconsistent with artifacts produced.
39
+ - name: infra
40
+ definition: Sandbox, network, auth, or external-service failure unrelated to the agent.
41
+ examples:
42
+ - "Auth token expired mid-run"
43
+ - "Network timeout reaching external service"
44
+ - name: criteria-bug
45
+ definition: Agent produced correct output but criteria did not match (false negative).
46
+ examples:
47
+ - "command_executed expected 'uip run' but agent used 'uipath run'"
48
+ - name: turn-timeout-too-short
49
+ definition: Task hit its per-turn timeout; the limit is too tight for the work the task requires.
50
+ examples:
51
+ - "300s turn_timeout exceeded at 80 turns; analysis recommends 600s"
52
+ - name: max-turns-too-low
53
+ definition: Task ran out of turns before completing; max_turns budget is below what the task realistically needs.
54
+ examples:
55
+ - "MAX_TURNS_EXHAUSTED: max_turns=50 but 86 turns used"