---
type: phase
name: plan_improvements
input: iteration_state
role: app_architect
model_class: strong
allowed_ops: [read_file, write_file, edit_file, delete_file, glob_files, grep_files]
---

Design concrete, minimal DSL file changes that will improve the target skill's score on the next iteration.

## Step 1 — Read the target skill's DSL

Read all DSL files under the work directory. The resolved work path is in
`iteration_state.session._resolved_paths.target_skill_root`. Use glob ops:

```
{"kind": "file", "op": "glob", "path": "<_resolved_paths.target_skill_root>/**/*.md"}
{"kind": "file", "op": "glob", "path": "<_resolved_paths.target_skill_root>/**/*.yaml"}
```

Do NOT construct path strings yourself. Use the value from `_resolved_paths.target_skill_root`
exactly as provided.

Read each returned file with file read ops. You need the current state of:
- `skill.md` — overall graph and finish criteria
- `phases/*.md` — instructions and roles
- `artifacts/*.yaml` — schemas

## Step 1b — Check for trace signal (optional)

If a `skill_improver_traces` artifact is available in the workspace (written by the
`collect_traces` phase when `improvement_source ∈ {traces, both}`), incorporate its
findings into your diagnosis. The traces artifact carries observed failure patterns,
error frequencies, slow phases, and version-hash success-rate splits — use this
alongside the test scores to identify which phase deserves attention first.

If no traces artifact is present, proceed with test scores alone (default behavior).

## Step 2 — Diagnose the weakness

Use `iteration_state.latest_eval.weakest_phase` and `latest_eval.summary` to identify what is failing this iteration. Cross-reference with the phase's instructions and the artifact schema you just read.

Find a concrete root cause from one of these patterns:
- **Phase instructions are vague** where the failing criterion requires specificity.
- **Artifact schema is missing fields** that the criterion checks for.
- **Phase role is mismatched** with the task being demanded.
- **Graph structure problem** — e.g. a review phase missing, or a phase ordering causing premature finish.

If `iteration_state.session.improvement_focus` is non-empty, prioritize issues that fall under that focus area.

## Step 3 — Consult the history (anti-loop logic)

Inspect `iteration_state.history`. Based on patterns there, adjust your plan:

- **Regression** — if `latest_eval.overall_score` is LOWER than the previous iteration's `eval_score` (`history[-1].eval_score`): the previous changes were harmful. Consider partial reverts of those files, or a different angle entirely. Look at `history[-1].files_modified` and `history[-1].plan_summary` to see what NOT to repeat.
- **Stagnation** — if `current_iteration > 1` and `latest_eval.overall_score` is within 0.02 of `history[-1].eval_score`: the previous strategy is not moving the needle. Try a fundamentally different angle — DO NOT propose the same kind of change again.
- **No more changes useful** — if you cannot identify a concrete, evidence-backed change that will plausibly improve the score, output an empty `changes` array. apply_improvements will treat this as a stop signal (`termination_reason = "no_more_changes_planned"`).

## Step 4 — Design changes

Output `improvement_plan.changes` — an ordered list of file changes. Rules:

- Make changes minimal and targeted. Do NOT rewrite files that already work.
- For phase files: preserve the frontmatter (--- delimited YAML) and existing style.
- For artifact files: follow the existing YAML schema format.
- Each change's `rationale` MUST cite specific evidence from `latest_eval.summary` or the file you read.
- Use action `update` for existing files, `create` for new ones, `delete` to remove a stale file.
- File paths are project-relative (e.g. `reyn/local/my_app/phases/review.md`).

### Step 4a — Consider adding a Python preprocessor

If the eval failure pattern looks **deterministic** rather than reasoning-shaped, a `python` preprocessor step is often the right fix instead of fiddling with prompts. Telltale signs:

- The criterion checks an exact count / length / regex / format and the LLM keeps getting it slightly wrong
- The phase wastes act turns reading a structured input the LLM only needs summarized numerically
- The artifact has fields the LLM has to compute (token counts, statistics, parsed components) and the LLM gets them inconsistent across runs

