Documentation v0.3.2
Earshot / Core concepts

Why Earshot listens

Twelve behaviors with mechanisms, boundaries, and tests.

Reviewed for 0.3.2 · Latest documentation

The problem#

Terminal coding agents fail in a recognisable way. Not by writing bad code — by not listening:

  • You ask for a fix; it refactors three neighbouring files.
  • Your request was ambiguous; it guesses, confidently, and is wrong.
  • You say "use bun, not npm"; four turns later it runs npm install.
  • It says "done"; the tests it never ran are failing.
  • It goes down a wrong path and you can only watch, or kill it and re-explain.

Each is a behaviour, so each gets a specification and a test rather than a paragraph of prompt text and hope.

The twelve behaviours#

1. Ask before guessing#

When two readings of a request lead to materially different work, ask first — 2–4 concrete options, not an open question. Implemented as an ask_user tool plus a system-prompt policy. curiosity: low | normal | high moves the threshold — --curiosity, or curiosity in settings. It moves that one threshold and nothing else: no level turns asking off, because a question the agent cannot ask becomes a guess you pay for, and no level makes asking free, because a prompt for something with an obvious default is its own failure.

The failure this prevents: twenty minutes of confident work in the wrong direction.

2. Scope contract#

Before the first mutating tool call, the agent states its scope in one paragraph: which files, which behaviours. Editing a file outside that scope triggers a confirmation prompt.

Unrequested refactors, renames, new dependencies and formatting sweeps are blocked by default — by policy text and by a guard, because policy text alone is not enforcement.

The guard is deliberately not a line count. A threshold either fires on every large change the user actually asked for or never fires on the small wrong ones, so what it checks is categorical: a file outside the declared list, a dependency manifest or install command, a rename or delete, a rewrite that changes no line's content, a removed test. Size is only a backstop, floored so that a small change is never over budget whatever the estimate said. A turn that declared no scope is not guarded at all — a prompt on every one-line fix is the prompt fatigue this document lists as an anti-goal.

3. A plan you can edit#

/plan produces a plan file you edit in $EDITOR or inline before approving. The approved plan is pinned into context for the run. A plan you can only accept or reject is a prompt, not a plan.

4. Steer without stopping#

Typing mid-turn queues a message injected at the next model call — you redirect without cancelling. Esc interrupts. /rewind returns to any node in the session tree. /undo reverts the last tool batch's file changes from a per-batch snapshot held in a shadow git object store — no commits in your repository.

5. Preference memory with provenance#

Corrections ("don't add comments", "use bun") are detected on prompt submit and offered as one-keystroke memory candidates, scoped to the project or globally.

Each memory records why and when. When one is applied, the agent says so: "applying your rule: no semicolons". Memory you can't inspect is memory you can't trust; /memory lists, edits and deletes.

6. Intent line#

Every tool batch is preceded by a one-line "why". Enough to catch a wrong turn after one line of output instead of forty.

7. Honest completion#

A built-in Stop self-check: before finishing, compare the original request against what actually changed, and state anything skipped, unverified or failing.

No silent narrowing of scope. If four of five items are done, that is the report — not "done".

8. Verification on by default#

After edits, run the project's test or lint command — declared in AGENTS.md, or detected from package.json scripts or a Makefile — and report the output verbatim. Not a summary of the output. The output.

9. Model-agnostic UX#

Identical behaviour and transcript across providers. /model switches mid-session while keeping context; thinking blocks convert to text for providers that can't consume another vendor's reasoning.

10. Cost and context transparency#

The status line always shows context percentage and session cost. --max-cost (or maxCostUsd in settings) sets a session ceiling, checked before each model call rather than after — stopping once the money is gone is not a budget, it is a receipt. Reaching it prompts to stop, raise the ceiling, or remove it; a headless run stops and exits 3, because it has no terminal to ask. Cost is computed from catalog pricing including cache reads and writes.

11. Trust levels, not nagging#

"Always for this project" persists. Permission prompts show the actual diff or the actual command — never a summary of it, because a summary is exactly where a bad edit hides.

12. Session tree#

/fork branches an experiment, /tree navigates, all in one JSONL file. Trying something risky shouldn't cost you the conversation that got you there.

How these are enforced#

Prompt text alone doesn't produce behaviour; every item above lands as a mechanism:

BehaviourMechanism
Ask before guessingask_user tool + policy
Scope contractdeclare_scope, a categorical out-of-scope check, a size backstop
SteeringMessage queue drained at the next model call
UndoPer-batch shadow git snapshots
MemoryFiles with frontmatter, index always in context
Honest completionEnd-of-turn self-check appended to the turn
VerificationDetected test command, output reported verbatim
ContextResult caps, stubs for older results, compaction at 80%
TrustPermission rules persisted per scope, deny-first

Each gets tests. "The agent asks instead of guessing" is a testable property given a fixed scenario, and that's how it will be treated.

Anti-goals#

  • Not a personality. Listening is not warmth, enthusiasm or apology.
  • Not more confirmation prompts. Prompt fatigue trains people to hit y. The aim is fewer, better interruptions: ask once, at the moment it matters, and remember the answer.
  • Not autonomy for its own sake. A longer unattended run is not the goal if it means a longer wrong run.
Something unclear?Suggest a correction on GitHub ↗