Files
fathom/fathom.ini.example
zzg 8a775b71fb Add fathom insights and a Stop hook, so findings come to you
A finding you have to remember to go and look at is a finding you do not see.
This closes the loop: `fathom insights` reports findings for other programs,
a Claude Code Stop hook runs it when a turn ends, and /insights shows the
detail on request.

The CLI reports as text, --json, or --jsonl, and --new returns only what has
not been reported before. The server owns the reported-findings state, because
two sessions can end at the same moment and a read-then-write would have both
report the same thing.

Two rules follow from running on every turn:

- It must be fast. --hook asks a running server for an analysis already
  computed rather than doing one, and never falls back to analysing directly,
  which could take seconds. Measured at ~20ms whether or not a server is up.
- It must never disrupt the session it observes. Every path exits 0 and prints
  at most one line. A Stop hook exiting 2 tells Claude Code to keep going
  rather than stop, so a crash here would hijack the session it was supposed to
  quietly watch.

Surfacing is a higher bar than appearing in the tab, and biased towards
silence. A tab is consulted; a notification arrives whether or not you wanted
it, and the costs are asymmetric -- a missed finding waits patiently, a wrong
nudge trains you to ignore all of them. Medium severity and $0.50 minimum, not
already reported unless it has recurred since, and at most one nudge every two
hours.

A local model, if one is running, can veto a finding as not worth interrupting
for. It can never promote one that did not clear the bar, is never consulted
about findings below it, and a model failure keeps the finding rather than
silencing it. Findings it rejects are recorded, so it is not asked the same
question every turn.

Also fixes a latent hazard this would have made routine. OpenShard runs tail
recovery, which truncates, so a CLI opening a store the server is mid-append to
could cut off the chunk being written -- the reader destroying the writer's
data. Added a read-only open that skips recovery and opens O_RDONLY, and
pointed every read-only command at it, including `fathom query`, which had the
same problem already.

The nudge's cost figure counted each finding separately, so a set of them could
claim more money than the window ever spent: $12.52 against a window costing
$7.94. Now counted once per trace.

claude/ ships the skill and the hook snippet with an installer. The snippet is
printed rather than applied, because a script should not blindly edit a
settings file that may already have hooks in it, and a mangled settings.json
silently disables everything in it.
2026-09-16 00:26:11 -07:00

88 lines
3.4 KiB
Plaintext

# Fathom configuration.
#
# Every setting here has a working default, so this file is optional — `fathom
# serve` with no config runs on :3000 against ./data. Copy this to fathom.ini
# to change anything.
#
# Environment variables override the file; command-line flags override both.
[server]
# Default :3000, matching Gitea's, so the mental model transfers.
addr = :3000
[storage]
# Everything Fathom owns lives under this one directory. Stop the process, tar
# the directory, and you have moved the installation.
# Override with FATHOM_DATA_DIR.
dir = ./data
# Spans are buffered and written as one chunk when either threshold trips,
# so ingest costs one fsync per flush rather than one per span.
flush_rows = 1000
flush_interval = 1s
# Age-based eviction. Shards are whole days, so eviction is an unlink.
# Zero disables it. Override with FATHOM_RETENTION_DAYS.
retention_days = 30
# Size-based eviction, oldest shard first, applied after the age policy.
# Zero (the default) disables it. Accepts KB/MB/GB/TB.
# max_bytes = 20GB
[insights]
# The insights tab: a background analyzer that looks for cost patterns in
# recent traces. Nothing else in Fathom depends on it — turning it off removes
# a tab and a goroutine and changes nothing about ingest, storage, or queries.
enabled = true
# How far back an analysis looks. Habits worth capturing are recent; a pattern
# from six weeks ago is history.
window_days = 7
# Floor on how often the analysis is recomputed. It is normally driven by
# ingest instead, a few seconds after new traces land.
interval = 2m
# Look for a model server already running on this machine — Ollama, LM Studio,
# llama.cpp — and use it if one is found.
#
# It never leaves the machine, needs no credential, and costs nothing per call.
# If nothing is listening, everything works exactly as it does without this and
# there is no warning to ignore.
#
# What it is used for, and what it is deliberately not:
#
# - Grouping prompts by meaning rather than by shared wording. Without a
# model, "diagnose the intermittent failure" and "fix the flaky test" do
# not group, because they share no vocabulary.
# - Naming a group, so a finding reads "Flaky test triage" rather than "You
# asked for much the same thing 10 times".
# - It is NEVER asked to produce a number or to decide what counts as a
# finding. Occurrence counts, costs, and the traces behind them are
# computed the same way with or without it.
#
# Set FATHOM_LOCAL_MODEL_URL to point at a specific endpoint, or OLLAMA_HOST
# if you already use it.
use_local_model = true
# The surfacing bar for `fathom insights --new` and the Stop hook is set on the
# command line rather than here, since it is a property of how you want to be
# interrupted rather than of the store:
#
# fathom insights -min-severity high -min-cost 2 -cooldown 6h -hook
[privacy]
# Claude Code stamps user.email, user.id, user.account_uuid, user.account_id,
# and organization.id onto every span and log record. Fathom drops them at
# ingest, before anything reaches disk: a single-user store gains nothing from
# them, and keeping them writes your email into every shard file you might
# later tar up or share.
#
# Set to true to retain them — useful if you run this for a team and want
# per-user attribution.
keep_identity_attrs = false
# Prompts, tool payloads, and model responses over this size are truncated.
# An agent that dumps a megabyte tool result should cost a megabyte once.
max_blob_bytes = 1MB