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.
88 lines
3.4 KiB
Plaintext
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
|