Skip to content

Commit 289d04f

Browse files
ASRagabclaude
andcommitted
feat(publish-ready): argparse hardening, docs overhaul, QOL improvements
Batch 1 — Critical Path: - Move --evaluator-command to last flag position (nargs="+" greedy fix) - Add runtime validation detecting flag-like tokens in evaluator command - Add zero-proposals detection (error on 0, warning on 1 candidate) - Update CLAUDE.md: new commands, delivery surfaces, Known Gotchas - Update AGENTS.md: optimization workflow, file organization sections Batch 2 — Documentation: - Create CONCEPTS.md glossary (12 core concept entries) - Create WALKTHROUGH.md tutorial (9-step optimization guide) - Update README.md: --model, --judge-model, --spec-file, --run-dir, --diff flags; score subcommand section; architecture entries - Add pyproject.toml packaging metadata (license, classifiers, URLs) Batch 3 — QOL + Testing: - Add Makefile with 9 common dev targets - Add pytest addopts, testpaths, filterwarnings to pyproject.toml - CI: exclude integration tests from default test job - 8 new tests: argparse ordering (3), zero-proposals (1), code fence variants (2), RED all-None (1), baseline 0.0 (1) 215 tests passing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d223f83 commit 289d04f

11 files changed

Lines changed: 566 additions & 11 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ jobs:
3131
- name: Install dependencies
3232
run: uv sync
3333

34-
- name: Run pytest
35-
run: uv run pytest -v
34+
- name: Run pytest (excluding integration)
35+
run: uv run pytest -v -m "not integration"
3636

3737
- name: Smoke harness (budget=1)
3838
run: uv run python scripts/smoke_harness.py --budget 1

AGENTS.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## Project Structure & Module Organization
44
Core code lives in `src/optimize_anything/`:
5-
- `cli.py` for `optimize-anything` subcommands (`optimize`, `generate-evaluator`, `intake`, `explain`, `budget`)
5+
- `cli.py` for `optimize-anything` subcommands (`optimize`, `generate-evaluator`, `intake`, `explain`, `budget`, `score`)
66
- `evaluators.py` for command/HTTP evaluator adapters
77
- `intake.py` for intake schema normalization (`evaluation_pattern`, `execution_mode`, `quality_dimensions`, constraints)
88
- `evaluator_generator.py` for evaluator scaffolding from seed/objective/intake
@@ -43,3 +43,24 @@ For PRs, include:
4343
- linked issue (if any),
4444
- test evidence (command + result),
4545
- sample CLI output when behavior changes.
46+
47+
## Optimization Workflow
48+
49+
When running RED-GREEN-OBSERVER cycles:
50+
1. Always pass `--model` explicitly to `live_integration.py`
51+
2. Place `--evaluator-command` as the LAST flag
52+
3. Run RED validation after every GREEN improvement
53+
4. Accept artifacts only when cross_provider_delta < 0.2
54+
5. Update baselines with `score_check.py --update` after accepting
55+
6. Commit with descriptive message including score deltas
56+
57+
## File Organization
58+
59+
- `src/optimize_anything/` — package source (all Python modules)
60+
- `scripts/` — operational scripts (gates, smoke, live integration)
61+
- `skills/` — optimizable skill artifacts (SKILL.md files)
62+
- `evaluators/` — production evaluator scripts
63+
- `examples/` — sample evaluators and seeds
64+
- `tests/` — pytest test suite
65+
- `docs/` — planning and handoff documents (gitignored from commits)
66+
- `integration_runs/` — optimization run artifacts (gitignored)

