docs(coder): analyze DSPy/GEPA for the coder optimizer tier

f3bad5277b67 · AtlantisPleb · · parent 4f4846065757

docs(coder): analyze DSPy/GEPA for the coder optimizer tier

Review the prior art across both codebases — the DSE Effect implementation
and its history audit, the 2026-06-28 Python-versus-Effect tier decision,
and the 2026-07-04 evolve-the-harness findings — against what the coder has
now. Two facts carry the analysis: DSE shipped 9,678 lines with grid search
standing in for a real optimizer and was removed by a repo-wide mandate
rather than on merit, and the plugin system already implements the DSPy
layer around the gap (typed signatures, content-addressed modules, traces,
and an external verifier as the metric).

Record the tier boundary the coder should keep: upstream gepa at the Python
tier where Harbor already lives, emitting candidates with evidence; landing
stays a reviewed change. Add the O-series ledger entries and thread the
optimizer through the plan's axes, sequencing, and lever sources.

Tracker: OpenAgentsInc/openagents#122, #123.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K7q2vA5LJroLTR6ZFbRq6j
Co-Authored-By
Claude Fable 5 <noreply@anthropic.com>

Deploy story

What this commit did to the running system — joined from the forge receipt chain, the part a commit page elsewhere cannot show.

Not deployed through the forge lane

No push, promotion, build, or deploy receipt references this commit (receipts are scanned over a bounded recent window). Changes shipped by full node replacement carry their proof in the release gate receipt instead.

Changed files

  • added docs/coder/2026-08-26-dspy-gepa-coder-optimization.md
  • modified docs/coder/autoimprove.md
  • modified docs/coder/best-practices.md
  • modified docs/coder/runbook.md

Diff

4 files changed, +302 -2

docs/coder/2026-08-26-dspy-gepa-coder-optimization.md added +208

@@ -0,0 +1,208 @@

1
# DSPy, GEPA, and the coder: from DSE to an optimizer over the Gym
2
3
Date: 2026-08-26. Status: analysis, feeding the autoimprovement plan
4
(`docs/coder/autoimprove.md`) and the tracker. Companions:
5
`docs/coder/runbook.md`, `docs/coder/best-practices.md`. Prior art reviewed:
6
the DSPy-in-Effect history audit
7
(`docs/dspy/2026-07-20-dspy-in-effect-git-history-audit.md`), the
8
Python-versus-Effect decision audit
9
(`docs/research/2026-06-28-dspy-rlm-python-backend-vs-effect-audit.md`,
10
historical), the evolve-the-harness audit
11
(`docs/research/2026-07-04-harness-optimization-evolve-the-harness-audit.md`,
12
historical), and in the openagents.com repo the plugin model assessment
13
(`docs/2026-08-24-triage-and-plugin-model-assessment.md`), the Harbor plan
14
(`docs/2026-08-24-harbor-terminal-bench-plan.md`), and the registry network
15
strategy (`docs/2026-08-24-registry-network-strategy.md`). Upstream clones:
16
`../projects/repos/dspy`, `../projects/repos/gepa`, `../projects/repos/rlm`.
17
18
## 1. What we already built, twice
19
20
The history is longer than it looks from the current tree, and it settles
21
several questions the coder lane would otherwise re-litigate.
22
23
**DSE (February 2026).** OpenAgents built a real DSPy-in-Effect:
24
`@openagentsinc/dse`, "Declarative Self-Improving Effect," 53 files and
25
9,678 lines in the terminal package plus production wiring in the web app —
26
typed signatures on Effect Schema, a structured Prompt IR under stable
27
hashes, an evaluation system with datasets, metrics, and caching, a bounded
28
compiler, immutable policy artifacts, receipts, budgets, a canary gate, and
29
one recorded production promotion. Two findings from the history audit
30
matter now:
31
32
- **It never contained a real optimizer.** The compiler was deterministic
33
  grid search, greedy few-shot selection, and rule-based refinement — not
34
  MIPROv2, not GEPA. The hard part of DSPy was the part DSE did not have.
35
- **It was not removed on merit.** A whole-app deletion took the serving
36
  side, then a repository-wide Rust-only mandate deleted the package
37
  (pruned in `d7f53fccc`, archived in the backroom repo). The audit's words:
38
  the removal sequence shows unstable architecture ownership, not a
39
  comparative rejection of DSE quality.
