All skills
Skillintermediate

Iterative Optimization Loop

Run metric-driven iterative optimization. Define a goal, build measurement scaffolding, then run parallel experiments that converge toward the best solution.

Claude Code Knowledge Pack7/10/2026

Overview

Iterative Optimization Loop

Run metric-driven iterative optimization. Define a goal, build measurement scaffolding, then run parallel experiments that converge toward the best solution.

Interaction Method

Use the platform's blocking question tool: AskUserQuestion in Claude Code (call ToolSearch with select:AskUserQuestion first if its schema isn't loaded), request_user_input in Codex, ask_user in Gemini, ask_user in Pi (requires the pi-ask-user extension). Fall back to numbered options in chat only when no blocking tool exists in the harness or the call errors (e.g., Codex edit modes) — not because a schema load is required. Never silently skip the question.

Input

<optimization_input> #$ARGUMENTS </optimization_input>

If the input above is empty, ask: "What would you like to optimize? Describe the goal, or provide a path to an optimization spec YAML file."

Optimization Spec Schema

Reference the spec schema for validation:

references/optimize-spec-schema.yaml

Experiment Log Schema

Reference the experiment log schema for state management:

references/experiment-log-schema.yaml

Quick Start

For a first run, optimize for signal and safety, not maximum throughput:

  • Start from references/example-hard-spec.yaml when the metric is objective and cheap to measure
  • Use references/example-judge-spec.yaml only when actual quality requires semantic judgment
  • Prefer execution.mode: serial and execution.max_concurrent: 1
  • Cap the first run with stopping.max_iterations: 4 and stopping.max_hours: 1
  • Avoid new dependencies until the baseline and measurement harness are trusted
  • For judge mode, start with sample_size: 10, batch_size: 5, and max_total_cost_usd: 5

For a friendly overview of what this skill is for, when to use hard metrics vs LLM-as-judge, and example kickoff prompts, see:

references/usage-guide.md


Persistence Discipline

CRITICAL: The experiment log on disk is the single source of truth. The conversation context is NOT durable storage. Results that exist only in the conversation WILL be lost.

The files under .context/compound-engineering/ce-optimize/<spec-name>/ are local scratch state. They are ignored by git, so they survive local resumes on the same machine but are not preserved by commits, branches, or pushes unless the user exports them separately.

This skill runs for hours. Context windows compact, sessions crash, and agents restart. Every piece of state that matters MUST live on disk, not in the agent's memory.

If you produce a results table in the conversation without writing those results to disk first, you have a bug. The conversation is for the user's benefit. The experiment log file is for durability.

Core Rules

  1. Write each experiment result to disk IMMEDIATELY after measurement — not after the batch, not after evaluation, IMMEDIATELY. Append the experiment entry to the experiment log file the moment its metrics are known, before evaluating the next experiment. This is the #1 crash-safety rule.

  2. VERIFY every critical write — after writing the experiment log, read the file back and confirm the entry is present. This catches silent write failures. Do not proceed to the next experiment until verification passes.

  3. Re-read from disk at every phase boundary and before every decision — never trust in-memory state across phase transitions, batch boundaries, or after any operation that might have taken significant time. Re-read the experiment log and strategy digest from disk.

  4. The experiment log is append-only during Phase 3 — never rewrite the full file. Append new experiment entries. Update the best section in place only when a new best is found. This prevents data loss if a write is interrupted.

  5. Per-experiment result markers for crash recovery — each experiment writes a result.yaml marker in its worktree immediately after measurement. On resume, scan for these markers to recover experiments that were measured but not yet logged.

  6. Strategy digest is written after every batch, before generating new hypotheses — the agent reads the digest (not its memory) when deciding what to try next.

  7. Never present results to the user without writing them to disk first — the pattern is: measure -> write to disk -> verify -> THEN show the user. Not the reverse.

Mandatory Disk Checkpoints

These are non-negotiable write-then-verify steps. At each checkpoint, the agent MUST write the specified file and then read it back to confirm the write succeeded.

