Skip to content

Configuration

Configuration

jjj uses a TOML configuration file stored at .jj/jjj-meta/config.toml within the repository.

Config File Format

# Project name
name = "my-project"
# Default reviewers for all solutions
default_reviewers = ["alice", "bob"]
# Custom key-value settings
[settings]
key = "value"
[github]
enabled = true
repo = "owner/my-project"
auto_push = false
sync_critiques = true
sync_lgtm = true
auto_close_on_solve = true
problem_label = "jjj"
[github.label_priority]
"P0" = "critical"
"P1" = "high"
"P2" = "medium"
"P3" = "low"

Fields

FieldTypeDescription
namestringProject name
default_reviewersstring[]Reviewers added to all new solutions
settingsmapArbitrary key-value pairs for custom settings

Deprecated: review.default_required has been removed. Review is now per-solution: a solution requires review when it has assigned reviewers. Use --reviewer on jjj solution new to assign reviewers at creation, or create review critiques with jjj critique new "solution" "Review requested" --reviewer @name. Solutions without assigned reviewers do not require sign-offs.

[github] Section

Controls GitHub integration. All fields are optional and have sensible defaults.

FieldTypeDefaultDescription
enabledbool | nullauto-detectEnable GitHub integration. When omitted, jjj checks for the gh CLI. Set to false to disable explicitly.
repostring | nullauto-detectOverride the repository as "owner/repo". Useful when jjj cannot infer the repo from the remote.
auto_pushboolfalseAutomatically create issues/PRs on local operations (e.g., problem new, solution new).
sync_critiquesbooltrueImport “Request Changes” PR reviews as critiques when syncing.
sync_lgtmbooltrueImport “Approve” PR reviews (LGTM) when syncing.
auto_close_on_solveboolfalseClose the linked GitHub issue automatically when jjj problem solve runs.
problem_labelstring"jjj"Label applied to GitHub issues created for problems.
label_prioritymap{}Map custom GitHub label names to jjj priorities. See below.

[github.label_priority]

Standard priority labels (p0, critical, high, p1, medium, p2, low, p3) are recognized automatically. Use label_priority only when your team uses non-standard label naming conventions.

[github.label_priority]
"P0 — Critical" = "critical"
"P1 — High" = "high"
"P2 — Medium" = "medium"
"P3 — Low" = "low"

When jjj imports an issue with one of these labels, it maps the label to the corresponding jjj priority.

Automation Rules

Automation rules let you trigger actions when jjj events occur. Rules are defined in .jj/jjj-meta/automation.toml using the [[automation]] array-of-tables syntax.

Why a separate file. automation.toml is machine-local: push never copies it and fetch never writes it. config.toml, by contrast, syncs through the shared jjj bookmark — a action = "shell" rule placed there would execute whatever a collaborator pushed, on every clone. Rules found in config.toml are reported and ignored; run jjj automation migrate --force to move them.

[[automation]]
on = "solution_submitted"
action = "github_pr"
[[automation]]
on = "problem_created"
action = "github_issue"
[[automation]]
on = "solution_approved"
action = "shell"
command = "echo 'Solution {{title}} approved by {{user}}'"
[[automation]]
on = "solution_withdrawn"
action = "shell"
command = "notify-team --msg '{{title}} was withdrawn'"
enabled = false

Fields

FieldTypeRequiredDescription
onstringyesEvent type that triggers this rule
actionstringyesAction to execute (shell or a built-in action)
commandstringfor shellShell command to run (supports template variables)
enabledboolnoWhether the rule is active (default: true)

Event Types

EventFires when
problem_createdA new problem is created
problem_solvedA problem is marked solved
problem_dissolvedA problem is dissolved
problem_reopenedA solved/dissolved problem is reopened
solution_createdA new solution is proposed
solution_submittedA solution is submitted for review
solution_approvedA solution is approved
solution_withdrawnA solution is withdrawn
critique_raisedA new critique is raised
critique_addressedA critique is addressed
critique_dismissedA critique is dismissed
critique_validatedA critique is validated
critique_repliedA reply is added to a critique
milestone_createdA new milestone is created
milestone_completedA milestone is completed

Built-in Actions

ActionDescription
github_issueCreate a GitHub issue for the problem
github_prCreate or update a GitHub PR for the solution
github_mergeSquash-merge the linked GitHub PR
github_closeClose the linked GitHub issue
github_syncSync state with GitHub
shellRun a custom shell command

Template Variables

Shell commands support {{var}} template expansion:

VariableDescription
{{id}}Entity UUID
{{title}}Entity title
{{user}}Current user
{{problem.title}}Title of the linked problem
{{pr_number}}GitHub PR number (if linked)

VS Code Extension

The VS Code extension uses the jjj.path setting to locate the jjj binary:

{
"jjj.path": "/usr/local/bin/jjj"
}