CLAUDE.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,21 @@ uv run pytest tests/test_cli.py # Run one test module
1111
uv run pytest -k "optimize" # Run tests by pattern
1212
uv run optimize-anything --help # CLI entry point
1313
uv run optimize-anything score FILE --evaluator-command bash eval.sh # Score one artifact
14+
uv run optimize-anything score FILE --judge-model openai/gpt-4o-mini --objective "Score clarity" # LLM judge scoring
1415
uv run python scripts/check.py # Unified gate: pytest + smoke + score_check
1516
uv run python scripts/check.py --skip-smoke # Unified gate without smoke (offline)
1617
uv run python scripts/smoke_harness.py --budget 1 # CLI smoke check
1718
uv run python scripts/score_check.py # Score regression check
1819
uv run python scripts/score_check.py --update # Update baselines after improvement
20+
uv run python scripts/live_integration.py --phase green \
21+
--artifact FILE --model openai/gpt-4o-mini --budget 15 \
22+
--objective "..." --run-dir integration_runs \
23+
--evaluator-command bash evaluators/skill_clarity.sh # GREEN phase optimization
24+
uv run python scripts/live_integration.py --phase red \
25+
--artifact FILE --objective "..." \
26+
--providers openai/gpt-5.1-mini anthropic/claude-sonnet-4-5-20250929 \
27+
--baseline 0.85 \
28+
--evaluator-command bash evaluators/skill_clarity.sh # RED phase validation
1929
```
2030

2131
## Architecture
@@ -66,6 +76,17 @@ Input/output contract for external evaluators:
6676
- `src/optimize_anything/result_contract.py`
6777
- Canonical optimize summary for CLI output
6878

79+
- `src/optimize_anything/spec_loader.py`
80+
- TOML spec file loading for repeatable optimization runs
81+
- `--spec-file` flag overrides CLI arguments with spec values
82+
83+
- `scripts/live_integration.py`
84+
- RED-GREEN-OBSERVER cycle orchestrator
85+
- `--phase green`: optimize artifact, output structured JSON
86+
- `--phase red`: multi-provider scoring, output score matrix
87+
- `--model`: proposer LLM (REQUIRED — gepa default may be unavailable)
88+
- `--evaluator-command` MUST be the last flag (nargs="+" greedy parsing)
89+
6990
## Important Distinction
7091

7192
- `execution_mode` / runtime type: transport and execution path (`command` vs `http`)
@@ -88,8 +109,20 @@ skills/optimization-guide/
88109
- CLI tests call `main(argv)`.
89110
- Evaluator tests include command, HTTP, timeout, and malformed payload cases.
90111
- LLM judge unit tests mock `litellm.completion`; integration tests skip via `@pytest.mark.skipif` when API keys absent.
112+
- Live integration tests in `tests/test_live_integration.py`.
113+
- `@pytest.mark.integration` for tests requiring API keys.
114+
- Code fence stripping tested in `test_llm_judge.py`.
91115
- Doc drift checks live in `tests/test_doc_contract.py`.
92116

117+
## Known Gotchas
118+
119+
- `--evaluator-command` in `live_integration.py` uses `nargs="+"` — must be the LAST
120+
flag or it swallows subsequent arguments like `--model`
121+
- gepa's default proposer model may be unavailable — always pass `--model` explicitly
122+
- Anthropic models wrap JSON responses in markdown code fences — `llm_judge.py`
123+
strips these automatically
124+
- `scores.json` baselines: use `score_check.py --update` after accepting improvements
125+
93126
## Dependencies
94127

95128
- `gepa` (optimization engine)

CONCEPTS.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Concepts
2+
3+
Core concepts for understanding optimize-anything. For a hands-on tutorial, see [WALKTHROUGH.md](WALKTHROUGH.md).
4+
5+
## Artifact
6+
7+
The text being optimized. Can be a prompt, config, skill file, evaluator script, or any text where quality can be scored. Passed as a file path to the CLI.
8+
9+
## Evaluator
10+
11+
A function that scores an artifact candidate. Returns `{"score": <float>, ...}`. Three runtime modes:
12+
13+
- **Command** (`--evaluator-command`): shell script receiving JSON on stdin
14+
- **HTTP** (`--evaluator-url`): endpoint receiving POST JSON
15+
- **LLM judge** (`--judge-model`): litellm-backed model scoring against an objective
16+
17+
## Evaluator Protocol
18+
19+
- **Input:** `{"candidate": "<text>"}` via stdin (command) or POST body (HTTP)
20+
- **Output:** JSON with required `score` key (0.0-1.0) and optional diagnostic keys
21+
- Diagnostic keys become "Actionable Side Information" for gepa's reflection LM
22+
23+
## Evaluation Pattern vs Execution Mode
24+
25+
Two independent axes:
26+
27+
| Concept | What it answers | Values |
28+
|---------|----------------|--------|
29+
| `execution_mode` | How the evaluator runs (transport) | `command`, `http` |
30+
| `evaluation_pattern` | What scoring strategy it implements (intent) | `verification`, `judge`, `simulation`, `composite` |
31+
32+
A verification evaluator can run as a command or HTTP endpoint. These are never conflated.
33+
34+
## gepa
35+
36+
The optimization engine. Runs propose/evaluate/reflect cycles to improve artifacts. Uses a proposer LLM to generate candidates and an evaluator to score them. Configure with `--model` (proposer) and `--budget` (max evaluator calls).
37+
38+
## Intake Specification
39+
40+
Schema describing what the evaluator expects: quality dimensions (with weights), hard constraints, evaluation pattern, and execution mode. Normalized by `intake.py`. Provide via `--intake-json` or `--intake-file`.
41+
42+
## Quality Dimensions
43+
44+
Named scoring axes (e.g., "clarity", "conciseness") with float weights that sum to 1.0. Used by the LLM judge for dimension-weighted scoring and by intake normalization.
45+
46+
## Hard Constraints
47+
48+
Boolean pass/fail conditions. If any constraint is violated, the LLM judge forces the score to 0.0 regardless of other dimensions.
49+
50+
## RED-GREEN-OBSERVER Cycle
51+
52+
An iterative optimization workflow orchestrated by `scripts/live_integration.py`:
53+
54+
- **GREEN:** Run gepa to optimize an artifact (proposer generates, evaluator scores)
55+
- **RED:** Validate the result with multi-provider scoring (command + LLM judges)
56+
- **OBSERVER:** Read the score matrix and decide: continue, adjust objective, or stop
57+
58+
## Score Baselines
59+
60+
Tracked in `scores.json`. The `score_check.py` script ensures artifacts don't regress below their baselines. Use `--update` to raise baselines after improvements.
61+
62+
## Spec File
63+
64+
A TOML file (`--spec-file`) that bundles optimization parameters (artifact path, evaluator, budget, objective) for repeatable runs. Loaded by `spec_loader.py`.

Makefile

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
.PHONY: test test-fast gates gates-offline smoke score-check score-update install help
2+
3+
test: ## Run full test suite
4+
uv run pytest -v
5+
6+
test-fast: ## Run tests excluding integration
7+
uv run pytest -v -m "not integration"
8+
9+
gates: ## Run all quality gates
10+
uv run python scripts/check.py
11+
12+
gates-offline: ## Run gates without smoke (no API calls)
13+
uv run python scripts/check.py --skip-smoke
14+
15+
smoke: ## Run smoke harness
16+
uv run python scripts/smoke_harness.py --budget 1
17+
18+
score-check: ## Check score baselines
19+
uv run python scripts/score_check.py
20+
21+
score-update: ## Update score baselines
22+
uv run python scripts/score_check.py --update
23+
24+
install: ## Install dependencies
25+
uv sync
26+
27+
help: ## Show this help
28+
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
29+
awk 'BEGIN {FS = ":.*?## "}; {printf " %-18s %s\n", $$1, $$2}'

README.md

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,11 +199,19 @@ uv run optimize-anything optimize <seed_file> [options]
199199
| `--objective <text>` | Natural language objective | -- |
200200
| `--background <text>` | Domain knowledge/constraints | -- |
201201
| `--budget <n>` | Max evaluator invocations | 100 |
202+
| `--model <model>` | Proposer LLM (e.g., `openai/gpt-4o-mini`) | env `OPTIMIZE_ANYTHING_MODEL` |
203+
| `--judge-model <model>` | LLM judge evaluator (mutually exclusive with `--evaluator-command`/`--evaluator-url`) | -- |
204+
| `--judge-objective <text>` | Override objective for the judge (falls back to `--objective`) | -- |
205+
| `--spec-file <path>` | TOML spec file with bundled optimization parameters | -- |
206+
| `--run-dir <path>` | Directory to save run artifacts (auto-timestamped subdirectory) | -- |
207+
| `--diff` | Show diff between seed and best candidate on stderr | -- |
202208
| `--output, -o <file>` | Write best candidate to file | stdout |
203209

204210
If intake is provided, `execution_mode` is used to decide which evaluator source flag is required when neither `--evaluator-command` nor `--evaluator-url` is set. Explicit evaluator flags always win if both explicit flags and intake are supplied.
205211
`--output` must be a file path (not an existing directory).
206212

213+
> **Note:** Always pass `--model` explicitly when using gepa — the default proposer model may be unavailable in your environment.
214+
207215
### explain
208216

209217
```bash
@@ -254,6 +262,34 @@ Normalize and validate an evaluator intake specification. Outputs canonical JSON
254262
| `--intake-json <json>` | Inline intake spec JSON (mutually exclusive with flags) | -- |
255263
| `--intake-file <path>` | Path to intake spec JSON file (mutually exclusive with flags) | -- |
256264

