orizu 0.3.5 → 0.3.6

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.
@@ -0,0 +1,830 @@
1
+ # Prompt Control Plane
2
+
3
+ Use this reference for the Phase 0 prompt, judge, scorer, runner, score, run, and optimization event control plane. These commands are for coding agents that need to push local artifacts into Orizu, run them locally, submit scores, and stream optimization progress back to the platform.
4
+
5
+ ## Contents
6
+
7
+ - [Default Command Strategy](#default-command-strategy)
8
+ - [Artifact Contracts](#artifact-contracts)
9
+ - [Command Matrix](#command-matrix)
10
+ - [Optimization Event Logging](#optimization-event-logging)
11
+ - [End-to-End Flow](#end-to-end-flow)
12
+ - [Notes and Limits](#notes-and-limits)
13
+
14
+ ## Default Command Strategy
15
+
16
+ 1. Verify auth: `orizu --local whoami`.
17
+ 2. Export API context for scripts: `eval "$(orizu --local env --project <team>/<project>)"`.
18
+ 3. Create immutable dataset versions before creating splits.
19
+ 4. Push runners before prompts, judges, or prompt-runner scorers; prompt versions pin runner versions.
20
+ 5. Register scorers after their backing prompt/runner exists, then bind headline/tracked scorers to prompts when the UI should show those metrics.
21
+ 6. Use `runners exec` to prove the runner contract locally before submitting runs or score runs.
22
+ 7. For common text-candidate optimization, prefer `orizu optimizations run-gepa`; it starts the run and logs events for you.
23
+ 8. After `run-gepa`, inspect `logs/<optimization_run_id>` first; it is the complete local trace for coding-agent analysis.
24
+ 9. Use `orizu optimizations export <run-id> --out <run-id>.optimization.json` when the local log is missing or the run happened elsewhere.
25
+ 10. Write and attach a markdown report for finished, failed, or cancelled runs; use `optimization-reports.md` for structure and diagnostic guidance.
26
+ 11. For custom optimizers, start an optimization run before local execution, then stream events into that run.
27
+ 12. Use bare HTTP for optimization events; use `orizu log` only as a shell fallback.
28
+ 13. Promote only accepted candidates; rejected candidates stay in optimization events.
29
+
30
+ Customer model-provider secrets stay local. Do not upload Anthropic/OpenAI/etc. API keys to Orizu.
31
+
32
+ Execution/privacy defaults:
33
+
34
+ - Runner subprocesses receive only the file-contract paths plus a small allowlist of provider/runtime environment variables. Orizu API tokens are not passed into runner processes.
35
+ - `orizu optimizations run-gepa` redacts dataset row payloads and reflection text in logged events by default. Use `--log-row-snapshots` only when the customer explicitly wants raw row and prompt text in the optimization event stream.
36
+ - `run-gepa` still writes complete local traces under `logs/<optimization_run_id>` by default. Treat those logs as sensitive: they include row inputs, model outputs, scores, feedback, scorer responses, reflection prompts, reflection responses, and candidate text.
37
+ - Runner artifacts, runner output, score result uploads, and optimization event payloads are size-capped. If a run needs larger observability payloads, store the large artifact separately and log a pointer.
38
+
39
+ ## Artifact Contracts
40
+
41
+ ### Runner Directory
42
+
43
+ Required file: `manifest.json`.
44
+
45
+ ```json
46
+ {
47
+ "name": "hip-note-judge-runner",
48
+ "description": "Scores one HIP note row with the judge prompt.",
49
+ "language": "python",
50
+ "command": ["python3", "runner.py"],
51
+ "supports_body_kinds": ["text"]
52
+ }
53
+ ```
54
+
55
+ For `runners exec`, the command must read input JSON from `ORIZU_RUNNER_INPUT_PATH` and write output JSON to `ORIZU_RUNNER_OUTPUT_PATH`.
56
+
57
+ Input shape:
58
+
59
+ ```json
60
+ {
61
+ "row": { "opaque": "dataset row object" },
62
+ "prompt": {
63
+ "body": "prompt body or null",
64
+ "body_kind": "text",
65
+ "provider_settings": {
66
+ "model": "claude-sonnet-4-6",
67
+ "temperature": 0,
68
+ "max_tokens": 4096
69
+ }
70
+ },
71
+ "prompt_version_id": "uuid",
72
+ "runner_version_id": "uuid",
73
+ "run_id": null
74
+ }
75
+ ```
76
+
77
+ Output shape:
78
+
79
+ ```json
80
+ {
81
+ "model_response": "parsed response or structured JSON",
82
+ "raw_api_response": {},
83
+ "token_in": 1234,
84
+ "token_out": 567,
85
+ "latency_ms": 1890,
86
+ "cost_usd": 0.0123,
87
+ "score": 0.8,
88
+ "feedback": "optional judge rationale",
89
+ "error": null
90
+ }
91
+ ```
92
+
93
+ Exit non-zero only for infrastructure failures. Row-level model or parsing errors should usually be represented in the output JSON with `error`.
94
+
95
+ ### Prompt Or Judge Directory
96
+
97
+ Required files: `orizu.prompt.json` plus the body file referenced by `body_file`.
98
+
99
+ Generator prompt:
100
+
101
+ ```json
102
+ {
103
+ "name": "hip-note-generator",
104
+ "role": "production_inference",
105
+ "description": "Generates a HIP note label.",
106
+ "body_file": "prompt.md",
107
+ "body_kind": "text",
108
+ "version_label": "v1",
109
+ "provider_settings": {
110
+ "model": "claude-sonnet-4-6",
111
+ "temperature": 0,
112
+ "max_tokens": 4096
113
+ },
114
+ "provenance": {
115
+ "kind": "coding-agent-edit"
116
+ }
117
+ }
118
+ ```
119
+
120
+ Judge prompt:
121
+
122
+ ```json
123
+ {
124
+ "name": "hip-note-judge",
125
+ "role": "judge_per_row",
126
+ "description": "Scores generated HIP notes row by row.",
127
+ "body_file": "judge.md",
128
+ "body_kind": "text",
129
+ "version_label": "v1",
130
+ "provider_settings": {
131
+ "model": "claude-sonnet-4-6",
132
+ "temperature": 0,
133
+ "max_tokens": 4096
134
+ },
135
+ "provenance": {
136
+ "kind": "coding-agent-edit"
137
+ }
138
+ }
139
+ ```
140
+
141
+ Judges are prompts with `role: "judge_per_row"` or `role: "judge_per_run"`.
142
+
143
+ ### Scorer Manifest
144
+
145
+ Scorers define metrics. The executable code still lives in a runner version, and LLM judge text still lives in a prompt version.
146
+
147
+ Prompt-runner row scorer:
148
+
149
+ ```json
150
+ {
151
+ "name": "hip-note-judge-score",
152
+ "description": "Scores HIP note candidates row by row.",
153
+ "mode": "row",
154
+ "implementation_kind": "prompt_runner",
155
+ "metric_key": "judge_score",
156
+ "metric_label": "Judge score",
157
+ "score_format": "percent",
158
+ "higher_is_better": true,
159
+ "requires_dataset": true,
160
+ "prompt_version_id": "judge-prompt-version-uuid",
161
+ "runner_version_id": "judge-runner-version-uuid"
162
+ }
163
+ ```
164
+
165
+ Set scorers aggregate over a set and can be used for headline, selection, or tracked reporting. They cannot be used as GEPA reflection scorers because reflection needs per-row feedback.
166
+
167
+ Use `orizu scorers exec` for scorer-level evaluation. Builtin set scorers run server-side over dataset rows plus subject results or dependency score-run evidence, then submit one canonical `score_runs` row by default. `runners exec --scorer-version` remains a low-level compatibility command that invokes a runner once per dataset row and writes JSONL row results.
168
+
169
+ Set scorer example for judge-vs-gold Cohen's kappa:
170
+
171
+ ```json
172
+ {
173
+ "name": "staged-actions-judge-kappa",
174
+ "description": "Measures batch-level agreement between the staged-actions judge and human labels.",
175
+ "mode": "set",
176
+ "implementation_kind": "builtin_metric",
177
+ "builtin_metric": "cohens_kappa",
178
+ "metric_key": "cohens_kappa",
179
+ "metric_label": "Cohen's kappa",
180
+ "score_format": "number",
181
+ "score_min": -1,
182
+ "score_max": 1,
183
+ "higher_is_better": true,
184
+ "requires_dataset": true,
185
+ "dependencies": [
186
+ {
187
+ "kind": "row_scorer",
188
+ "alias": "judge",
189
+ "scorer_version_id": "row-scorer-version-uuid"
190
+ }
191
+ ],
192
+ "input_mapping": {
193
+ "gold_label": "$row.gold_label",
194
+ "predicted_label": "$dependencies.judge.model_response.label"
195
+ },
196
+ "builtin_metric_config": {
197
+ "positive_class": "flag"
198
+ },
199
+ "dataset_requirements": {
200
+ "required_fields": ["gold_label"]
201
+ },
202
+ "diagnostics_schema": {
203
+ "sample_size": "number",
204
+ "accuracy": "number",
205
+ "confusion_matrix": "object",
206
+ "flag_recall": "number",
207
+ "flag_precision": "number",
208
+ "ok_recall": "number",
209
+ "ok_precision": "number"
210
+ }
211
+ }
212
+ ```
213
+
214
+ ### Optimizer Directory
215
+
216
+ Required file: `manifest.json`.
217
+
218
+ ```json
219
+ {
220
+ "name": "hip-gepa-optimizer",
221
+ "description": "Local GEPA-style optimizer for HIP judge prompt.",
222
+ "language": "python",
223
+ "entrypoint": "run_logged_optimization.py",
224
+ "optimizer_family": "gepa"
225
+ }
226
+ ```
227
+
228
+ The CLI stores optimizer zips and metadata. Phase 0 optimizer execution remains local.
229
+
230
+ ## Command Matrix
231
+
232
+ ### Environment
233
+
234
+ ```bash
235
+ orizu --local login
236
+ orizu --local whoami
237
+ eval "$(orizu --local env --project <team>/<project>)"
238
+ ```
239
+
240
+ `orizu env` exports `ORIZU_API_URL`, `ORIZU_TOKEN`, `ORIZU_PROJECT_ID`, and `ORIZU_PROJECT`.
241
+
242
+ ### Dataset Versions And Splits
243
+
244
+ ```bash
245
+ orizu --local datasets versions create <dataset-id-or-name> \
246
+ --project <team>/<project> \
247
+ --label v1 \
248
+ --json
249
+ ```
250
+
251
+ Returns `dataset_version_id`.
252
+
253
+ Create ratio-based splits:
254
+
255
+ ```bash
256
+ orizu --local datasets splits create <dataset-version-id> \
257
+ --name default \
258
+ --seed 42 \
259
+ --train 0.6 \
260
+ --validation 0.4 \
261
+ --test 0 \
262
+ --json
263
+ ```
264
+
265
+ Create predefined, Hugging Face-style splits:
266
+
267
+ ```json
268
+ {
269
+ "name": "default",
270
+ "strategy": "predefined",
271
+ "seed": 42,
272
+ "partitions": [
273
+ { "name": "train", "row_ids": ["row-1", "row-2"] },
274
+ { "name": "validation", "row_ids": ["row-3"] },
275
+ { "name": "test", "row_ids": [] }
276
+ ]
277
+ }
278
+ ```
279
+
280
+ ```bash
281
+ orizu --local datasets splits create <dataset-version-id> \
282
+ --from-file ./split.json \
283
+ --json
284
+ ```
285
+
286
+ Returns `split_set_id`.
287
+
288
+ ### Runners
289
+
290
+ ```bash
291
+ orizu --local runners push ./runner \
292
+ --project <team>/<project> \
293
+ --name hip-note-judge-runner \
294
+ --label default \
295
+ --json
296
+ ```
297
+
298
+ Returns `runner_version_id`.
299
+
300
+ Execute against a dataset split:
301
+
302
+ ```bash
303
+ orizu --local runners exec \
304
+ --prompt-version <prompt-version-id> \
305
+ --runner-version <runner-version-id> \
306
+ --dataset-version <dataset-version-id> \
307
+ --split-set <split-set-id-or-name> \
308
+ --split validation \
309
+ --runner-dir ./runner \
310
+ --out ./results.jsonl
311
+ ```
312
+
313
+ Omit `--runner-dir` to download and materialize the pinned runner version from Orizu. `--out` may end in `.jsonl` or `.jsonl.gz`.
314
+
315
+ ### Prompts And Judges
316
+
317
+ ```bash
318
+ orizu --local prompts push ./prompt \
319
+ --project <team>/<project> \
320
+ --runner-version <runner-version-id> \
321
+ --json
322
+
323
+ orizu --local judges push ./judge \
324
+ --project <team>/<project> \
325
+ --runner-version <judge-runner-version-id> \
326
+ --json
327
+ ```
328
+
329
+ Both commands return `prompt_version_id`.
330
+
331
+ List:
332
+
333
+ ```bash
334
+ orizu --local prompts list --project <team>/<project>
335
+ orizu --local judges list --project <team>/<project>
336
+ ```
337
+
338
+ List prompt comment threads for the latest version, or a specific label/version:
339
+
340
+ ```bash
341
+ orizu --local prompts comments <prompt-id-or-name> \
342
+ --project <team>/<project> \
343
+ [--label production | --version <prompt-version-id>] \
344
+ [--json]
345
+ ```
346
+
347
+ Human output shows the thread count, open/resolved counts, selected prompt text or source line, each top-level comment body, and replies. Use `--json` when an agent or script needs structured `summary` and `comments` data. Check unresolved comments before drafting or pushing the next prompt version.
348
+
349
+ Move a mutable label:
350
+
351
+ ```bash
352
+ orizu --local prompts labels set hip-note-judge production \
353
+ --project <team>/<project> \
354
+ --version <prompt-version-id> \
355
+ --json
356
+ ```
357
+
358
+ ### Scorers And Scores
359
+
360
+ Register a scorer after its backing prompt and runner versions exist:
361
+
362
+ ```bash
363
+ orizu --local scorers register \
364
+ --project <team>/<project> \
365
+ --name hip-note-judge-score \
366
+ --manifest ./scorer.manifest.json \
367
+ --prompt-version <judge-prompt-version-id> \
368
+ --runner-version <judge-runner-version-id> \
369
+ --label production \
370
+ --json
371
+ ```
372
+
373
+ Returns `scorer_version_id`.
374
+
375
+ Inspect scorers:
376
+
377
+ ```bash
378
+ orizu --local scorers list --project <team>/<project>
379
+ orizu --local scorers detail <scorer-id-or-name> --project <team>/<project> --json
380
+ orizu --local scorers labels set hip-note-judge-score production \
381
+ --project <team>/<project> \
382
+ --version <scorer-version-id> \
383
+ --json
384
+ ```
385
+
386
+ Use scorer versions directly with the runner contract:
387
+
388
+ ```bash
389
+ orizu --local runners exec \
390
+ --scorer-version <scorer-version-id> \
391
+ --dataset-version <dataset-version-id> \
392
+ --split-set <split-set-id-or-name> \
393
+ --split validation \
394
+ --runner-dir ./scorer-runner \
395
+ --out ./scores.jsonl
396
+ ```
397
+
398
+ Submit score results for a prompt version:
399
+
400
+ ```bash
401
+ orizu --local scores submit ./scores.jsonl \
402
+ --project <team>/<project> \
403
+ --scorer-version <scorer-version-id> \
404
+ --subject-version <prompt-version-id> \
405
+ --dataset-version <dataset-version-id> \
406
+ --split-set <split-set-id> \
407
+ --split validation \
408
+ --json
409
+ ```
410
+
411
+ For row scorer files, `.jsonl`, `.jsonl.gz`, or `.json` arrays are normalized into `resultsJsonl` and the server computes the mean unless the request body supplies an explicit score. Row objects may use `row_score`, `rowScore`, `score`, `judge_score`, or `passed`.
412
+
413
+ ```bash
414
+ orizu --local scorers exec \
415
+ --project <team>/<project> \
416
+ --scorer-version <set-scorer-version-id> \
417
+ --subject-version <prompt-version-id> \
418
+ --dataset-version <dataset-version-id> \
419
+ --split-set <split-set-id> \
420
+ --split validation \
421
+ --dependency-score-run judge=<row-score-run-id> \
422
+ --out ./set-score.json
423
+ ```
424
+
425
+ For builtin set scorers, `scorers exec` computes the aggregate and submits it by default. Use `--no-submit` to only write the aggregate object. Use `--dependency-results judge=./judge-results.jsonl` when the row-scorer evidence is local instead of already stored in a score run; use `--subject-results ./outputs.jsonl` for direct subject-output aggregation. Current builtin metrics are `cohens_kappa`, `accuracy`, `precision`, `recall`, and `f1`.
426
+
427
+ If you already computed the aggregate locally, submit the aggregate object explicitly:
428
+
429
+ ```bash
430
+ orizu --local scores submit ./set-score.json \
431
+ --aggregate \
432
+ --project <team>/<project> \
433
+ --scorer-version <set-scorer-version-id> \
434
+ --subject-version <prompt-version-id> \
435
+ --dataset-version <dataset-version-id> \
436
+ --split-set <split-set-id> \
437
+ --split validation
438
+ ```
439
+
440
+ Aggregate JSON shape:
441
+
442
+ ```json
443
+ {
444
+ "scorerVersionId": "set-scorer-version-uuid",
445
+ "subjectPromptVersionId": "prompt-version-uuid",
446
+ "datasetVersionId": "dataset-version-uuid",
447
+ "splitSetId": "split-set-uuid",
448
+ "splitName": "validation",
449
+ "scoreValue": 0.42,
450
+ "rowCount": 15,
451
+ "scoredRowCount": 15,
452
+ "diagnostics": {
453
+ "sample_size": 15,
454
+ "accuracy": 0.73,
455
+ "confusion_matrix": {
456
+ "tp": 5,
457
+ "fn": 1,
458
+ "fp": 3,
459
+ "tn": 6,
460
+ "positive_class": "flag"
461
+ },
462
+ "flag_recall": 0.83,
463
+ "flag_precision": 0.63,
464
+ "ok_recall": 0.67,
465
+ "ok_precision": 0.86
466
+ },
467
+ "feedbackSummary": "Kappa is positive but noisy on n=15; misses are concentrated in borderline staged-action rows.",
468
+ "rowEvidence": [
469
+ {
470
+ "row_id": "row-1",
471
+ "gold_label": "flag",
472
+ "predicted_label": "flag",
473
+ "row_score": 1
474
+ },
475
+ {
476
+ "row_id": "row-2",
477
+ "gold_label": "ok",
478
+ "predicted_label": "flag",
479
+ "row_score": 0
480
+ }
481
+ ],
482
+ "dependencyScoreRunIds": [
483
+ {
484
+ "alias": "judge",
485
+ "scoreRunId": "row-score-run-uuid"
486
+ }
487
+ ]
488
+ }
489
+ ```
490
+
491
+ For optimization candidate set scores, replace `subjectPromptVersionId` with `optimizationRunId` plus `candidateId`.
492
+
493
+ When `--aggregate` is omitted, the CLI still detects aggregate-looking JSON objects and preserves them with a warning. Use `--aggregate` in automation so intent is unambiguous.
494
+
495
+ Submit score results for an optimization candidate:
496
+
497
+ ```bash
498
+ orizu --local scores submit ./candidate-scores.jsonl \
499
+ --project <team>/<project> \
500
+ --scorer-version <scorer-version-id> \
501
+ --optimization-run <optimization-run-id> \
502
+ --candidate <candidate-id> \
503
+ --dataset-version <dataset-version-id> \
504
+ --split-set <split-set-id> \
505
+ --split validation \
506
+ --json
507
+ ```
508
+
509
+ Bind scorers to prompt UI surfaces:
510
+
511
+ ```bash
512
+ orizu --local prompts scorers set-headline <prompt-id> \
513
+ --project <team>/<project> \
514
+ --scorer-version <scorer-version-id> \
515
+ --dataset-version <dataset-version-id> \
516
+ --split-set <split-set-id> \
517
+ --split validation \
518
+ --json
519
+
520
+ orizu --local prompts scorers add <prompt-id> \
521
+ --project <team>/<project> \
522
+ --scorer-version <scorer-version-id> \
523
+ --dataset-version <dataset-version-id> \
524
+ --split-set <split-set-id> \
525
+ --split validation \
526
+ --json
527
+ ```
528
+
529
+ ### Runs
530
+
531
+ Submit local runner output:
532
+
533
+ ```bash
534
+ orizu --local runs submit ./results.jsonl \
535
+ --project <team>/<project> \
536
+ --prompt-version <prompt-version-id> \
537
+ --runner-version <runner-version-id> \
538
+ --dataset-version <dataset-version-id> \
539
+ --split-set <split-set-id> \
540
+ --split validation
541
+ ```
542
+
543
+ For generator runs scored by a judge:
544
+
545
+ ```bash
546
+ orizu --local runs submit ./generator-results.jsonl \
547
+ --project <team>/<project> \
548
+ --prompt-version <generator-version-id> \
549
+ --runner-version <generator-runner-version-id> \
550
+ --dataset-version <dataset-version-id> \
551
+ --split-set <split-set-id> \
552
+ --split validation \
553
+ --judge-version <judge-version-id> \
554
+ --judge-runner-version <judge-runner-version-id>
555
+ ```
556
+
557
+ ### Optimizers
558
+
559
+ ```bash
560
+ orizu --local optimizers push ./optimizer \
561
+ --project <team>/<project> \
562
+ --name hip-gepa-optimizer \
563
+ --label gepa-v1 \
564
+ --json
565
+ ```
566
+
567
+ Returns `optimizer_version_id`.
568
+
569
+ Start a local optimization run before the optimizer process begins:
570
+
571
+ ```bash
572
+ orizu --local optimizations start \
573
+ --project <team>/<project> \
574
+ --optimizer-version <optimizer-version-id> \
575
+ --prompt-version <prompt-version-id> \
576
+ --selection-scorer <scorer-version-id> \
577
+ --reflection-scorer <row-scorer-version-id> \
578
+ --dataset-version <dataset-version-id> \
579
+ --split-set <split-set-id> \
580
+ --train-split train \
581
+ --validation-split validation \
582
+ --metadata '{"source":"local-gepa"}' \
583
+ --json
584
+ ```
585
+
586
+ Returns `optimization_run_id`. Pass that ID to the local optimizer as `ORIZU_RUN_ID`.
587
+
588
+ Optional tracked scorers:
589
+
590
+ - `--pareto-scorer <scorer-version-id>` runs a scorer for Pareto candidates.
591
+ - `--best-scorer <scorer-version-id>` runs a scorer for the current best candidate.
592
+
593
+ For bundled text-candidate GEPA-style optimization, let the CLI manage start/event/finish wiring:
594
+
595
+ ```bash
596
+ orizu --local optimizations run-gepa \
597
+ --project <team>/<project> \
598
+ --optimizer-version-id <optimizer-version-id> \
599
+ --candidate-version-id <prompt-version-id> \
600
+ --runner-version-id <runner-version-id> \
601
+ --candidate-runner-dir ./candidate-runner \
602
+ --scorer-version-id <row-scorer-version-id> \
603
+ --scorer-runner-version-id <scorer-runner-version-id> \
604
+ --scorer-runner-dir ./scorer-runner \
605
+ --dataset-version-id <dataset-version-id> \
606
+ --split-set-id <split-set-id> \
607
+ --train-split train \
608
+ --val-split validation \
609
+ --log-dir ./logs \
610
+ --budget light \
611
+ --max-iterations 3 \
612
+ --minibatch-size 3
613
+ ```
614
+
615
+ Useful GEPA flags:
616
+
617
+ - `--budget auto|light|medium|high`, `--max-metric-calls <n>`, `--max-full-evals <n>`.
618
+ - `--minibatch-size <n>` defaults to 3.
619
+ - `--candidate-selection-strategy pareto|current_best|epsilon_greedy`; default is `pareto`.
620
+ - `--reflection-model <provider/model>`, `--reflection-temperature <n>`, `--reflection-prompt-template <text|@file>`.
621
+ - `--reflection-max-tokens <n>` is explicit provider config, not a global default. It maps to Anthropic `max_tokens` and OpenAI `max_output_tokens`; Anthropic native Messages reflection requires it, while OpenAI can omit it unless the user wants a cap.
622
+ - `--reflection-provider-settings <json|@file>` passes provider-native reflection settings separately from the prompt text. Anthropic example: `{"thinking":{"type":"adaptive","display":"omitted"},"output_config":{"effort":"medium"}}`. OpenAI example: `{"reasoning":{"effort":"medium","summary":"auto"}}`.
623
+ - `--disable-evaluation-cache` turns off candidate/row/scorer cache reuse.
624
+ - `--auto-promote --promotion-label <label>` promotes the best candidate at the end.
625
+ - `--log-row-snapshots` includes raw row and reflection text in events; leave off by default.
626
+ - `--log-dir <dir>` controls the local log root; default is `logs`.
627
+ - `--no-local-log` disables local trace files. Use this only when the environment must not persist raw rows or reflection context.
628
+
629
+ Local `run-gepa` logs:
630
+
631
+ - The optimizer prints `[orizu-gepa] local log: <path>` after the run starts.
632
+ - The default path is `logs/<optimization_run_id>`.
633
+ - `run.json` stores run metadata, CLI args, and project/run ids.
634
+ - `prompt_context.json` and `scorer_context.json` store the candidate and scorer prompt contexts.
635
+ - `trainset.json` and `valset.json` store the full split row payloads.
636
+ - `events.jsonl` mirrors the optimization event stream, including redacted server payload fields where applicable.
637
+ - `evaluations.jsonl` stores each row evaluation with row input, output, score, feedback, raw/scorer responses, latency, tokens, cost, error, and cache status.
638
+ - `reflections.jsonl` stores each reflection prompt, response, child candidate text, parent/child ids, and minibatch row ids.
639
+ - `result.json` stores best candidate id/text, best score, seed score, promoted prompt version id, and final budget state.
640
+
641
+ For coding-agent insight generation, prefer reading the local log files in this order:
642
+
643
+ 1. `result.json` for the final winner and aggregate outcome.
644
+ 2. `evaluations.jsonl` to cluster failures, improvements, regressions, and scorer feedback by row.
645
+ 3. `reflections.jsonl` to understand why each child candidate was proposed.
646
+ 4. `events.jsonl` to reconstruct iteration order, Pareto updates, decisions, pauses, and promotions.
647
+
648
+ Optimization export:
649
+
650
+ ```bash
651
+ orizu --local optimizations export <optimization-run-id> \
652
+ --out ./<optimization-run-id>.optimization.json
653
+ ```
654
+
655
+ Use export when the local log is unavailable, the run happened on another machine, or a coding agent needs a portable single JSON artifact. The export fetches all optimization events, derives seed vs best, Pareto frontier, score-over-time, candidates, iterations, minibatch rows, and validation rows, and rehydrates row inputs from the dataset version artifact when possible. Server events may not contain row snapshots or reflection prompts unless the run used `--log-row-snapshots`; reflection responses are included for bundled `run-gepa` runs.
656
+
657
+ Lifecycle controls:
658
+
659
+ ```bash
660
+ orizu --local optimizations pause <optimization-run-id> --reason "inspect candidate"
661
+ orizu --local optimizations resume <optimization-run-id>
662
+ orizu --local optimizations finish <optimization-run-id> \
663
+ --best-score 0.82 \
664
+ --best-candidate candidate-7 \
665
+ --result-prompt-version <prompt-version-id> \
666
+ --report-file ./reports/<optimization-run-id>.md
667
+ orizu --local optimizations fail <optimization-run-id> --reason "provider outage" --report-file ./reports/<optimization-run-id>.md
668
+ orizu --local optimizations cancel <optimization-run-id> --reason "user stopped" --report "## Cancelled\n\nStopped after manual inspection."
669
+ ```
670
+
671
+ `pause` and `cancel` store `metadata.reason`. `fail` stores `metadata.failure_reason`.
672
+ `finish` marks the run `succeeded`; use it after accepted candidates have been promoted and the final prompt version is known.
673
+ Use `--report-file <path>` or `--report <markdown|@file>` on `finish`, `fail`, or `cancel` to attach the markdown report shown in the optimization detail Report tab. Prefer generating this from the local GEPA logs (`result.json`, `evaluations.jsonl`, `reflections.jsonl`, and `events.jsonl`) after the run ends. Report structure and interpretation rules: `optimization-reports.md`.
674
+
675
+ ## Optimization Event Logging
676
+
677
+ Custom optimizers use bare HTTP. Optimizer scripts should start a run through the CLI and POST each event as it happens:
678
+
679
+ ```bash
680
+ curl -sS -X POST "$ORIZU_API_URL/api/cli/optimization-runs/$ORIZU_RUN_ID/events" \
681
+ -H "Authorization: Bearer $ORIZU_TOKEN" \
682
+ -H "Content-Type: application/json" \
683
+ -d @event.json
684
+ ```
685
+
686
+ Event envelope:
687
+
688
+ ```json
689
+ {
690
+ "eventId": "seed-completed-1",
691
+ "sequence": 3,
692
+ "eventType": "seed_val_set_completed",
693
+ "eventLayer": "extension",
694
+ "optimizerFamily": "gepa",
695
+ "iteration": null,
696
+ "candidateId": null,
697
+ "parentCandidateId": null,
698
+ "childCandidateId": null,
699
+ "occurredAt": "2026-05-26T16:00:00.000Z",
700
+ "payload": {
701
+ "mean_score": 0.35,
702
+ "per_row_scores": [
703
+ {
704
+ "row_id": "row-1",
705
+ "score": 0.4,
706
+ "feedback": "rationale"
707
+ }
708
+ ]
709
+ }
710
+ }
711
+ ```
712
+
713
+ Rules:
714
+
715
+ - `sequence` is client-supplied, positive, and monotonic within one optimization run.
716
+ - `eventId` must be stable and unique within one optimization run.
717
+ - `eventLayer` is `core`, `extension`, or `system`.
718
+ - Generic core events: `run_started`, `iteration_started`, `candidate_proposed`, `candidate_scored`, `candidate_recommended`, `iteration_completed`, `run_completed`, `run_failed`.
719
+ - GEPA extension events: `seed_val_set_started`, `seed_val_set_completed`, `parent_minibatch_started`, `parent_minibatch_completed`, `reflection_started`, `reflection_completed`, `child_candidate_created`, `child_minibatch_started`, `child_minibatch_completed`, `acceptance_decision_made`, `merge_started`, `merge_completed`.
720
+ - Do not send per-LM-call telemetry to this endpoint in Phase 0. Put aggregate call/token/cost stats in iteration or run payloads.
721
+
722
+ Shell fallback:
723
+
724
+ ```bash
725
+ orizu --local log seed_val_set_completed \
726
+ --run-id "$ORIZU_RUN_ID" \
727
+ --sequence 3 \
728
+ --event-layer extension \
729
+ --optimizer-family gepa \
730
+ --payload @event-payload.json
731
+ ```
732
+
733
+ `orizu log` creates an `eventId` unless `--event-id` is provided. `event-payload.json` is the payload object only, not the full envelope.
734
+
735
+ ### Candidate Promotion
736
+
737
+ Promotion creates a new immutable `prompt_versions` row and appends a system event to the optimization run.
738
+
739
+ ```bash
740
+ curl -sS -X POST "$ORIZU_API_URL/api/cli/optimization-runs/$ORIZU_RUN_ID/promote" \
741
+ -H "Authorization: Bearer $ORIZU_TOKEN" \
742
+ -H "Content-Type: application/json" \
743
+ -d @promotion.json
744
+ ```
745
+
746
+ ```json
747
+ {
748
+ "candidateId": "candidate-7",
749
+ "promptId": "prompt-uuid",
750
+ "parentPromptVersionId": "parent-prompt-version-uuid",
751
+ "body": "new prompt body",
752
+ "bodyKind": "text",
753
+ "providerSettings": {
754
+ "model": "claude-sonnet-4-6",
755
+ "temperature": 0,
756
+ "max_tokens": 4096
757
+ },
758
+ "runnerVersionId": "runner-version-uuid",
759
+ "label": "production"
760
+ }
761
+ ```
762
+
763
+ Response:
764
+
765
+ ```json
766
+ {
767
+ "promptVersionId": "new-prompt-version-uuid"
768
+ }
769
+ ```
770
+
771
+ ## End-to-End Flow
772
+
773
+ ```bash
774
+ eval "$(orizu --local env --project hip/judge-optimization)"
775
+
776
+ DATASET_VERSION_ID="$(orizu --local datasets versions create hip-note-judge-labels --project hip/judge-optimization --label v1 --json | jq -r .dataset_version_id)"
777
+ SPLIT_SET_ID="$(orizu --local datasets splits create "$DATASET_VERSION_ID" --name default --seed 42 --train 0.6 --validation 0.4 --test 0 --json | jq -r .split_set_id)"
778
+
779
+ RUNNER_VERSION_ID="$(orizu --local runners push ./runner --project hip/judge-optimization --name hip-note-judge-runner --label default --json | jq -r .runner_version_id)"
780
+ JUDGE_VERSION_ID="$(orizu --local judges push ./judge --project hip/judge-optimization --runner-version "$RUNNER_VERSION_ID" --json | jq -r .prompt_version_id)"
781
+ SCORER_VERSION_ID="$(orizu --local scorers register --project hip/judge-optimization --name hip-note-judge-score --manifest ./scorer.manifest.json --prompt-version "$JUDGE_VERSION_ID" --runner-version "$RUNNER_VERSION_ID" --label production --json | jq -r .scorer_version_id)"
782
+ OPTIMIZER_VERSION_ID="$(orizu --local optimizers push ./optimizer --project hip/judge-optimization --name hip-gepa-optimizer --label gepa-v1 --json | jq -r .optimizer_version_id)"
783
+ OPTIMIZATION_RUN_ID="$(orizu --local optimizations start --project hip/judge-optimization --optimizer-version "$OPTIMIZER_VERSION_ID" --prompt-version "$JUDGE_VERSION_ID" --selection-scorer "$SCORER_VERSION_ID" --reflection-scorer "$SCORER_VERSION_ID" --dataset-version "$DATASET_VERSION_ID" --split-set "$SPLIT_SET_ID" --json | jq -r .optimization_run_id)"
784
+ export ORIZU_RUN_ID="$OPTIMIZATION_RUN_ID"
785
+
786
+ orizu --local runners exec \
787
+ --prompt-version "$JUDGE_VERSION_ID" \
788
+ --runner-version "$RUNNER_VERSION_ID" \
789
+ --dataset-version "$DATASET_VERSION_ID" \
790
+ --split-set "$SPLIT_SET_ID" \
791
+ --split validation \
792
+ --runner-dir ./runner \
793
+ --out ./judge-results.jsonl
794
+
795
+ orizu --local runs submit ./judge-results.jsonl \
796
+ --project hip/judge-optimization \
797
+ --prompt-version "$JUDGE_VERSION_ID" \
798
+ --runner-version "$RUNNER_VERSION_ID" \
799
+ --dataset-version "$DATASET_VERSION_ID" \
800
+ --split-set "$SPLIT_SET_ID" \
801
+ --split validation
802
+
803
+ orizu --local scores submit ./judge-results.jsonl \
804
+ --project hip/judge-optimization \
805
+ --scorer-version "$SCORER_VERSION_ID" \
806
+ --subject-version "$JUDGE_VERSION_ID" \
807
+ --dataset-version "$DATASET_VERSION_ID" \
808
+ --split-set "$SPLIT_SET_ID" \
809
+ --split validation
810
+
811
+ # After the local optimizer has logged events and promoted its accepted candidate:
812
+ orizu --local optimizations finish "$OPTIMIZATION_RUN_ID" \
813
+ --best-score 0.82 \
814
+ --best-candidate candidate-7 \
815
+ --result-prompt-version "$JUDGE_VERSION_ID"
816
+ ```
817
+
818
+ ## Notes And Limits
819
+
820
+ - All HTTP endpoints require `Authorization: Bearer $ORIZU_TOKEN`.
821
+ - Runner and optimizer zips are content-hashed. Re-uploading the same zip dedupes at the version layer.
822
+ - Prompt and judge versions are immutable. Labels move; versions do not.
823
+ - Scorers are first-class metric contracts. A score is meaningful as `(scorer version, subject or candidate, dataset version, split set, split)`.
824
+ - GEPA reflection scorers must be row-mode scorers so reflection has per-row feedback.
825
+ - Dataset splits are tied to a specific dataset version.
826
+ - `runners exec` writes runner-level row results locally; `runs submit` uploads and aggregates prompt-run results.
827
+ - `scorers exec` is the preferred path for scorer evaluation, especially builtin set scorers that aggregate dependency evidence.
828
+ - `scores submit --aggregate` is the supported path for precomputed set-score objects; `scores submit` without `--aggregate` remains the row-result path.
829
+ - `optimizations start` creates the live run row up front so local scripts can stream events immediately.
830
+ - Rejected optimizer candidates should remain in the event stream. Only accepted candidates should be promoted.