Skip to main content
One SQLite file holds every answer, each judgment’s latest table, the run history and logged traffic. WAL mode: batch runs and judge() callers can use it at the same time.

Where it lives

In order:
  1. The store of the target named by --target, if it sets one.
  2. HUNCH_STORE, if set.
  3. The nearest .hunch/store.sqlite in the spec’s folder or any folder above it.
  4. Otherwise a new .hunch/store.sqlite at the root of the git repository, or in the spec’s folder outside a repository.
A new store is announced on stderr:
Seeing it for a project you have run before means every answer will be asked again; set HUNCH_STORE to the existing store. Keep .hunch/ out of git: about 80 MB per 100,000 rows, and it holds your rows’ text.

The answer cache

answers is content-addressed: each answer is keyed by a hash of exactly what was asked.
  • Same row, question and model anywhere (batch, judge(), server): cache hit.
  • New key: a change to instructions, criteria, option order, model, or any state column’s text.
  • Not in the key: act, gold, escalate, tests, where, the spec’s name and source.
Answer shapes:
noul is p(yes).

Judgment tables

hunch run writes one table per judgment, named after it, replaced in one transaction; a failed run leaves the previous table. Columns: the answer columns, plus: A run whose where keeps no rows still writes the table, empty, with the same columns, so judgments downstream read zero rows. Under --model, the table name gets the engine as a suffix (intent__deepseek_deepseek_flash), leaving the spec’s own table untouched. Under a --target that changes an engine, every table of the run gets the target’s name instead (intent@dev).

Runs and lineage

  • spec_hash covers what the judgment asks, not on_change or source; on_change: freeze compares it with the last complete run’s.
  • status is complete, or failed: <error>.
  • shash: hash of the row’s state. new_rows_only reuses an answer only when row id and shash both match.
  • /v1/drift and the runs page read label mixes from _hunch_row_answers.

Traffic

judge(..., log=True) and judge(..., shadow=...) keep each row in traffic (judgment, rhash, row, n, first_at, last_at), redacted by the live spec’s rules; a repeat increments n. --traffic exports it to .hunch/traffic/<judgment>.csv and runs on that.

Querying it

Plain SQLite. The command guard’s table holds the 1,315 shell commands coding agents ran in public sessions, with whether the person refused each one. Set the guard’s verdict beside what people did:
The guard says 36 commands would destroy something, and people let every one of them run. Join a judgment’s <question>_key to answers for which engine answered and when:
From Python, hunch.results returns a judgment’s table as a list of dicts.