> ## Documentation Index
> Fetch the complete documentation index at: https://docs.basemode.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Corrections and tuning

> How base notices that you corrected the AI, turns the correction into a rule change you approve, and looks through your recent sessions for the changes your rules need.

When you correct the AI, the correction usually means a rule is missing, did not arrive, or did not work. Since 0.16.0 base closes that loop: it notices the correction, asks the AI to file it as a proposal, and changes nothing until you approve. A rule pass, `base tune`, finds the corrections nobody filed and the rules that need merging, splitting or retiring.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
base rule review
base tune
base log corrections
```

## How base notices a correction

Four checks, and no one of them decides.

1. **Your wording.** Each prompt after a session's first is checked for a phrase from `[corrections] phrases`, as whole words with case ignored: "no,", "wrong", "that's not", "not what i asked", "i told you", "again", "stop doing", "why did you", swearing, and the rest of the list.
2. **What you did.** You stopped a turn with Esc, refused a tool call, changed a file the AI had just written, or sent nearly the same request twice in a row.
3. **The AI's own marker** at the start of a line of its reply: `CORRECTED:`, `UPDATED:`, `MISREAD:` or `DEFERRED:` (`[corrections] markers`). A marker in backticks, in a code block or in the middle of a line is a quote and does not count.
4. **The question.** When your wording suggests a correction, that prompt carries one line; when what you did does, your next prompt carries it:

```text theme={"theme":{"light":"github-light","dark":"github-dark"}}
This may be a correction. If it is, run base rule propose --from-turn after answering.
```

The AI, which read the whole exchange, decides. It answers you first, then files the correction if it was one.

**Why it works this way.** A phrase alone is a poor detector: "don't" and "again" are in plenty of prompts that correct nothing. The AI on the other end has the context to tell, so base asks rather than guesses, and the question costs one line.

**The gotcha.** A `DEFERRED:` marker means the AI held its position. It is logged and never proposed, because a disagreement is not a rule.

## The CORRECTED line

`base install` and `base scaffold` offer once to add one line to `~/.claude/CLAUDE.md`, just above the BASE CLI section:

```text theme={"theme":{"light":"github-light","dark":"github-dark"}}
When the user corrects you, start that reply with "CORRECTED: <what you got wrong>".
```

That makes the AI mark its own corrections, the clearest of the four signals. For an unattended install, `base install --corrections-line` adds it and `base install --no-corrections-line` does not. If you said no, or were never asked, session start carries the same line once per session instead, unless a CLAUDE.md the session loads already asks for one of the markers. `base uninstall` removes it with the BASE CLI section.

## Turning a correction into a proposal

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
base rule propose --from-turn
base rule propose --from-turn --keywords "refund, chargeback" --dry-run
```

Inside Claude Code, `--from-turn` reads the session's own transcript: the last prompt you typed, the AI's marker line and the signals of that turn. It compares the correction with every rule and decision base holds and sorts it three ways:

| The correction is | When | The proposal |
| - | - | - |
| a **rewrite** | a rule fits and was served this session | new wording for that rule (`--text`) |
| a **keyword gap** | a rule or decision fits and was never served | keywords on that rule, decision or domain |
| a **new rule** | nothing fits | a new rule (`--text`), placed with `--domain` |

The prompt you typed becomes the proposal's first test prompt, so the change has to serve it. `--rule`, `--decision` or `--new` overrides the sort, and running it again for the same turn replaces that turn's proposal. Nothing changes until the proposal is approved.

## Reviewing proposals

Session start shows the count when any are waiting:

```text theme={"theme":{"light":"github-light","dark":"github-dark"}}
rule proposals: 3 pending · base rule review
```

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
base rule review
base rule review --approve p-0004
base rule review --reject p-0005 --reason "one-off, not a rule"
base rule review --edit p-0006 --text "Refunds need a reason code" --keywords "refund"
```

On a terminal, `base rule review` walks the pending proposals with one key each: `a` approve, `e` edit, `r` reject, `s` skip, `q` stop. Each shows the prompts behind it and its replay. A rejected proposal is never made again.

**Check before you approve.** `base rule replay` runs a change over your recent prompts in the match log (the last 500 by default) and shows which prompts would start or stop serving the rule, and its share of all of them:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
base rule replay p-0004
base rule replay --rule billing.9f2c --drop-keyword "main"
```

