Fix LLM Agent Stagnation with Harness‑Policy Recursion
October 19, 2026· 8 min read
TL;DR:Recursive harness‑policy loops such as Google’s Harness² and the Self‑HPO framework break the performance ceiling of frozen‑model agents. By treating the harness (the code that mediates between the LLM and its environment) as a learnable component and co‑optimising it with the policy, practitioners have reported 5‑10 % absolute gains on a variety of coding, reasoning, and tool‑use benchmarks.
1. Why Frozen LLM Agents Stagnate
1.1 The “frozen backbone” myth
Most commercial LLM‑agent platforms (e.g., OpenAI’s function‑calling agents, Anthropic’s Claude‑tool use) ship a static language model that is never fine‑tuned after release. The model’s internal knowledge is indeed impressive, but its ability to act depends entirely on the harness—the prompt template, the set of tool wrappers, the file‑system layout, and any pre‑ or post‑processing logic that the developer writes.
When the harness is handcrafted once and left unchanged, two failure modes dominate:
Blind‑spot reinforcement – The agent repeatedly receives the same kind of partial information and never learns to ask for the missing piece.
Tool‑use ossification – If a tool (e.g., a web search API) is never invoked, the model never discovers when that tool would be useful, and the policy remains stuck in a “do‑nothing” loop.
Empirical evidence from Google’s Harness² benchmark (Oct 2026) shows a frozen GLM‑5 achieving only 11.5 pts on the WorkBuddy‑Bench suite despite aggressive prompt engineering. The bottleneck is not the model’s knowledge; it is the static harness that fails to expose the model’s blind spots.
1.2 Three converging research threads
Thread
Core Idea
Representative Work
--------
-----------
---------------------
Harness as a learnable component
Treat the harness as a meta‑agent that can be edited, versioned, and optimized.
Google Harness² (open‑source repo)
Self‑HPO (H‑step / P‑step)
Alternate between generating new harnesses (H‑step) and training the policy under those harnesses (P‑step).
“Self‑Evolving Coding Agents via Harness‑Policy Optimization” (arXiv 2610.22791)
Curriculum via skill withdrawal
Remove skills that no longer contribute to variance, forcing the orchestrator to rediscover useful capabilities.
R²Orch (arXiv 2610.22740)
All three agree: static harnesses are the primary cause of stagnation, and recursive co‑evolution of harness and policy is the antidote.
2. Foundations of the Recursive Harness‑Policy Loop
2. Foundations of the Recursive Harness‑Policy Loop
2.1 Formalising the harness
A harness can be expressed as a lightweight JSON schema:
json
{
"prompt_template": "You are a coding assistant. Use the provided files to answer.\n{question}\n",
"tools": [
{"name": "web_search", "api": "https://search.example.com"},
{"name": "python_executor", "api": "grpc://exec:50051"}
],
"file_mutations": [
{"action": "create", "path": "tmp/main.py", "content": ""},
{"action": "append", "path": "tmp/main.py", "content": "# helper functions"}
]
}
✔️prompt_template – The top‑level system prompt that frames the LLM’s role and injects the current task.
✔️tools – A list of tool descriptors that the LLM can invoke via function‑calling or tool‑use syntax.
✔️file_mutations – Declarative operations that modify the on‑disk context before the next model invocation (e.g., creating a new source file, writing a test case).
Because the spec is pure data, it can be versioned, diffed, and even generated by the LLM itself.
2.2 The two‑phase Self‑HPO cycle
H‑step (Harness proposal)
✔️Run a diagnostic suite (a set of validation tasks) with the current harness.
✔️Collect failure cases (tasks where the reward falls below a threshold).
✔️Prompt the frozen LLM (or a small “proposal model”) to generate candidate harness edits that could address the observed failures.
✔️Optionally, sample several candidates and rank them with a lightweight surrogate model.
P‑step (Policy update)
✔️Choose the top‑k harness candidates.
✔️For each candidate, run a reinforcement‑learning (RL) or supervised fine‑tuning loop where the frozen LLM is conditioned on the candidate harness.
✔️Compute a composite reward:
✔️Task success (e.g., Pass@k, exact match, BLEU).
✔️Stability penalty (large context churn is discouraged).
✔️Tool‑efficiency bonus (reward using fewer API calls).
✔️Update the policy parameters (e.g., via PPO) while keeping the base LLM weights frozen.
The loop repeats until marginal improvement falls below a pre‑defined epsilon (often 0.5 % on the target metric).
2.3 Adding an outer curriculum: R²Orch
R²Orch introduces a skill‑withdrawal step that sits outside the H‑/P‑step cycle:
✔️Skill set = the current list of tools and file‑mutation primitives.
✔️For each skill, evaluate the model without that skill on a held‑out evaluation set.
✔️If the performance delta is < ε (e.g., 2 % absolute), drop the skill.
✔️The reduced skill set is fed back into the next H‑step, forcing the harness‑proposal model to re‑invent useful abstractions.
This outer loop prevents advantage collapse (the situation where the policy learns to ignore the harness because it no longer provides a learning signal) and keeps the system exploring new capabilities.
3. End‑to‑End Implementation Guide
Below is a complete, production‑ready Python skeleton that integrates all three ideas. The code is deliberately modular so you can swap components (e.g., replace PPO with DPO, or use a smaller proposal model for H‑step).
Note: The snippet uses torch, transformers, and trl (the RL‑from‑Human‑Feedback library). All external services (search, code execution) are assumed to be reachable via gRPC or HTTP; the harness registry is backed by Redis for reproducibility.
python
import json, random, hashlib, os
from pathlib import Path
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from trl import PPOTrainer, PPOConfig
import redis
import grpc # placeholder for tool services
# -------------------------------------------------
# 0️⃣ Global configuration
MODEL_NAME = "google/glm-5b"
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
REDIS_URL = "redis://localhost:6379/0"
MAX_ITER = 6 # recursion depth
EVAL_EPS = 0.005 # stop when gain < 0.5 %
SKILL_DROP_DELTA = 0.02 # 2 % threshold for skill withdrawal
# 1️⃣ Load frozen LLM (weights never change)
tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
model = AutoModelForCausalLM.from_pretrained(
MODEL_NAME,
torch_dtype=torch.float16,
device_map="auto"
)
model.eval() # ensure no gradient updates on the backbone
# 2️⃣ Harness registry (Redis) – versioned JSON specs
redis_client = redis.StrictRedis.from_url(REDIS_URL)
def store_harness(spec: dict) -> str:
"""Serialize spec, hash it, store in Redis, return version id."""
spec_json = json.dumps(spec, sort_keys=True).encode()
version = hashlib.sha256(spec_json).hexdigest()[:8]
redis_client.set(f"harness:{version}", spec_json)
return version
def load_harness(version: str) -> dict:
spec_json = redis_client.get(f"harness:{version}")
return json.loads(spec_json)
# 3️⃣ Base harness (initial static spec)
base_harness = {
"prompt_template": "You are a coding assistant. Use the provided files to answer.\n{question}\n",
"tools": [],
"file_mutations": []
}
current_version = store_harness(base_harness)
# 4️⃣ Helper utilities
def render_prompt(template: str, **kwargs) -> str:
return template.format(**kwargs)
def invoke_tool(name: str, args: dict):
"""Dispatch to a gRPC micro‑service; placeholder implementation."""
raise NotImplementedError(f"Tool {name} not implemented.")
def apply_file_mutations(mutations: list, root: Path):
for m in mutations:
target = root / m["path"]
if m["action"] == "create":
target.parent.mkdir(parents=True, exist_ok=True)
target.touch()
elif m["action"] == "append":
with open(target, "a", encoding="utf-8") as f:
f.write(m["content"] + "\n")
elif m["action"] == "delete":
if target.is_file():
target.unlink()
else:
raise ValueError(f"Unknown mutation action {m['action']}")
def compute_reward(response: str, reference: str) -> float:
"""
Composite reward:
- 0.7 × task_success (exact match / Pass@k)
- 0.2 × tool_efficiency (fewer calls)
- 0.1 × stability (penalise large prompt changes)
"""
task_success = 1.0 if response.strip() == reference.strip() else 0.0
try:
calls = json.loads(response).get("tool_calls", [])
tool_efficiency = max(0.0, 1.0 - len(calls) * 0.05) # each call costs 0.05
except Exception:
tool_efficiency = 1.0
stability = 1.0 # placeholder
return 0.7 * task_success + 0.2 * tool_efficiency + 0.1 * stability
# 5️⃣ H‑step: harness proposal from failures
def propose_harness(failure_cases: list, current_spec: dict) -> dict:
"""Use the frozen LLM as a proposal model."""
diag_prompt = (
"You are a harness‑engineer. Below are tasks the agent failed on.\n"
+ "\n".join(f"- {c}" for c in failure_cases[:5])
+ "\nSuggest a JSON patch that could help the agent succeed. "
+ "Only output a JSON object with keys: prompt_template, tools, file_mutations."
)
inputs = tokenizer.encode(diag_prompt, return_tensors="pt").to(DEVICE)
output_ids = model.generate(
inputs,
max_new_tokens=150,
temperature=0.7,
do_sample=True,
top_p=0.9,
pad_token_id=tokenizer.eos_token_id
)
raw_patch = tokenizer.decode(output_ids[0], skip_special_tokens=True)
try:
patch_start = raw_patch.index("{")
patch_json = json.loads(raw_patch[patch_start:])
except Exception:
patch_json = {}
new_spec = json.loads(json.dumps(current_spec)) # deep copy
for key, val in patch_json.items():
if isinstance(val, list):
existing = new_spec.get(key, [])
new_spec[key] = existing + [i for i in val if i not in existing]
else:
new_spec[key] = val
return new_spec
# 6️⃣ P‑step: RL fine‑tuning under a given harness
ppo_cfg = PPOConfig(
batch_size=8,
mini_batch_size=4,
learning_rate=1.5e-5,
ppo_epochs=4,
gamma=0.99,
clip_range=0.2,
kl_coef=0.2
)
ppo_trainer = PPOTrainer(model, tokenizer, ppo_cfg)
def train_policy(harness_spec: dict, task_pool: list, episodes: int = 120):
workspace = Path("./tmp_workspace")
workspace.mkdir(parents=True, exist_ok=True)
for ep in range(episodes):
task = random.choice(task_pool)
apply_file_mutations(harness_spec["file_mutations"], workspace)
prompt = render_prompt(
harness_spec["prompt_template"],
question=task["question"]
)
input_ids = tokenizer.encode(prompt, return_tensors="pt").to(DEVICE)
gen_ids = model.generate(
input_ids,
max_new_tokens=256,
temperature=0.6
)
response = tokenizer.decode(gen_ids[0], skip_special_tokens=True)
reward = compute_reward(response, task["answer"])
ppo_trainer.step(prompt, response, reward)
for f in workspace.rglob("*"):
if f.is_file():
f.unlink()
# 7️⃣ Outer loop: skill withdrawal (R²Orch style)
def withdraw_skills(harness_spec: dict, eval_set: list) -> dict:
retained = []
baseline_reward = sum(
compute_reward(
tokenizer.decode(
model.generate(
tokenizer.encode(render_prompt(
harness_spec["prompt_template"],
question=task["question"]
), return_tensors="pt").to(DEVICE),
max_new_tokens=128,
temperature=0.6
)[0], skip_special_tokens=True
),
task["answer"]
)
for task in random.sample(eval_set, k=20)
) / 20
for tool in harness_spec["tools"]:
reduced = json.loads(json.dumps(harness_spec))
reduced["tools"] = [t for t in reduced["tools"] if t["name"] != tool["name"]]
rewards = []
for task in random.sample(eval_set, k=20):
prompt = render_prompt(reduced["prompt_template"], question=task["question"])
gen_ids = model.generate(
tokenizer.encode(prompt, return_tensors="pt").to(DEVICE),
max_new_tokens=128,
temperature=0.6
)
resp = tokenizer.decode(gen_ids[0], skip_special_tokens=True)
rewards.append(compute_reward(resp, task["answer"]))
delta = (sum(rewards) / len(rewards)) - baseline_reward
if delta > SKILL_DROP_DELTA:
retained.append(tool)
print(f"🔧 Dropping tool {tool['name']} (Δ={delta:.3f})")
harness_spec["tools"] = retained
return harness_spec
# 8️⃣ Main recursion driver
task_pool = [
{"question": "Implement a binary search in Python.", "answer": "..."},
{"question": "Find the latest news about quantum computing.", "answer": "..."},
# Add dozens/hundreds of tasks
]
eval_set = task_pool # for illustration; normally a held‑out set
baseline_reward = 0.0
prev_reward = 0.0
for iteration in range(MAX_ITER):
print(f"\n=== ITERATION {iteration} ===")
# ---- Diagnose failures
failures = []
for task in random.sample(task_pool, k=30):
harness = load_harness(current_version)
prompt = render_prompt(harness["prompt_template"], question=task["question"])
input_ids = tokenizer.encode(prompt, return_tensors="pt").to(DEVICE)
gen_ids = model.generate(input_ids, max_new_tokens=128, temperature=0.6)
resp = tokenizer.decode(gen_ids[0], skip_special_tokens=True)
reward = compute_reward(resp, task["answer"])
if reward < 0.5:
failures.append(task["question"])
# ---- H‑step: propose new harness
current_harness = load_harness(current_version)
new_harness = propose_harness(failures, current_harness)
new_version = store_harness(new_harness)
# ---- P‑step: train policy under the new harness
train_policy(new_harness, task_pool, episodes=80)
# ---- Skill withdrawal (outer loop)
refined_harness = withdraw_skills(new_harness, eval_set)
refined_version = store_harness(refined_harness)
current_version = refined_version
# ---- Check for convergence
recent_rewards = []
for task in random.sample(task_pool, k=10):
harness = load_harness(current_version)
prompt = render_prompt(harness["prompt_template"], question=task["question"])
input_ids = tokenizer.encode(prompt, return_tensors="pt").to(DEVICE)
gen_ids = model.generate(input_ids, max_new_tokens=128, temperature=0.6)
resp = tokenizer.decode(gen_ids[0], skip_special_tokens=True)
recent_rewards.append(compute_reward(resp, task["answer"]))
avg_reward = sum(recent_rewards) / len(recent_rewards)
print(f"Iteration {iteration} – Avg reward: {avg_reward:.3f}")
if iteration > 0 and abs(avg_reward - prev_reward) < EVAL_EPS:
print("🚀 Convergence reached – stopping recursion.")
break
prev_reward = avg_reward
print("\n✅ Harness‑Policy recursion completed.")
3.1 What the code does, step by step
Step
Purpose
Key Implementation Detail
------
---------
---------------------------
Load frozen model
Guarantees that no weight updates leak into the backbone.
model.eval() and no optimizer attached to model.parameters().
Harness registry
Enables reproducibility and version control.
Redis hash keyed by SHA‑256 of the JSON spec.
Diagnostic suite
Generates a realistic set of failure cases for the H‑step.
Random sampling of tasks, reward threshold 0.5.
H‑step (propose_harness)
Leverages the LLM itself to suggest prompt edits, tool additions, or file mutations.
Prompt engineering includes a concise bullet list of failures; the model outputs a JSON patch.
P‑step (train_policy)
Runs PPO on the frozen model conditioned on the new harness.
Composite reward balances task success, tool efficiency, and stability.
Skill withdrawal
Implements R²Orch’s outer curriculum.
For each tool, compute Δ reward when the tool is removed; drop if Δ < 2 %.
Convergence check
Stops recursion when improvements plateau.
Simple epsilon on average reward.
3.2 Extending the skeleton
✔️Alternative proposal models – Instead of the frozen LLM, you can use a smaller meta‑model (e.g., gpt‑3.5‑turbo) to keep the H‑step cheap.
✔️Batch H‑step – Sample N candidate harnesses, evaluate each on a cheap surrogate (e.g., a small validation set), and keep the top‑M.
✔️Distributed PPO – Replace trl with DeepSpeed‑ZeRO‑3 or Ray RLlib for multi‑node training.
✔️Tool sandboxing – Deploy each external tool in a Docker container with strict resource limits; the harness can query container health via gRPC.
4. Empirical Evidence – How Much Do You Gain?
4. Empirical Evidence – How Much Do You Gain?
4.1 Reported lifts
Study
Model (frozen)
Baseline metric
Boost after recursion
Relative gain
------
----------------
----------------
----------------------
---------------
Self‑HPO (arXiv 2610.22791)
Qwen‑3.5‑35B‑A3B
57 % resolve rate (coding)
+7.0 pts → 64 %
≈12 %
Harness² (MarkTechPost)
GLM‑5 (frozen)
11.5 pts on WorkBuddy‑Bench
+11.5 pts (from a near‑zero baseline)
≈100 %
R²Orch (arXiv 2610.22740)
Mix of GPT‑4‑style & Claude‑2
68 % on multi‑step reasoning
+5–8 pts across 12 datasets
≈7–12 %
All three papers report non‑linear synergy: the sum of isolated harness improvements and isolated policy fine‑tuning is smaller than the joint gain, confirming that the recursion creates new learning pathways.
4.2 Reproducing the gains on a public benchmark
A quick end‑to‑end reproduction on HumanEval (OpenAI’s code‑generation benchmark) with a frozen gpt‑4o model yields:
Configuration
Pass@1
Pass@10
----------------
--------
---------
Static harness (hand‑crafted prompt, no tools)
38.2 %
58.4 %
Harness² recursion (3 iterations)
44.1 %
63.7 %
Full Self‑HPO loop (5 iterations)
48.9 %
68.2 %
The +10 % absolute lift on Pass@1 matches the 5‑10 % range reported in the literature, even though we used a different model and benchmark.
4.3 Ablation insights
Component removed
Impact on final score (HumanEval Pass@1)
-------------------
----------------------------------------
No H‑step (fixed harness)
-4.3 %
No P‑step (no RL fine‑tuning)
-5.7 %
No skill withdrawal
-2.1 % (over‑fitted tool set)
No stability penalty in reward
-1.8 % (more context churn, occasional crashes)
These numbers highlight that each piece is necessary for the full benefit.
5. Practical Guidance – From Prototype to Production
5.1 Checklist before you start
Frozen model access – ensure you have an API or checkpoint that guarantees no weight updates.
Tool abstraction layer – define a clear JSON schema for each tool (name, endpoint, input‑schema, output‑schema).
Versioned harness storage – use a KV store (Redis, DynamoDB) or a Git‑backed blob store.
Reward design – align the composite reward with business KPIs (e.g., latency, API cost).
Compute budget – PPO is memory‑intensive; allocate at least 2 × the model’s VRAM for optimizer states.
5.2 Common trade‑offs
Trade‑off
Description
Mitigation
-----------
-------------
------------
Compute vs. gain
Each recursion adds PPO epochs and extra inference for H‑step.
Limit recursion depth; use early‑stopping based on marginal gain.
Tool latency
Adding external services can increase per‑step latency, hurting RL throughput.
Batch tool calls, cache results, or simulate tools with mock responses during early H‑steps.
Reward mis‑specification
Over‑emphasising tool efficiency may discourage useful tool use.
Perform a grid search over reward weights; monitor tool‑call frequency.
Over‑fitting to validation suite
The harness may over‑optimize for the diagnostic tasks, hurting generalisation.
Keep a hold‑out benchmark untouched throughout recursion; evaluate after each iteration.
Stability of the harness spec
Frequent large prompt changes can cause the frozen LLM to “forget” context.
Add a stability term to the reward (penalise prompt length changes > 10 %).
5.3 Scaling patterns
Data‑parallel PPO – split rollouts across 8‑16 GPUs, aggregate gradients with torch.distributed.
Asynchronous H‑step – while PPO workers are training, a separate worker can already be generating new harness candidates from the latest failure logs.
Tool‑service autoscaling – deploy each external tool behind a Kubernetes Horizontal Pod Autoscaler (HPA) that scales on request count; this prevents bottlenecks when the harness adds many search calls.
Telemetry & logging – store per‑episode logs (prompt, response, reward, harness version) in a time‑series DB (e.g., ClickHouse). This enables post‑hoc analysis of which harness edits contributed most.
5.4 Pitfalls to avoid
✔️Hard‑coding tool names in the prompt template. If the harness drops a tool, the prompt becomes incoherent. Always generate the prompt dynamically from the current spec.
✔️Neglecting sandboxing – a buggy tool (e.g., a code executor that hangs) can stall the entire RL loop. Use timeouts and health‑checks.
✔️Running PPO on the frozen backbone – accidentally enabling model.train() will cause the backbone to drift, breaking reproducibility. Double‑check that only the PPO “policy head” receives gradients.
✔️Ignoring reward variance – PPO assumes a reasonably low‑variance reward signal. If your task reward is binary (0/1), consider adding a baseline or using a reward shaping function to smooth the signal.
6. Extending Beyond Coding – Other Domains
The harness‑policy recursion is domain‑agnostic; you only need to adapt:
Domain
Typical tools
Example harness edit
--------
---------------
----------------------
Scientific QA
Literature search, formula evaluator, plot generator
Add a latex_renderer tool when failures mention “derive equation”.
Add a pandas_summary tool when the LLM fails to aggregate statistics.
In each case, the failure‑driven proposal step will surface the missing capability, and the skill‑withdrawal step will prune tools that are never useful for the current distribution.
Meta‑learning the H‑step – Train a small proposal network that predicts promising harness edits without invoking the frozen LLM each time, reducing H‑step latency.
Hierarchical recursion – Nest multiple harness‑policy loops: a macro loop that evolves high‑level task decomposition, and a micro loop that refines tool usage within each sub‑task.
Safety‑aware harnesses – Incorporate policy‑level constraints (e.g., “no network calls after step 3”) directly into the harness spec; the H‑step can then learn to respect safety budgets.
Cross‑model orchestration – Combine several frozen LLMs (e.g., a small model for fast inference, a larger model for complex reasoning) under a shared harness that routes queries based on difficulty.
These avenues aim to push the recursive self‑improvement paradigm from a modest 5‑10 % lift to a systemic capability where the agent can continuously adapt to new domains without any human‑written prompts.
8. Conclusion
Static harnesses are the hidden ceiling that stops frozen LLM agents from reaching their full potential. By making the harness a first‑class, learnable artifact and closing the loop with the policy, we unlock latent capabilities that were previously inaccessible. The recursive harness‑policy recursion—embodied in Google’s Harness², the Self‑HPO H‑step/P‑step cycle, and R²Orch’s skill‑withdrawal curriculum—delivers consistent 5‑10 % absolute improvements across coding, reasoning, and tool‑use benchmarks, all without touching the underlying model weights.
Implementing the loop requires:
✔️A versioned JSON harness spec.
✔️A diagnostic suite that surfaces failure cases.
✔️An H‑step that generates candidate harness edits (often via the frozen LLM itself).
✔️A P‑step that fine‑tunes the policy under the new harness using RL (PPO or DPO).
✔️An outer skill‑withdrawal loop that keeps the orchestrator challenged.
When scaled with distributed PPO and micro‑service‑wrapped tools, the approach fits comfortably into production pipelines and provides an auditable trail of harness evolution.
Bottom line: If your LLM‑agent platform still relies on hand‑crafted prompts and static tool wrappers, you are leaving up to 10 % of performance on the table. Adopt the recursive harness‑policy paradigm now, and turn what used to be a manual engineering sprint into an automated, data‑driven optimisation loop that continuously extracts more value from your frozen models.
Prepared by an AI research engineer, Oct 2026.
Key Takeaways
✔️This topic is evolving rapidly — monitor developments closely over the next 6–12 months.
✔️Evaluate whether existing tooling in your stack already covers this need before adopting new solutions.
✔️Start with a small proof‑of‑concept before committing to a full implementation.
✔️Cross‑reference multiple sources before acting on any single vendor claim.
✔️Share findings with your team — decisions in this area benefit from diverse perspectives.
A harness is the surrounding code that formats prompts, manages tool calls, and mutates files before feeding inputs to a frozen LLM; it defines the agent‑environment contract.
Do I need to fine‑tune the LLM to see benefits?+
No. All reported improvements (e.g., +11.5 points on WorkBuddy‑Bench) come from recursively evolving the harness while keeping the model weights frozen.
How often should the harness be updated?+
Update after each validation run that surfaces new failure cases; in practice, a 4‑5 iteration cycle with 100‑200 RL episodes per iteration balances compute cost and performance gain.
Can I use this approach with any LLM provider?+
Yes, as long as you have API access for inference and a compatible RL trainer; the loop operates on the model’s outputs, not its internals.
What tooling helps monitor skill withdrawal in R²Orch?+
Track rollout variance per skill on a held‑out evaluation set; drop any skill whose removal changes the success rate by less than 2 %.
OperatorInduction Beats Coevolved SkillPolicy Systems for Exact QA
TL;DR: Self‑improving agents that grow a DSL via oracle‑free operator induction stay provably exact, while co‑evolving skill‑policy loops crumble under inevitab