Configuration and local state
Downshift by Tiago Vilas Boas. Updated . Source on GitHub
Downshift stores local data under a single state directory. No cloud sync.
State directory resolution
| Priority | Location |
|---|---|
| 1 | DOWNSHIFT_STATE_DIR (absolute path) |
| 2 | ~/.downshift/ when it exists |
| 3 | ~/.harness-downshift/ when it exists and ~/.downshift does not |
| 4 | ~/.downshift/ (default for new installs) |
Check the effective path:
downshift doctor
Effective settings: downshift config
downshift config [--json] prints every effective setting with its value and where it comes from. It is read-only: it reads what the hooks read and writes nothing.
| Source | Meaning |
|---|---|
env |
An environment variable set for the process (named in env in the JSON) |
file |
A file under the state dir, or one an environment variable points at |
default |
Nothing configured: the built-in behaviour |
legacy |
state_dir only: the pre-rename ~/.harness-downshift |
Settings shown: state_dir; session_models per harness (native export, discovery cache, session-models.json, inferred from the current model, or none); quota_mode; scores on/off and score_routing (shadow or live); control_share; quiet; catalog (embedded or user override); semantic_boost and semantic_embedder; infer_session. The session lists reflect the files on this machine; a list in a hook payload overrides them at spawn time.
Files under the state dir
| File | Purpose |
|---|---|
events.jsonl |
Routing telemetry (no prompt text) |
agents.jsonl |
Optional spawn log (dsmon-hook) |
catalog.json |
User catalog override (downshift models pull) |
openrouter.json |
Optional OpenRouter shadow list; records a would-have-used slug and tier, never routes to a gateway model |
session-models.json |
Per-harness session allowlists |
loop-events.jsonl |
Routing v2 training events |
weights.json |
Classifier weight override |
scores.json |
Public capability score cache (sources, selected source, rows); see scores.md |
Override paths:
DOWNSHIFT_EVENT_LOG— telemetry JSONL (advanced)DOWNSHIFT_SESSION_MODELS— session allowlist fileDOWNSHIFT_INFER_SESSION=off— disable the inferred session (Claude Code); an unknown session then never rewritesDOWNSHIFT_SCORES=off— no network for public capability scores: no hook, install or doctor refresh, and the router ignoresscores.json. Details: scores.mdDOWNSHIFT_SCORE_ROUTING=1— let the public-score pick change the routed model (default: shadow only, recorded in telemetry asscore_would). Details: scores.md
Every variable the routing core reads, with its default, is listed in one place: the doc comment of internal/core/env.go. The ones not covered above:
DOWNSHIFT_QUOTA_MODE=required— the quota gate needs fresh evidence and keeps abandoned snapshots holding (quota.md)DOWNSHIFT_EXPLICIT_UPSHIFT=1— an upshift may pickexplicit_onlyids (session-models.md)DOWNSHIFT_DISCOVERY=off,DOWNSHIFT_DISCOVERED=<path>,DOWNSHIFT_DISCOVERY_TTL=<duration>— the discovered-models layer, its cache and its freshness (default 24h) (session-discovery.md)DOWNSHIFT_NB_UPSHIFT=1,DOWNSHIFT_NB_DOWNSHIFT=1— apply the naive-Bayes second opinion instead of recording it (default: shadow; R1 still holds an unconfident downshift)DOWNSHIFT_CURSOR_NATIVE_FILE=<path>— Cursor's native session export (each harness that has one declares it inHarnessCapabilities.NativeSessionEnv)
Outside the core: DOWNSHIFT_NO_ROUTE=1 (control baseline), DOWNSHIFT_CONTROL_SHARE (built-in control group), DOWNSHIFT_QUIET, DOWNSHIFT_MINILM (hash semantic boost; 0 opts out), DOWNSHIFT_LOG_MAX_MB / DOWNSHIFT_LOG_BACKUPS. downshift config prints the effective value and source of most of them. Graphify, DOWNSHIFT_MINILM_EMBED, and DOWNSHIFT_E5_* were removed on 2026-10-17 and are ignored. DOWNSHIFT_EXPLICIT_UPSHIFT, DOWNSHIFT_DISCOVERY, DOWNSHIFT_DISCOVERY_TTL and the two NB_* switches are not reported by config yet.
OpenRouter allowlist (optional)
OpenRouter gateway models use full vendor/model ids. They are never chosen automatically. Listing a row in openrouter.json under the state directory enables shadow recording only; the live route, rewrite and model written to a spawn remain unchanged.
For each decision with a listed row for its tier, Downshift records gateway_would (the slug) and gateway_would_tier (the decision tier). It stores no prompt, task text, path, token count or dollar amount for the row. downshift stats --detail prints a shadow line with the would-route count and most frequent slug when at least one decision would have used a listed row.
{
"version": 1,
"harness": "opencode",
"models": [
{"id": "deepseek/deepseek-v4.1-flash", "tier": "mid", "max_effort": "medium", "zdr": true},
{"id": "x-ai/grok-x", "tier": "frontier", "max_effort": "high", "zdr": false}
]
}
A listed row needs a full vendor/model slug and a small, mid or frontier tier. Remove openrouter.json to stop recording the shadow choice.
Project tier floor (optional)
A project can require a minimum tier with .downshift/policy.json in the project directory or any parent (nearest file wins, at most 8 levels up). A floor only raises a tier; it never lowers one. Applied once, in the shared core, so every adapter inherits it; each adapter only reports where its payload keeps the working directory.
{
"version": 1,
"min_tier": "small",
"min_tier_by_class": { "COMPLEX": "frontier" },
"min_tier_by_risk": { "payment": "frontier" }
}
Tiers:
small,mid,frontier. Classes:TRIVIAL,SIMPLE,MEDIUM,COMPLEX.Risk categories are the names in
internal/core/risk.go(for examplesecret,payment,destructive).The highest applicable rule wins.
Fail-open: a missing, oversized, symlinked or invalid file (unknown field, tier, class or category) is ignored and the spawn routes as if there were no file. The floor still goes through the session allowlist and quota gates, so it cannot select a model the session does not offer.
The hook runner takes the project directory from the payload, and falls back to the hook process's working directory when the payload has none. Offline commands such as
benchmarkandtryhave no project directory and never read a policy file. Where each harness keeps it (evidence level, 2026-10-11):Harness Payload field Evidence Claude Code cwdDocumented by the vendor Cursor cwd, else firstworkspace_rootsThird-party and forum reports only; cwdis reported empty in placesCodex cwdThird-party guides only Antigravity cwd, else firstworkspacePathsIDE docs for workspacePaths; CLIcwdfrom a third-party guideKiroCrew cwdSearch summary of the vendor page; the Crew hook payload has no captured sample Only Claude Code is confirmed. For the others, capture a real payload under
docs/evidence/before relying on the floor; if the field is absent the floor is looked up from the process directory, so run the hook from the project directory.A floor changes nothing when the current model already sits at that tier. Where a harness does not honor model rewrites (see the harness matrix), the floor stops a downshift from being attempted but cannot force the model.
Migrating from ~/.harness-downshift
mkdir -p ~/.downshift
cp -a ~/.harness-downshift/. ~/.downshift/
downshift doctor # should show state_dir under ~/.downshift
Or set export DOWNSHIFT_STATE_DIR=$HOME/.harness-downshift until you copy data.
More docs
- Install Downshift: Claude Code, Codex and Cursor hook setupGuide
- Downshift and Claude Code subagents: when routing can actGuide
- Downshift session view: subagent tree, reasons and pauseGuide
- Downshift report: routed versus control tokens per completed taskGuide
- Downshift evidence ladder: what each saving figure meansGuide
- Downshift companions: Headroom and Claude Code mods to pairGuide
- Downshift capability matrix: status and evidence per featureReference
- Downshift capability matrix, part 2: context and packagingReference
- Downshift harness matrix: Claude Code, Codex, Cursor statusReference
- Downshift value per harness: observed tokens vs list priceReference
- Downshift spawn state: switched, held or only observedReference
- Downshift offline simulation: routing without model callsGuide
- Downshift session models: which model IDs a hook may writeReference
- Downshift complexity weights: how a task becomes a modelReference
- Downshift architecture: deterministic hook and offline CLIDesign
- Downshift routed vs control: first Claude Code A/B resultsEvidence
- Downshift real usage: a Claude Code day and the last 30 daysEvidence
- Downshift Code Intelligence MVP: evidence for Claude CodeEvidence