A change that would serve more than a quarter of your prompts (`[tune] broad_share`) is flagged TOO BROAD, and approving it needs `--broad-ok`.

## The rule pass: `base tune`

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
base tune --dry-run
base tune
```

`base tune` reads what your sessions did since the last pass and writes proposals for `base rule review`. It applies nothing. For each session it reads the transcript past the turns the last pass judged and makes one call to headless Claude on Haiku, which says which prompts corrected the AI, the one-sentence rule that would have prevented each, and which domain a prompt that matched nothing belongs to. Answers are cached, so a rerun on the same turns makes no call, and `--max-calls` (10 by default) caps a pass. `--dry-run` shows what it would read and how many calls it would make, and calls nothing.

Once a day, or with `--store`, one more call reads the match log for rules to merge, rules to split and keywords to drop. A rule nothing served in 30 days, that the pass has known for 30 days, is proposed for retirement with no call.

**You do not have to remember it.** No hook ever runs it, because it spends model calls and can take minutes. After `[tune] corrections` (3) flagged corrections, or after `[tune] turns` (15) prompts without a pass when one was corrected or matched nothing, the next prompt carries a line saying a pass is due. Session start says when earlier sessions ended with corrections not yet passed.

## What each kind of proposal changes

| Proposal | When approved |
| - | - |
| keyword gap | adds the keywords to the rule, decision or domain |
| rewrite | replaces the rule's wording; the old wording is superseded, not deleted |
| new rule | adds the rule, with the prompt as its first test |
| drop keyword | takes the keyword off and adds the narrower words the judge gave, in one approval |
| merge | writes one rule with the merged wording, carrying both rules' keywords and tests, and supersedes both |
| split | rewrites the rule as its first part and adds the second part as a new rule beside it |
| retire | stops the rule being served by any hook; it keeps its text, gets the status `retired`, and still shows in `base rule list --include-superseded` |

`base rule unretire <domain>.<id>` serves a retired rule again. A rule written in `domains.toml` moves to the graph first when a proposal changes it, and the output says so.

## Trying a matcher in the shadows

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
base shadow start --matcher bm25
base shadow start --from-proposals p-0004,p-0006
base shadow report
base shadow promote
base shadow rollback
base shadow stop
```

Shadow mode runs a candidate beside the live matcher on every prompt and file touch: a different matcher, or pending proposals applied in memory. Only the live matcher's pick is served, so your sessions see no change. `base shadow report` shows where the two differ, and whether the corrections that followed favour the candidate.

A candidate promotes itself at session start once it has run on at least 200 prompts and its wins are at least three times its losses. After a promotion base watches the next 100 prompts and rolls back if corrections rise clearly. Both are announced once, with the command that undoes them. `[shadow] auto_promote = false` leaves promotion to `base shadow promote`. `base rule update <rule> --protected` marks a rule so that a candidate which loses it is never promoted automatically. Nothing runs until you start a shadow.

## Seeing what it read

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
base log corrections
base log corrections --session 3e82c1d0
base log corrections --transcript ~/.claude/projects/my-project/3e82c1d0.jsonl
```

Every flag is a row in the match log. `base log corrections` prints this session's, `--session` another's, and `--transcript` runs the same checks over a whole transcript, turn by turn. `[corrections] enabled = false` turns all of it off.

## Next

<CardGroup cols={2}>
  <Card title="Rules and domains" icon="gavel" href="/guides/rules-and-domains">
    Writing a rule that reaches Claude, and its test prompts.
  </Card>

  <Card title="Context reference" icon="sliders" href="/reference/context">
    Every rule, log, tune and shadow command, with its flags.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.