If not set, the extension looks for jjj on PATH.

Environment Variables

Identity and sync

VariableDescription
JJJ_USERWho the actor is. Takes precedence over the jj user name, so every event and claim is attributable. Set it per agent in a fleet; a blank or whitespace-only value is ignored rather than honoured.
JJJ_PODNames this clone’s push bookmark and event shard (events/{user}.jsonl). Setting only JJJ_POD still yields a stable identity. It does not override last_synced_rev, which always comes from .sync_state.json.

Diagnostics and measurement

VariableDescription
JJJ_DEBUGWhen set to anything, echoes every jj and gh subprocess invocation to stderr before running it.
JJJ_TELEMETRY1 appends one JSON line per command ({ts, command, subcommand, ms}) to .jj/jjj-meta/.telemetry.jsonl. Opt-in; unset, the cost is a single getenv. It cannot see invocations clap rejects, because timing starts after argument parsing.
JJJ_NO_DBMakes the store behave as if .jj/jjj.db had never been built, so reads fall through to the canonical markdown walk. A read-path measurement switch, not a supported way to run: commands that open the database directly and have no filesystem fallback — db rebuild, search, and the --search flags — are unaffected and still require the file, and a write taken with it set skips the cache upsert, leaving an existing cache stale.

Embeddings

These override the matching key in [embeddings] of .jj/jjj.toml (see Local configuration). Booleans accept true or 1.

VariableDescription
JJJ_EMBEDDINGS_ENABLEDTurn embeddings on. When explicitly on, a connection failure warns instead of passing silently.
JJJ_EMBEDDINGS_MODELModel name label, e.g. all-MiniLM-L6-v2.
JJJ_EMBEDDINGS_MODEL_PATHDirectory holding the local model files (config.json, tokenizer.json, model.safetensors). Default: ~/.cache/jjj/models/all-MiniLM-L6-v2.
JJJ_EMBEDDINGS_DIMENSIONSEmbedding dimensions. A value that does not parse as a number is ignored.
JJJ_EMBEDDINGS_API_KEYAPI key for a remote embedding service. Prefer this to the config file — a key in .jj/jjj.toml is stored in plaintext and jjj warns about it on every load.
JJJ_EMBEDDINGS_BASE_URLBase URL for a remote embedding API. Unused by the in-process semantic backend; kept so older config files still parse.
JJJ_EMBEDDINGS_DUPLICATE_CHECKCheck for near-duplicates when creating an entity. Default: on when embeddings are enabled.
JJJ_EMBEDDINGS_DUPLICATE_THRESHOLDSimilarity above which a duplicate is reported. Default: 0.85.

Standard variables jjj honours

EDITOR and VISUAL choose the editor for -E / problem edit --editor; XDG_CONFIG_HOME and HOME locate the user-level config; PATH is how the jj and gh binaries are found.

Local configuration

.jj/jjj.toml holds machine-local settings and is never synced — unlike .jj/jjj-meta/config.toml, which travels through the shared jjj bookmark.

[embeddings]
enabled = true
model = "all-MiniLM-L6-v2"
model_path = "~/.cache/jjj/models/all-MiniLM-L6-v2"
dimensions = 384
duplicate_check_enabled = true
duplicate_threshold = 0.85

Environment variables override these per-key. Keys absent from the file fall back to their defaults; a missing file is equivalent to an empty one.

Storage Location

Metadata lives in .jj/jjj-meta/ on an orphaned jjj bookmark. The working copy is never touched by jjj operations.

Synced — published by jjj push, fetched by jjj fetch:

.jj/jjj-meta/
config.toml # Project configuration (shared)
problems/ # Problem markdown files
solutions/ # Solution markdown files
critiques/ # Critique markdown files
findings/ # Finding markdown files
milestones/ # Milestone markdown files
rankings/ # rankings/{milestone_id}/{user}.json — per-user ordering
events.jsonl # Append-only event log
events/{user}.jsonl # Per-pod event shards, union-merged on fetch

Machine-local — deliberately not synced, and safe to delete:

.jj/jjj-meta/
automation.toml # Executable rules. Local by design: a rule in the
# synced config.toml would let any collaborator run
# shell commands on every clone.
.sync_state.json # This clone's last_synced_rev and pod name
.events_offsets.json # Offset index making a shard tail an O(delta) read
.lamport # This clone's logical clock
.telemetry.jsonl # Only written when JJJ_TELEMETRY=1
.jj/
jjj.toml # Local configuration (above)
jjj.db # SQLite cache. Disposable: rebuild with `jjj db
# rebuild`. Markdown is the source of truth, and
# `jjj push` validates the markdown without it.

To sync metadata with collaborators:

Terminal window
jj git push -b jjj # Push metadata bookmark
jj git fetch # Fetch collaborator changes