CheckpointFile WrittenPhase
CP-0: Spec savedspec.yamlPhase 0, after user approval
CP-1: Baseline recordedexperiment-log.yaml (initial with baseline)Phase 1, after baseline measurement
CP-2: Hypothesis backlog savedexperiment-log.yaml (hypothesis_backlog section)Phase 2, after hypothesis generation
CP-3: Each experiment resultexperiment-log.yaml (append experiment entry)Phase 3.3, immediately after each measurement
CP-4: Batch summaryexperiment-log.yaml (outcomes + best) + strategy-digest.mdPhase 3.5, after batch evaluation
CP-5: Final summaryexperiment-log.yaml (final state)Phase 4, at wrap-up

Format of a verification step:

  1. Write the file using the native file-write tool
  2. Read the file back using the native file-read tool
  3. Confirm the expected content is present
  4. If verification fails, retry the write. If it fails twice, alert the user.

File Locations (all under .context/compound-engineering/ce-optimize/<spec-name>/)

FilePurposeWritten When
spec.yamlOptimization spec (immutable during run)Phase 0 (CP-0)
experiment-log.yamlFull history of all experimentsInitialized at CP-1, appended at CP-3, updated at CP-4
strategy-digest.mdCompressed learnings for hypothesis generationWritten at CP-4 after each batch
<worktree>/result.yamlPer-experiment crash-recovery markerImmediately after measurement, before CP-3

On Resume

When Phase 0.4 detects an existing run:

  1. Read the experiment log from disk — this is the ground truth
  2. Scan worktree directories for result.yaml markers not yet in the log
  3. Recover any measured-but-unlogged experiments
  4. Continue from where the log left off

Phase 0: Setup

0.1 Determine Input Type

Check whether the input is:

  • A spec file path (ends in .yaml or .yml): read and validate it
  • A description of the optimization goal: help the user create a spec interactively

0.2 Load or Create Spec

If spec file provided:

  1. Read the YAML spec file. The orchestrating agent parses YAML natively -- no shell script parsing.
  2. Validate against references/optimize-spec-schema.yaml:
    • All required fields present
    • name is lowercase kebab-case and safe to use in git refs / worktree paths
    • metric.primary.type is hard or judge
    • If type is judge, metric.judge section exists with rubric and scoring
    • At least one degenerate gate defined
    • measurement.command is non-empty
    • scope.mutable and scope.immutable each have at least one entry
    • Gate check operators are valid (>=, <=, >, <, ==, !=)
    • execution.max_concurrent is at least 1
    • execution.max_concurrent does not exceed 6 when backend is worktree
  3. If validation fails, report errors and ask the user to fix them