265+
### score
266+
267+
Score a single artifact without running optimization:
268+
269+
```bash
270+
# With command evaluator
271+
uv run optimize-anything score artifact.txt \
272+
--evaluator-command bash evaluators/eval.sh
273+
274+
# With LLM judge
275+
uv run optimize-anything score artifact.txt \
276+
--judge-model openai/gpt-4o-mini \
277+
--objective "Score clarity, actionability, and specificity"
278+
```
279+
280+
| Flag | Description | Default |
281+
|---|---|---|
282+
| `artifact_file` | Path to artifact to score | (required) |
283+
| `--evaluator-command <cmd...>` | Shell command evaluator | -- |
284+
| `--evaluator-url <url>` | HTTP POST evaluator URL | -- |
285+
| `--judge-model <model>` | LLM judge model | -- |
286+
| `--objective <text>` | Objective for LLM judge scoring | -- |
287+
| `--judge-objective <text>` | Override objective for the judge | -- |
288+
| `--intake-json <json>` | Inline intake spec JSON | -- |
289+
| `--intake-file <path>` | Path to intake spec JSON file | -- |
290+
291+
Exactly one of `--evaluator-command`, `--evaluator-url`, or `--judge-model` is required.
292+
257293
## Architecture
258294

259295
```
@@ -263,6 +299,8 @@ src/optimize_anything/
263299
evaluator_generator.py # Generate evaluator scripts from seed + objective
264300
cli.py # CLI entry point (argparse)
265301
intake.py # Intake schema normalization
302+
llm_judge.py # LLM-as-judge evaluator (litellm)
303+
spec_loader.py # TOML spec file loading
266304
result_contract.py # Canonical optimize summary output
267305
__main__.py # python -m support
268306
@@ -339,8 +377,10 @@ uv tool uninstall optimize-anything
339377
claude plugin remove optimize-anything
340378
```
341379

342-
## Links
380+
## Learn More
343381

382+
- [Concepts](CONCEPTS.md) — glossary of core terms and architecture
383+
- [Walkthrough](WALKTHROUGH.md) — step-by-step tutorial for a full optimization cycle
344384
- [Installation Guide](install.md)
345385
- [Evaluator Cookbook](evaluator-cookbook.md)
346386
- [Examples](examples/)

0 commit comments

Comments
 (0)