Suggestions and What goes wrong¶
Interlatch looks through your recorded sessions for failures that keep coming back: a screenshot path Playwright
refuses, an edit that fails because the file changed, a zsh glob that matched nothing. What goes wrong lists
those causes, with noise kept apart. Suggestions is one queue of proposed fixes: lines for your CLAUDE.md or
AGENTS.md, a change to Claude Code's MCP config, and setup steps for you to run. Knowledge that sessions confirmed
often enough is proposed as an instruction line too.
Nothing is written until you approve a suggestion, and the file is backed up first. No model is called: both are built from your archive by fixed rules, in a few seconds.
What goes wrong¶
What it reads: the tool calls that failed in your sessions (a row stored twice, as happens when a session is
resumed, counts once), and the friction notes that session analysis wrote about each session, in English or
Japanese. One note can name several causes. Sessions recovered from history.jsonl have neither, and are left out.
Each failure is matched against a catalog of known causes, each with its fixes:
| Cause | What happens | Fixes proposed |
|---|---|---|
edit-stale-context |
An edit fails because the file changed since it was read, or the expected lines are not there | Instruction: re-read the exact lines right before every edit |
playwright-output-roots |
Playwright MCP refuses a screenshot path outside its allowed roots, or blocks a file:// page |
Config: --isolated and an output directory; instruction: save screenshots by bare filename, preview over http://localhost |
playwright-browser-in-use |
"Browser is already in use": another session holds Playwright's shared browser | Config: --isolated; instruction: stop instead of retrying |
playwright-stale-refs |
Playwright actions on refs from an old snapshot, or on ambiguous selectors | Instruction: take a fresh snapshot before using refs |
zsh-nomatch |
zsh stops a whole command on an unquoted glob that matches nothing | Setup step: setopt NO_NOMATCH; instruction: quote every glob |
zsh-equals |
zsh expands a word starting with = (echo ==== fails) |
Setup step: unsetopt EQUALS; instruction: quote separator lines |
zsh-dialect |
Bash habits that zsh treats differently ($status is read-only, no word splitting) |
Instruction: write zsh, not bash |
sleep-polling-blocked |
Claude Code blocks sleep N; cmd used to wait |
Instruction: wait with run_in_background or Monitor |
timeout-missing |
timeout is not installed (macOS), so wrapped commands exit 127 |
Setup step: brew install coreutils; instruction: use the Bash tool's timeout instead |
bare-python-modules |
Throwaway Python on the system interpreter: missing modules, no python |
Instruction: run it through uv run --with |
interactive-aliases |
cp -i, rm -i aliases hang commands or skip overwrites |
Setup step: keep the aliases out of agent shells; instruction: use command cp -f |
stale-dev-server |
A port is already taken, or an old server answers a live check | Instruction: check who owns the port first |
cwd-drift |
A relative cd sub && … runs from the wrong directory |
Instruction: use absolute paths |
auto-mode-retry |
An action the auto-mode classifier denied is tried again | Instruction: stop and ask |
bash-timeout |
A long command hits the shell tool's time limit | Instruction: run it in the background |
concurrent-sessions |
An edit, port or browser conflict while another session was active in the same project (within 10 minutes) | Instruction, in that project's file: assume other sessions share this tree |
Noise is kept apart and never gets a fix: tests, linters and type checks failing inside a dev loop; read-only
command chains (cat, grep, ls…) whose last command exited non-zero; provider and platform failures
(overloaded, connection lost, usage limits, expired login); and tool calls you turned down at the permission
prompt. A failure that matches a real cause is never counted as noise.
Friction notes that match no known cause but recur in 3 or more sessions are listed as other, as context, with no fix.
For each cause you see how many sessions and projects it hit, how many times it happened, how often the same failure was retried right away, the share of sessions in those projects it affected, first and last seen, whether it is still happening (seen in the last 14 days), its sessions per week over the last 12 weeks, and example sessions. Below the causes, the 15 tools with the most failed calls, with their failure rate.
In the dashboard: the What goes wrong page (from the Suggestions sidebar, or ⌘K). Pick 30 days, 90 days (the default), 180 days or all, and a project. Click a cause for its examples, agents, projects and fixes; N to review opens its suggestions. The Noise card says how many failures were expected; Show lists them.
On the command line: interlatch friction [-p project] [--days N] [--json] [--noise]. It covers all time unless
you give --days, and prints one line on how much noise was left out; --noise lists it.
Where suggestions come from¶
From What goes wrong. A cause gets suggestions when it is still happening and has hit 3 or more sessions or 2
or more projects. Its instruction lines go to your user-level files when it hit 3 or more projects, otherwise to the
project where it happened most. concurrent-sessions always goes to the project files, for up to 5 projects where
it hit 2 or more sessions. Lines are proposed only for the agents that ran into the cause: Claude Code's file, Codex's
file, or both. A cause you moved goes where you put it.
From knowledge. An item becomes a suggestion when it is:
- active, extracted by analysis or added by you (never from Claude Code or Codex memory notes),
- a gotcha, fix, command, preference or decision,
- high confidence, or established or canonical,
- worded as a rule ("never…", "use X, not Y", "requires…"), not a changelog entry ("Fixed…", "Added…", "X now does Y"). An item seen in one session only also has to be a command, a preference, or a firm rule.
Items with near-identical titles are treated as one lesson, and the most trusted one is proposed. A lesson becomes one line in your user-level file instead of one per project when it was found in 3 or more projects, or when it is a preference the analysis marked global: how you like to work, not how the project works. Other global items (a gotcha about a tool, a command) stay in the file of the project they came from, since the user-level file is read in every session. A lesson you moved goes where you put it. A lesson the catalog above already covers is left to it. The line is the item's title in bold and the first sentence of its body, up to 240 characters. At most 8 knowledge lines are proposed per file, the best first: a higher stage, more sessions behind it and more recent use rank higher. Lines you dismissed or applied don't take one of the 8 places, and neither do lessons you moved. The rest wait, and come up as places free; they are not shown as stale.
A suggestion is not made when a line outside Interlatch's block in that file already says much the same thing.
The three kinds¶
| Kind | Shown as | What it changes | Who does it |
|---|---|---|---|
instruction |
Instruction | One line in Interlatch's block of a CLAUDE.md or AGENTS.md |
Interlatch, when you apply it |
config |
Config change | Arguments added to the Playwright MCP server in ~/.claude.json |
Interlatch, when you apply it |
environment |
Setup step | Your shell setup: a setopt line, brew install coreutils, aliases |
You. Interlatch shows the command and never runs it; you mark it done |
Where instruction lines go¶
| User level | Project level | |
|---|---|---|
| Claude Code | ~/.claude/CLAUDE.md (in $CLAUDE_CONFIG_DIR when set) |
<project>/CLAUDE.md |
| Codex | ~/.codex/AGENTS.md (in $CODEX_HOME when set) |
<project>/AGENTS.md |
| GitHub Copilot, IBM Bob, Google Antigravity | none (skipped) | <project>/AGENTS.md |
User-level files follow the first folder in [sources] claude_dirs and codex_dirs (see
Configuration), so the background job and your shell write to the same file. A project line is
only proposed for a folder that still exists, and never for your home folder or a folder above it: a CLAUDE.md
there would reach every project.
When a project's AGENTS.md refers to CLAUDE.md, the line goes to CLAUDE.md only; when its CLAUDE.md
imports @AGENTS.md, it goes to AGENTS.md only. A project file is proposed only for agents that work in that
project (3 or more sessions, or a fifth of them), and never for a project folder that no longer exists. A missing
file is created when you apply.
Interlatch's block¶
Interlatch writes only inside one block at the end of the file:
<!-- BEGIN interlatch -->
- Re-read the exact lines (Read, or `sed -n 'a,bp' file`) in the same turn before every edit … <!-- interlatch:friction:edit-stale-context -->
<!-- END interlatch -->
- The block goes after everything else in the file, including other tools' managed blocks (such as
agent-ninja-START…agent-ninja-END), never inside one. - Text outside the block is never touched. A line you type inside the block without an
interlatch:marker is kept. - Each line ends with a comment naming the suggestion, so undo removes exactly that line. Agents ignore the comment.
- Nothing else is added: no header, no "generated by" text.
- Removing the last line removes the block. If Interlatch created the file for that line, undoing it deletes the file again; a file you already had is kept.
- A file with Windows line endings (CRLF) keeps them. A symlinked file is written where the link points, and stays a link.
- A damaged block (a
BEGINwithout itsEND, or two blocks) is never guessed at: applying stops and names the file to fix by hand.
Language¶
Lines from what goes wrong are written in the language of [analysis] language (Configuration):
English, or Japanese with ja. Commands and config changes read the same in both. The marker at the end of a line
(<!-- interlatch:friction:… -->) does not change with the language, so a line already in the file is not proposed
again in the other one, and a line outside the block that says the same thing counts in either language. A waiting
line you have not edited switches language at the next refresh; an applied or edited one stays as it is. Lines from
knowledge are in the language the knowledge was written in.
The config change¶
For playwright-output-roots and playwright-browser-in-use, Interlatch proposes:
mcpServers.playwright.args += ["--isolated", "--output-dir", "/Users/you/.cache/playwright-mcp"]
It is proposed only when ~/.claude.json (or $CLAUDE_CONFIG_DIR/.claude.json, when that exists) has a server
named playwright that lacks these arguments. Applying adds only the missing ones; a flag that is already set
keeps its value. Every other key is kept, and the file is written back with two-space indentation. Once the
arguments are there, the suggestion goes stale. New Claude Code sessions pick up the change.
This is the only config change Interlatch makes. Edited text is checked too: it must name a Playwright server and
may only add --isolated and --output-dir <absolute path>. Undo removes only the arguments Interlatch added.
Reviewing and applying¶
In the dashboard: Suggestions in the rail, with a badge counting suggestions you haven't seen. Opening the page clears it. The sidebar and the switch at the top move between To review, Applied, Done, Stale and Dismissed; a menu narrows to user-level suggestions or one project. Suggestions are grouped by the file they change, with setup steps in their own group. Each card shows:
- the kind, where it came from (from what goes wrong or from knowledge), and the agent,
- the evidence: "seen in 31 sessions across 17 projects · still happening · last 2026-10-01", or for knowledge "established · confirmed in 2 sessions · agents-chronicle", with two example sessions to open,
- any warnings (below),
- the line itself, which you can edit. Your wording is saved when you leave the box and kept on later refreshes; Reset to the proposed text goes back.
Preview shows the exact change to the file as a diff, without writing it. Apply writes it. Dismiss takes it out of the queue. A setup step shows its command with Copy, and Mark done once you have run it. Check again looks at the latest sessions and knowledge now.
Moving a line¶
A waiting instruction line can go somewhere else than Interlatch chose. On a project's card, Move to every project puts it in your user-level file instead. On a user-level card, Move to project only (or Move to its projects, when it came from several) puts it back in the files of the projects it came from; for a recurring failure, up to 5 projects where it hit 2 or more sessions. Interlatch remembers the choice for that lesson or cause: every waiting card of it moves along (two project cards become one user-level card), your edited wording comes with it, and later refreshes keep it there. Lines already applied stay where they were written.
The Home page shows a Suggestions card with the top 3 waiting, when there are any, with Approve and Dismiss (setup steps get Copy command). Its title opens the full card.
On the command line:
| Command | |
|---|---|
interlatch suggest [-p project] [--all] [--json] |
List what is waiting (--all: also applied, done, stale and dismissed), with the target file, evidence and warnings |
interlatch suggest show ID |
The diff applying would make, or a setup step's command |
interlatch suggest apply ID… [--yes] |
Show each diff and ask before writing (--yes: don't ask). A setup step is refused: run it yourself |
interlatch suggest dismiss ID [--reason R] |
Never propose it again (an applied one must be undone first) |
interlatch suggest done ID |
You ran a setup step |
interlatch suggest undo ID |
Take an applied line or config change back out, or put a dismissed or done one back in the queue |
interlatch suggest move ID --to user\|project |
Move a waiting line to your user-level file, or back to the files of its projects |
interlatch suggest refresh |
Look at the latest sessions and knowledge now |
Backups: before any write, the current file is copied to ~/.interlatch/backups/<name>.<time>.bak, for
example CLAUDE.md.2026-10-02T091500.123Z.bak, or .claude.json.<time>.bak (with its leading dot, so ls -a shows it).
A file Interlatch creates has nothing to back up. The new file is written to a temporary file and moved into place,
so it is never half-written.
Warnings¶
A suggestion that should be read twice says so. Warnings don't block applying.
- Public repository: the project's
originis a public GitHub repository, so anyone can read the line once you commit it. Interlatch asksgit remote get-url origin, thengh repo viewfor GitHub remotes, and remembers the answer for a week. Withoutgh, or for other hosts, the visibility is unknown and no warning is shown. - Looks sensitive: the line contains an email address, a GUID, a private IP address, a home folder path
(
/Users/<name>/,/home/<name>/), a host name that isn't a well-known public one, or something that looks like a token or key. In the dashboard these follow your edits when you press Preview. - File not tracked by git: the file exists in a git repository but isn't committed, so the line stays on this computer.
Later refreshes¶
The queue is refreshed after every background sync (no model is called; it takes a few seconds), by Check
again, and by interlatch suggest refresh.
| Status | Meaning |
|---|---|
new (To review) |
Waiting for you. A refresh updates its evidence, but keeps text you edited |
applied |
Written. For a friction fix, the evidence counts the sessions that hit the cause since ("seen in 2 sessions since applied", or "not seen since applied") |
done |
A setup step you marked as run |
stale |
Its cause stopped happening, or the file already has it. It comes back as new if the cause returns |
dismissed |
Never proposed again, whatever later sessions show. Restore (or interlatch suggest undo ID) puts it back |
Undoing¶
- Undo on an applied card, or
interlatch suggest undo ID, takes the line back out of the block (or the arguments back out of~/.claude.json) and returns the suggestion to the queue. - To remove everything at once, delete the lines from
<!-- BEGIN interlatch -->to<!-- END interlatch -->from the file. A refresh never writes a line back, but those suggestions still show as applied until you undo or dismiss them. - Every earlier version of a changed file is in
~/.interlatch/backups/. interlatch uninstalldoes not touch instruction files or~/.claude.jsonfor suggestions; undo them first if you want them gone.
What Interlatch never does¶
- Write anything you haven't applied. A refresh only updates the queue.
- Run a setup step, or edit your shell startup files (
~/.zshrc,~/.zshenv): it shows the command, and you run it. - Write outside its block in an instruction file, or change any part of
~/.claude.jsonexcept the Playwright server'sargs. - Add attribution or "generated by" text.
- Send your sessions anywhere for this. The only outside call is
gh repo view, described below.
Settings¶
| Key | Default | |
|---|---|---|
suggestions.enabled |
true |
refresh the queue after each background sync. Off, the queue stays as it is; Check again and interlatch suggest refresh still work |
suggestions.notify |
false |
show a desktop notification ("3 new suggestions. Open the dashboard > Suggestions") when a background sync finds new ones |
Set them with interlatch config set suggestions.notify true, or under [suggestions] in config.toml
(Configuration).
Privacy¶
What goes wrong and Suggestions read only your local database and the instruction files they would change. To warn
about public repositories, Interlatch runs git remote get-url origin in the projects it proposes lines for and, for
GitHub remotes, gh repo view <owner>/<repo> --json visibility with your own gh login. That sends the repository's
name to GitHub and nothing else; the answer is kept for a week. Without gh nothing is sent. See
Data and privacy.