In those cases, plan two changes together:

1. `create` a `<phase_name>_helpers.py` (or extend an existing one) under
   the skill directory with a function `(artifact: dict) -> JSON-serializable`
   that does the deterministic computation.
2. `update` the relevant phase's frontmatter to add a `preprocessor` block
   with a `type: python` step plus a matching `permissions.python` entry.
   Default `mode: safe`. Include the `output_schema` so the LLM sees the
   typed enriched artifact.

A typical skill_improver plan that adds one Python step is **2–3 changes**:
the new `.py` file, the phase frontmatter update, and (sometimes) a tweak to
the phase instructions telling the LLM to use the new injected fields.

Anti-pattern guard: do NOT add a Python step when the failure is genuinely
about judgment, tone, or generation quality. Python helps with counting and
parsing — it cannot make the LLM write better prose.

### Step 4b — Consider tightening `allowed_ops`

If the eval failure pattern looks like the phase **is using the wrong tool**
(e.g. a memory phase calling `web_search`, a review phase running `shell`),
the fix is often `allowed_ops` rather than instruction edits. Telltale signs:

- The phase's act turns include op kinds that aren't part of its core task
- The phase emits `web_search` / `web_fetch` when no external lookup was
  asked for
- The phase wastes turns on ops whose results don't shape the final artifact

Cross-reference `op_catalog` with what the phase actually needs and propose
an `update` to its frontmatter narrowing `allowed_ops`. Smallest set wins.
Empty `[]` for pure decision phases. The runtime default `[file, ask_user]`
is permissive — meta-skills should explicitly tighten it when the eval
shows drift.

### Step 4c — Consider rewriting `description` for trigger accuracy

The `description` field in the target skill's `skill.md` is the **primary
triggering signal** the chat router compares the user's turn against.
A "too narrow" description causes **under-triggering**: the router
hand-waves the request instead of dispatching the right skill when the
user's phrasing doesn't literally include the skill's name.

This pattern is invisible to `eval.md`-driven scoring (= eval measures
correctness when the skill IS invoked, not whether the router picks it).
Two signals to watch for:

  1. **User's `improvement_focus` mentions** "trigger", "description",
     "routing", "under-trigger", "doesn't fire", "doesn't get picked", or
     similar — the user has noticed the under-trigger problem directly.
  2. **Eval evidence is thin** but the user reports the skill "isn't being
     used" — the eval criteria pass when invoked, so the gap is at the
     router-decision layer, not the phase-execution layer.

When either signal is present, propose an `update` to the target skill's
`skill.md` rewriting the `description` field using the pushy pattern
established in `skill_builder/phases/plan_skill.md`:

  - Cover BOTH what the skill does AND when it should fire.
  - List the synonym set / trigger contexts the user is likely to use.
  - Keep ≤ 2 sentences, ≤ ~250 chars (300 soft cap).
  - No marketing fluff — every word is either capability or trigger.

Example shape:

```
description: <one-sentence what>. Use whenever the user asks to <action1>,
  <action2>, or <related-phrasing> — even if they don't explicitly say
  "<skill_name>".
```

Treat this as one candidate `update` change in your plan. If the skill
already has a pushy + trigger-aware description (= explicit "Use whenever
…" or equivalent), and the user's reported problem is something else,
do NOT touch the description.

## Output

Emit `improvement_plan` with:
- `iteration_state`: the input, passed through verbatim
- `summary`: one paragraph describing this iteration's strategy
- `target_phase`: typically `latest_eval.weakest_phase`
- `changes`: the list designed in Step 4 (may be empty for a stop signal)

Choose `transition` → `apply_improvements`.

**CRITICAL — decide turn output format**: Every decide turn response MUST include a top-level `control` block with at least `type` and `decision` fields. The OS rejects responses that emit only `artifact` without `control`. Always structure your final output with both `control` and `artifact` at the top level.
