Chapter 16 of 18

Don't Let the Optimizer Cheat

Explain this chapter with AI

Copy this prompt into ChatGPT, Claude, Gemini, a local model, or another AI.

Apply this chapter with AI

Copy this prompt into ChatGPT, Claude, Gemini, a local model, or another AI.

Don’t Let the Optimizer Cheat

Chapter 15 expanded the program’s access. It can search repositories, retrieve memory, use tools, and maybe explore large contexts programmatically.

That power threatens the experiment.

The central question:

How can an optimization experiment appear valid while the program or optimizer has quietly gained access to the answer?

Cheating is often accidental. A field named validation_result slips into generation inputs. A memory tool returns the historical accepted patch. A retriever indexes a later repository revision. A GEPA feedback string includes the exact gold answer. A developer inspects holdout failures, changes instructions, and evaluates on the same holdout again.

None of those failures requires malicious behavior. They come from giving one phase of the experiment access to information that belongs to another phase.

The experiment therefore needs a firewall around information flow, not merely a train/dev/holdout column.


1. Leakage paths

Common paths:

Path Example
direct input leakage human_accepted=true is passed through .with_inputs(...)
nested metadata leakage an allowed context object contains validation_result several levels down
outcome leakage historical validation or promotion state enters the generation-time view
duplicate-family leakage train and holdout have different IDs but represent the same normalized input or source revision family
memory leakage a retrieved prior case contains the accepted solution or a later outcome
temporal leakage retrieval or tools expose a repository revision newer than the case being replayed
tool leakage an agent can read answer files, future commits, evaluator state, or forbidden stores
feedback leakage reflective feedback contains the exact gold answer rather than a bounded diagnosis
evaluator leakage candidate generation can query the evaluator or validation result it is supposed to predict
holdout exhaustion holdout failures repeatedly influence prompts, metrics, tools, or policy
policy leakage comparison thresholds are changed after seeing holdout results

CoCoder’s current corpus architecture encodes several of these boundaries explicitly. ProgramEvaluationCaseDTO separates generation-time input from outcome evidence, and sanitize_generation_input() removes known outcome fields before historical executions become case inputs. Dataset splitting uses a stable grouped hash so cases with the same normalized input fingerprint remain in the same split rather than leaking duplicate inputs across train and holdout.

Its frozen DSPy protocol adds another layer: the optimizer receives train/development cases only, candidate demonstrations are audited against holdout IDs before evaluation, and the comparison policy, validation contract, dataset membership, model configuration, and prompts remain frozen after holdout evidence is observed.


2. A small firewall

The teaching version is simple:

This is still only a teaching firewall, but it illustrates two stronger rules.

First, construct generation inputs from an allowlist. A blacklist is useful as a second assertion, but it cannot anticipate every future outcome-field name.

Second, isolation must be checked at more than the case-ID level. Different rows can represent the same underlying input, chapter, repository state, or solution family. The candidate_manifest fields above are an application-owned audit contract, not DSPy fields; if demonstration lineage or feedback lineage matters, record it while optimization is running rather than trying to reconstruct it later.

Even this is not a complete semantic firewall. A field called notes can contain the gold answer without using a forbidden key. Deterministic checks catch structural leakage; adversarial tests and provenance audits are still required.


3. Adversarial leakage example

Suppose a dataset conversion accidentally does this:

bad_example = dspy.Example(
    sentence=row["sentence"],
    editorial_goal=row["editorial_goal"],
    local_context=row["local_context"],
    reference_rewrite=row["reference_rewrite"],
).with_inputs(
    "sentence",
    "editorial_goal",
    "local_context",
    "reference_rewrite",
)

The task model now receives the reference answer. An optimizer can appear excellent without learning the intended mapping.

The important detail from Chapter 7 still applies: .with_inputs(...) controls which dspy.Example fields are passed to the task program. It is not a complete optimizer firewall. Train/dev labels can legitimately remain on an example for metrics or optimization, while holdout examples must remain outside optimizer visibility entirely.

The firewall version constructs the task input separately from the label-bearing evaluation row:

The same rule applies to repository repair. A generation-time view may include the issue, target/intervention references, base repository revision, allowed repository evidence, and validation-contract identity. Historical patches, validation outcomes, engineering deltas, comparison recommendations, and human dispositions belong to outcome evidence when those are what the experiment is trying to predict or improve against.

Then attack the firewall deliberately:


4. Holdout exhaustion

A holdout set is not magic. It is an information role.

Strategies:


5. Tool and memory firewalls

Tool permissions should be split by phase and case time:


What Usually Goes Wrong

Symptom Likely cause How to diagnose it What to change
Holdout score keeps improving after manual tuning Holdout has become development evidence Review the exposure ledger and changes after first inspection Retire it for final claims and freeze a fresh holdout
Train and holdout have different IDs but nearly identical cases Split checks only case IDs Compare normalized-input/source-family fingerprints Group related cases before splitting and rerun the experiment
Agent finds the exact historical patch Memory/tool surface exposed post-outcome evidence Inspect retrieved records, timestamps, revisions, and fields Build decision-time memory/tool views and exclude later outcomes
GEPA produces reference-specific instructions Feedback carried answer-bearing labels Inspect feedback records and source case lineage Emit bounded diagnostic feedback or explicitly classify the run as label-bearing optimization
Candidate demos cannot be checked against holdout Demo source lineage was lost Audit compile-time provenance rather than saved demo text alone Persist source case/group IDs when demos are created
Nested metadata contains validation outcome Blacklist checked only top-level keys Recursively inspect structured payloads and audit free-text fields Build generation payloads from allowlisted fields
Future repository file appears in evidence Tool or index used the wrong temporal snapshot Compare case revision with worktree/index/result fingerprints Pin repository and index state per case
Candidate beats baseline only with a broader tool surface Capability change was confounded with program optimization Compare tool/capability fingerprints Freeze capabilities or declare a separate experiment
Comparison threshold changes after holdout review Policy was tuned on evaluation evidence Diff manifest/policy versions against first holdout run Reject the comparison and rerun with a frozen policy
Leakage guard passes but gold answer appears in free text Semantic leak bypassed structural field checks Run adversarial fixtures and inspect provenance of text fields Add source-specific sanitizers and keep manual/semantic audits for high-risk channels

Conclusion

We gained an experimental firewall around information flow. Task inputs, labels, feedback, demonstrations, retrieval, memory, tools, repository revisions, evaluator state, and holdout inspection all need explicit phase-specific access rules and provenance.

We removed three assumptions: that a split alone prevents leakage, that different case IDs imply independent evidence, and that structural field filtering can catch every semantic leak.

A trustworthy candidate is therefore not one that merely scores well. It is one whose improvement survives a frozen comparison and whose provenance shows that the candidate and optimizer had access only to information allowed by the experiment.

That is enough to consider promotion. It is still not enough to call the candidate a production version.