40
41
**The hybrid decision (June 2026, historical but load-bearing).** The
42
2026-06-28 audit answered "reimplement or adopt" with a tier rule this
43
document keeps: **use upstream Python DSPy and GEPA as the offline
44
optimization tier that produces candidate artifacts and evidence; keep the
45
online serving and governance path native, as the authority that selects,
46
gates, and admits.** Reimplementing GEPA's Pareto-evolutionary search in
47
our own stack was judged large, low-leverage duplication of a co-developed,
48
paper-backed upstream (`dspy` depends on `gepa[dspy]`) — and DSE's missing
49
optimizer is the empirical support for that judgement.
50
51
**The external validation (July 2026).** The evolve-the-harness audit
52
recorded a published result: a frozen open model moved 63.4% → 80.1% on a
53
1,251-task legal benchmark purely by letting an automated loop rewrite the
54
harness. The loop mechanics it recommended adopting are exactly the shape
55
the coder loop needs: one mechanism per candidate with copy-and-adapt
56
diffs, a token-cost term inside the objective, a noise-floor acceptance
57
gate stated with its trial count, code mechanisms treated as first-class
58
candidates (they transferred across model families; tuned prompts did
59
not), and a judge-gaming caution — a harness evolved against a grader can
60
overfit the grader.
61
62
**The lineage in current product docs.** The openagents.com plugin model
63
assessment names the plugin manifest's typed interface "a signature in the
64
DSE sense" and adopts the old laws wholesale: sandboxed modules, typed and
65
validated boundaries, content-addressed immutable artifacts, no keyword
66
routing, and *an optimizer output is a candidate, never a deployment*. The
67
Harbor plan reserves RewardKit rollouts for "GEPA-style" optimization under
68
the same law, and the registry strategy places the eventual selection loop
69
"bounded and offline in the DSE/GEPA style." The direction was decided
70
three times; what is missing is the running loop.
71
72
## 2. The plugin system is the DSPy layer we kept
73
74
The claim "our plugin system lends itself to DSPy" is precise, not
75
analogical. The correspondence:
76
77
| DSPy concept | Coder counterpart | State |
78
| --- | --- | --- |
79
| Signature (typed I/O contract) | Plugin manifest input/output schemas, host-validated | Shipped |
80
| Module (a callable unit) | WASM plugin, content-addressed by digest | Shipped, twelve in-tree |
81
| Program (composed modules) | The coder harness: system prompt + tools + catalog + plugins per turn | Shipped |
82
| Demos / instructions (optimizable text) | System prompt, tool descriptions, the twelve catalog lines, knowledge-base stances | Shipped, but **compiled into code** |
83
| Retrieval rail | `knowledge-base` plugin: corpus queried every turn, attached as a bracketed note | Shipped, with a governed promotion path |
84
| Metric | Harbor verifier + ATIF token metrics per trial | Shipped (the Gym) |
85
| Trace | ATIF trajectory per trial | Shipped |
86
| Optimizer / teleprompter | — | **Missing** (as it was in DSE) |
87
88
Two entries deserve emphasis:
89
90
- **The knowledge base is an optimizable parameter surface that reaches
91
  every turn without a code change.** A stance edit plus a rebuild changes
92
  what the harness attaches to matching turns; the corpus is already
93
  reviewed-like-content with stable ids and a promotion path from system
94
  memory. This is the cleanest channel an optimizer could write
95
  candidates into — and the one with governance already in place.
96
- **The catalog lines are the selection policy DSPy would tune.** Twelve
97
  slots, one sentence each, contested (best practice P3). Which plugin a
98
  model reaches for on which task class is a function of that text today;
99
  the registry strategy's "selection loop" is this surface under
100
  optimization.