If description provided:

  1. Analyze the project to understand what can be measured

  2. Detect whether the optimization target is qualitative or quantitative — this determines type: hard vs type: judge and is the single most important spec decision:

    Use type: hard when:

    • The metric is a scalar number with a clear "better" direction
    • The metric is objectively measurable (build time, test pass rate, latency, memory usage)
    • No human judgment is needed to evaluate "is this result actually good?"
    • Examples: reduce build time, increase test coverage, reduce API latency, decrease bundle size

    Use type: judge when:

    • The quality of the output requires semantic understanding to evaluate
    • A human reviewer would need to look at the results to say "this is better"
    • Proxy metrics exist but can mislead (e.g., "more clusters" does not mean "better clusters")
    • The optimization could produce degenerate solutions that look good on paper
    • Examples: clustering quality, search relevance, summarization quality, code readability, UX copy, recommendation relevance

    IMPORTANT: If the target is qualitative, strongly recommend type: judge. Explain that hard metrics alone will optimize proxy numbers without checking actual quality. Show the user the three-tier approach:

    • Degenerate gates (hard, cheap, fast): catch obviously broken solutions — e.g., "all items in 1 cluster" or "0% coverage". Run first. If gates fail, skip the expensive judge step.
    • LLM-as-judge (the actual optimization target): sample outputs, score them against a rubric, aggregate. This is what the loop optimizes.
    • Diagnostics (logged, not gated): distribution stats, counts, timing — useful for understanding WHY a judge score changed.

    If the user insists on type: hard for a qualitative target, proceed but warn that the results may optimize a misleading proxy.

  3. Design the sampling strategy (for type: judge):

    Guide the user through defining stratified sampling. The key question is: "What parts of the output space do you need to check quality on?"

    Walk through these questions:

    • What does one "item" look like? (a cluster, a search result page, a summary, etc.)
    • What are the natural size/quality strata? (e.g., large clusters vs small clusters vs singletons)
    • Where are quality failures most likely? (e.g., very large clusters may be degenerate merges; singletons may be missed groupings)
    • What total sample size balances cost vs signal? (default: 30 items, adjust based on output volume)

    Example stratified sampling for clustering:

    stratification:
      - bucket: "top_by_size"     # largest clusters — check for degenerate mega-clusters
        count: 10
      - bucket: "mid_range"       # middle of non-solo cluster size range — representative quality
        count: 10
      - bucket: "small_clusters"  # clusters with 2-3 items — check if connections are real
        count: 10
    singleton_sample: 15          # singletons — check for false negatives (items that should cluster)
    

    The sampling strategy is domain-specific. For search relevance, strata might be "top-3 results", "results 4-10", "tail results". For summarization, strata might be "short documents", "long documents", "multi-topic documents".

    Singleton evaluation is critical when the goal involves coverage — sampling singletons with the singleton rubric checks whether the system is missing obvious groupings.

  4. Design the rubric (for type: judge):

    Help the user define the scoring rubric. A good rubric:

    • Has a 1-5 scale (or similar) with concrete descriptions for each level
    • Includes supplementary fields that help diagnose issues (e.g., distinct_topics, outlier_count)
    • Is specific enough that two judges would give similar scores
    • Does NOT assume bigger/more is better — "3 items per cluster average" is not inherently good or bad

    Example for clustering:

    rubric: |
      Rate this cluster 1-5:
      - 5: All items clearly about the same issue/feature
      - 4: Strong theme, minor outliers
      - 3: Related but covers 2-3 sub-topics that could reasonably be split
      - 2: Weak connection — items share superficial similarity only
      - 1: Unrelated items grouped together
      Also report: distinct_topics (integer), outlier_count (integer)
    
  5. Guide the user through the remaining spec fields:

    • What degenerate cases should be rejected? (gates — e.g., "solo_pct <= 0.95" catches all-singletons, "max_cluster_size <= 500" catches mega-clusters)
    • What command runs the measurement?
    • What files can be modified? What is immutable?
    • Any constraints or dependencies?
    • If this is the first run: recommend execution.mode: serial, execution.max_concurrent: 1, stopping.max_iterations: 4, and stopping.max_hours: 1
    • If type: judge: recommend sample_size: 10, batch_size: 5, and max_total_cost_usd: 5 until the rubric and harness are trusted
  6. Write the spec to .context/compound-engineering/ce-optimize/<spec-name>/spec.yaml

  7. Present the spec to the user for approval before proceeding

0.3 Search Prior Learnings

Dispatch ce-learnings-researcher to search for prior optimization work on similar topics. If relevant learnings exist, incorporate them into the approach.

0.4 Run Identity Detection

Check if optimize/<spec-name> branch already exists:

git rev-parse --verify "optimize/<spec-name>" 2>/dev/null

If branch exists, check for an existing experiment log at .context/compound-engineering/ce-optimize/<spec-name>/experiment-log.yaml.

Present the user with a choice via the platform question tool:

  • Resume: read ALL state from the experiment log on disk (do not rely on any in-memory context from a prior session). Recover any measured-but-unlogged experiments by scanning worktree directories for result.yaml markers. Continue from the last iteration number in the log.
  • Fresh start: archive the old branch to optimize-archive/<spec-name>/archived-<timestamp>, clear the experiment log, start from scratch

0.5 Create Optimization Branch and Scratch Space

git checkout -b "optimize/<spec-name>"  # or switch to existing if resuming

Create scratch directory:

mkdir -p .context/compound-engineering/ce-optimize/<spec-name>/

Phase 1: Measurement Scaffolding

This phase is a HARD GATE. The user must approve baseline and parallel readiness before Phase 2.

1.1 Clean-Tree Gate

Verify no uncommitted changes to files within scope.mutable or scope.immutable:

git status --porcelain

Filter the output against the scope paths. If any in-scope files have unco