101
102
What we do not have — and did not have in DSE either — is the optimizer.
103
The rest of the machine is more complete than DSE's ever was, because the
104
metric is now an external verifier over a receipted store rather than an
105
in-house eval harness.
106
107
## 3. GEPA fits the Gym
108
109
GEPA's contract is one call: `optimize(seed_candidate, trainset, valset,
110
task_lm, reflection_lm, max_metric_calls)` over any textual parameter,
111
reflecting on full execution traces, keeping a Pareto pool of candidates.
112
Every argument has a concrete referent here:
113
114
| GEPA argument | Gym referent |
115
| --- | --- |
116
| `seed_candidate` | The current text surfaces: system prompt, tool descriptions, catalog lines, stances |
117
| `trainset` (dev) | `tb2-quick`, plus `owned-closed-issues` once its environments exist |
118
| `valset` (holdout) | `tb2-cross-section` — run sparingly, exactly as the runbook already directs |
119
| metric | Verifier acceptance minus a token-cost term, read from the trial's `result.json` and ATIF metrics |
120
| traces for reflection | ATIF trajectories — GEPA reflects over full execution traces, which every trial already leaves |
121
| `reflection_lm` | The cycle review (runbook §6); OpenAgentsInc/openagents#121 automates precisely this role |
122
| Pareto pool + lineage | `bench-results/` rows (hash-chained) + `docs/coder/reviews/` |
123
124
Read in this frame, **the autoimprovement loop just seeded is manual GEPA
125
with a population of one**: a cycle is a single mutation, the compare
126
against baseline is the acceptance test, a refutation is a discarded
127
candidate, and the ledger is the surviving pool. The runbook's disciplines
128
are the same ones the evolve-the-harness audit extracted from the
129
published loop — one lever per cycle is copy-and-adapt; M-series practices
130
are the acceptance gate. GEPA parallelizes the loop and adds the Pareto
131
memory; it does not change its laws.
132
133
The budget shapes the split. GEPA's own numbers are 100–500 metric calls
134
per optimization; a cross-section run is 12 tasks at minutes-to-hours each
135
under emulation. So the optimizer screens on the cheap sets (`tb2-quick`,
136
owned tasks — minutes per rollout) and only surviving candidates spend a
137
cross-section run, which doubles as the judge-gaming control: a candidate
138
that gamed the dev verifier fails the holdout it never trained against.
139
The dev set's narrowness is a real constraint (the audit's 24-task caveat
140
applies to our 2+12 harder), which is one more reason the
141
`owned-closed-issues` suite — real tracker issues with known accepted
142
outcomes — is worth standing up as trainset material.
143
144
## 4. The tier boundary, restated for the coder
145
146
The June rule survives contact with the current architecture cleanly,
147
because the offline tier already speaks Python: Harbor is Python, the
148
adapter is Python, and `bench/` is where both live. Concretely:
149
150
- **Offline (Python, `bench/optimize/`):** upstream `gepa` wrapped around
151
  the existing suite runner as its metric function. Output is a candidate
152
  artifact — a diff over the staged text surfaces, its evidence rows, its
153
  lineage, and a transfer label naming the model family it was evolved
154
  against (prompt-class candidates are family-specific; the audit's
155
  transfer finding). Nothing in this tier lands anything.
156
- **Online (the coder, unchanged):** a candidate becomes a change the way
157
  every other lever does — a fresh worktree, a landing commit stating the
158
  measured delta, the review, the ledger. The standing law holds: **an
159
  optimizer output is a candidate, never a deployment.**
160
- **The seam that makes it possible:** the text surfaces must be staged as
161
  data the optimizer can diff and the build can verify, rather than string
162
  literals inside `crates/coder-lite` and `packages/openagents-cli`. The
163
  knowledge base already has this shape (corpus file → build → digest);
164
  the system prompt, tool descriptions, and catalog lines need the same
165
  treatment. This staging is also what lets a human cycle (#119) and an
166
  optimizer cycle produce identical artifact shapes.
167
168
What we deliberately do not do, on the DSE evidence: reimplement the
169
optimizer in-house, in Rust or TypeScript or Effect. DSE died with a
170
grid-search stand-in where GEPA should have been; the lesson is to consume
171
the upstream that now exists, at the tier where Python is already native.
172
RLM stays out of scope for the coder lane entirely, and no DSPy program
173
layer enters the product — the coder harness is the program.
174
175
## 5. What to build
176
177
Two new tracker issues, sequenced against the existing loop set
178
(#116–#121):
179
180
1. **Stage the optimizable text surfaces as artifacts**
181
   (OpenAgentsInc/openagents#122). Extract system prompt, tool
182
   descriptions, and catalog lines into versioned data with digests,
183
   consumed by both CLIs; define the candidate-diff format. Prerequisite
184
   for the optimizer; makes #119's manual levers diffable artifacts too.
185
2. **A GEPA lane over the Gym** (OpenAgentsInc/openagents#123).
186
   `bench/optimize/` wrapping upstream `gepa` around the suite runner:
187
   dev-set screening, holdout confirmation, candidate artifacts with
188
   evidence, lineage, cost-term objective, and transfer labels. Waits on
189
   the baselines (#118) and the staging (#122); the review command (#121)
190
   is its reflection seam.
191
192
And three relations to the existing set: #119's manual cycles are the seed
193
candidates and the noise-floor calibration for the optimizer; #120's
194
plugin A/B rows are the per-module evidence the selection-policy surface
195
will eventually train on; #121's typed proposals are GEPA reflections and
196
should share the candidate schema from #122 so a review proposal and an
197
optimizer mutation are the same object.
198
199
## 6. Risks and their controls
200
201
| Risk | Control |
202
| --- | --- |
203
| Overfitting the verifier (judge-gaming) | Holdout suite the optimizer never screens on; suites pinned by content; the deliberately-false-candidate discipline when a judge is an LLM |
204
| Prompt candidates silently family-specific | Transfer label required on every candidate; re-evaluate when the target model differs from the evolved-against family |
205
| Optimizer buys score with spend | Token-cost term inside the objective, not alongside it (ledger M2 already refuses laundered cost) |
206
| Rollout cost explosion | Screen on cheap sets, spend cross-section runs only on survivors; `max_metric_calls` is a hard argument, treat it as a budget envelope |
207
| Candidate auto-landing by drift | The standing law is a ledger entry with detection (review of any change that lands optimizer output without its evidence rows) |
208
| A third dead optimizer stack | Do not reimplement; consume upstream at the Python tier where Harbor already lives |
docs/coder/autoimprove.md modified +21 -2

@@ -99,7 +99,22 @@ work is the most expensive axis; it enters the loop only when a cheaper

99 99
lever has stopped paying, and it lands under the repository completion gate
100 100
(`pnpm run check`) like any other code.
101 101
102
### 2.4 Lanes and routing: which model gets which task class
102
### 2.4 The optimizer tier: GEPA over the Gym
103
104
The manual loop is GEPA with a population of one; the upstream optimizer
105
parallelizes it without changing its laws. The full analysis — the DSE
106
history, the plugin-system-as-DSPy-layer correspondence, the tier boundary,
107
and the budget math — is
108
`docs/coder/2026-08-26-dspy-gepa-coder-optimization.md`. The short form:
109
upstream Python `gepa` wraps the suite runner as its metric, mutates the
110
staged text surfaces (system prompt, tool descriptions, catalog lines,
111
knowledge-base stances), screens on cheap sets, confirms on the holdout
112
cross-section, and emits **candidates with evidence — never deployments**.
113
A candidate lands the way every other lever does: a reviewed commit with
114
its measured delta. Do not reimplement the optimizer in-house; DSE died
115
with a grid-search stand-in where GEPA should have been.
116
117
### 2.5 Lanes and routing: which model gets which task class
103 118
104 119
The same suite per catalog model, per lane, is the comparative matrix the
105 120
compute mix wants. Scores per lane are not just a leaderboard: they are the

@@ -207,7 +222,11 @@ up.**

207 222
5. **Automated review** — a second agent invoked with the trial artifacts
208 223
   produces the review without a human copying transcripts; proposals
209 224
   arrive machine-readable and the adopt step becomes a diff.
210
6. **Routing feedback** — per-lane suite scores feed lane selection, once
225
6. **The optimizer lane** (§2.4) — text surfaces staged as diffable
226
   artifacts, then upstream GEPA screening candidates against the Gym,
227
   sharing the review's proposal schema so a reflection and a mutation
228
   are the same object.
229
7. **Routing feedback** — per-lane suite scores feed lane selection, once
211 230
   cached-token accounting (#220) makes the cost axis honest.
212 231
213 232
The loop is not expected to converge on perfection. It is expected to stop
docs/coder/best-practices.md modified +65

@@ -169,6 +169,71 @@ plugin applies.

169 169
**Provenance:** harvest. **Detection:** review question when the A/B shows
170 170
a plugin installed but never invoked on tasks it should have served.
171 171
172
## Optimization
173
174
Provenance for this section: **dspy-gepa** —
175
`docs/coder/2026-08-26-dspy-gepa-coder-optimization.md`, which reviews the
176
DSE history audit, the 2026-06-28 Python-versus-Effect decision, and the
177
2026-07-04 evolve-the-harness audit.
178
179
### O1. An optimizer output is a candidate, never a deployment — `adopted`
180
181
An offline optimizer produces candidate artifacts with evidence. Landing
182
one is a separate, reviewed change carrying its measured delta, exactly
183
like a hand-written lever. This law predates the coder — it is carried
184
forward verbatim from the DSE/Blueprint contracts and restated in the
185
current plugin model assessment and registry strategy.
186
**Provenance:** dspy-gepa. **Detection:** review of any change that lands
187
optimizer output without its evidence rows.
188
189
### O2. Do not reimplement the optimizer — `adopted`
190
191
Consume upstream `gepa`/DSPy at the Python tier where Harbor already
192
lives. DSE shipped 9,678 lines with deterministic grid search standing in
193
for MIPROv2 and GEPA, and the hard part was the part it never had.
194
**Provenance:** dspy-gepa; DSE history audit. **Detection:** review of any
195
proposal to build a search/teleprompter in Rust, TypeScript, or Effect.
196
197
### O3. Screen on the dev set, confirm on a holdout — `adopted`
198
199
The optimizer screens candidates on cheap sets (`tb2-quick`,
200
`owned-closed-issues` when its environments exist) and spends a
201
`tb2-cross-section` run only on survivors. The holdout is also the
202
judge-gaming control: a candidate that gamed the dev verifier fails a set
203
it never trained against.
204
**Provenance:** dspy-gepa; the evolve-the-harness audit's judge-overfit
205
caution. **Detection:** candidate evidence must name both sets and state
206
dev-set coverage; a candidate scored only on the screening set is not
207
confirmed.
208
209
### O4. The cost term lives inside the objective — `adopted`
210
211
A token/cost penalty is part of the metric the optimizer maximizes, not a
212
figure reported beside it. Otherwise the optimizer buys score with spend.
213
**Provenance:** dspy-gepa (the published loop's
214
`−0.005×tokens_per_million`); ledger M2 for the accounting half.
215
**Detection:** review of the objective function in the optimizer lane.
216
217
### O5. Candidates carry a transfer label — `adopted`
218
219
Every candidate records the model family it was evolved against. Code
220
mechanisms transfer across families; tuned prompts do not, and have
221
backfired cross-family. Re-evaluate a prompt-class candidate when the
222
target family differs.
223
**Provenance:** dspy-gepa (the published loop measured +14.4 points
224
same-family versus +0.4 cross-family). **Detection:** candidate artifact
225
schema requires the field; review rejects an unlabeled prompt candidate.
226
227
### O6. Acceptance states its trial count and floor — `adopted`
228
229
A promotion gate is "beats the incumbent by ≥N over K trials," with both
230
numbers written down, set just above the measured noise floor. On
231
`tb2-quick` the available rates are 0, .5, and 1, so the floor is
232
structural, not statistical — say so rather than implying precision the
233
suite cannot carry (ledger M5).
234
**Provenance:** dspy-gepa. **Detection:** candidate evidence without a
235
stated trial count is not a promotion argument.
236
172 237
## Repository
173 238
174 239
### R1. Pack with `pnpm pack`, never `npm pack` — `adopted`
docs/coder/runbook.md modified +8

@@ -113,6 +113,10 @@ One per cycle (best practice M1). Sources, in order of cost:

113 113
4. **Structural gaps** (autoimprove §2.3: compaction, history, shell
114 114
   parsing) — only when cheaper levers have stopped paying, and with the
115 115
   designated suite oracle named before you start.
116
5. **Optimizer candidates** (autoimprove §2.4), once the lane exists — a
117
   GEPA candidate is a lever like any other and enters at §4 with its
118
   evidence rows, transfer label, and stated acceptance floor. It does not
119
   land on the optimizer's say-so (ledger O1).
116 120
117 121
Write down, before implementing: the lever, the suite that will measure it,
118 122
and the delta direction that would confirm it. If you cannot name the

@@ -264,3 +268,7 @@ an evidence pointer is rejected at the adopt step. Save the output to

264 268
  rounds.
265 269
- The PTY-driven interactive harness (autoimprove §7.4) does not exist
266 270
  yet; best practice V2 is enforced by rule, not by gate.
271
- The optimizer lane (autoimprove §2.4) does not exist yet. The text
272
  surfaces it would mutate are still string literals in the two CLIs
273
  rather than staged artifacts; until that lands, every cycle is a
274
  hand-written lever and the ledger's O-series applies to nothing running.

This page updates live while a promote is in flight · changelog