Skip to content
Documentation menu

Reference

The ~/.trusty-tools/<crate>/config.yaml convention

This is the cross-crate configuration standard for the trusty-tools workspace. It gives every trusty-* crate one canonical place to read and write its user-facing configuration.

Location & format

~/.trusty-tools/<crate>/config.yaml
  • <crate> is the crate's package name, e.g. trusty-mpm, trusty-search.
  • The format is YAML.
  • An absent file means "no config" → the crate uses its built-in defaults.
  • A malformed file is logged at warn (to stderr, never stdout) and the crate falls back to defaults — a bad config never aborts startup.

Examples:

  • ~/.trusty-tools/trusty-mpm/config.yaml
  • ~/.trusty-tools/trusty-search/config.yaml (when that crate adopts it)

trusty-mpm's settings (the first adopter)

~/.trusty-tools/trusty-mpm/config.yaml:

# Managed-session workspace root template. A leading `~` is expanded.
# Sessions nest under <root>/<owner>/<repo>/<session-id>/.
workspace_root_template: ~/trusty-mpm-projects
# Default supervisor auto-resume preference.
auto_resume: false
# Default model id or tier alias for launched sessions.
default_model: sonnet
# Machine-level Rust build settings (#6868). Absent → the defaults below.
build:
  # Shared CARGO_TARGET_DIR. Default: ~/.trusty-tools/cargo-target/<owner>/<repo>,
  # derived from the project's `origin` remote. A leading `~` is expanded.
  cargo_target_dir: ~/.trusty-tools/cargo-target/bobmatnyc/trusty-tools
  # CARGO_BUILD_JOBS. Default: half this host's cores, minimum 2.
  build_jobs: 8
  # Whether briefs should carry RUSTC_WRAPPER=sccache. Default false.
  sccache: false

The build: section and the rust_build_env row (#6868)

Every tm-provisioned worktree gets an empty target/, so each dispatched engineer pays a cold full-workspace build. Measured on the 16-core dev host on 2026-09-16: ~200 s cold in a fresh worktree, 103 s with CARGO_TARGET_DIR pointed at a warm shared directory from another worktree, 17 s from that path again. sccache was neutral on the same tree, because path crates build incrementally and incremental artifacts are not cacheable. Cargo's target lock serialises concurrent builds sharing the directory, which is wanted here — six concurrent cold builds crashed that host on 2026-08-08.

tm doctor's rust_build_env row reports the resolved values for any project whose detected stack includes Rust, and closes with the line a PM pastes verbatim into an engineer brief:

CARGO_TARGET_DIR=<dir> CARGO_BUILD_JOBS=<n> [RUSTC_WRAPPER=sccache] SKIP_UI_BUILD=1

RUSTC_WRAPPER=sccache appears only when build.sccache is true. The row is Ok when the directory exists and is writable, Warn when it does not exist yet, and Fail only when it exists and cannot be written. tm doctor --fix --yes creates the directory and seeds this section when no build key is present, preserving every key already there. Neither the row nor the repair ever writes ~/.cargo/config.toml — that file is machine-global for every Rust project on the host, so wiring build.rustc-wrapper there stays an operator decision, and the row warns when sccache: true sits beside an unwired config.

Resolution precedence for the workspace root is:

  1. TRUSTY_MPM_WORKSPACE_ROOT environment variable (back-compat escape hatch);
  2. workspace_root_template from this file;
  3. the built-in default ~/trusty-mpm-projects.

These settings are editable from the trusty-console Config tab (/api/console/config/mpm, backed by the config_read / config_write MCP tools), so operators never hand-edit the YAML unless they want to.

Workspace-root migration

The managed-session workspace root moved from a legacy ~/.trusty-mpm/workspaces/<project>/<session-id>/ layout to the current ~/trusty-mpm-projects/<owner>/<repo>/<session-id>/ layout. This was a migration, not a hard cutover: trusty_mpm::core::workspace_scan discovers sessions under both roots, so pre-migration workspaces are never silently orphaned. The authoritative session state remains ~/.trusty-mpm/session-manager/sessions.json; the dual-root scan is the migration-aware filesystem discovery layer.

The project-level .trusty-mpm.toml (#5207)

Every surface above is per-host. A project's own conventions — "this repo launches on main, never in a worktree" — had to be re-declared by every operator on every machine, and could never be reviewed or versioned. Since #5207 trusty-mpm also reads one file from the project itself:

<project>/.trusty-mpm.toml
# Do this repo's managed sessions get a per-session git worktree?
worktree = false
# Does an agent DISPATCHED in this repo get a worktree of its own? (#5814)
agent_worktree = false
# Default model id or tier alias for sessions launched in this project.
default_model = opus
# Does every prompt composed for this project ask for a `## Prompt feedback`
# addendum? (#7688)
prompt_self_improvement = true

This file is committed. It is tracked in git, travels with clones, and shows up in PR diffs. That is the point of it, and it is why the file sits at the project ROOT rather than inside <project>/.trusty-mpm/: that directory holds machine-local session state, so projects gitignore it wholesale (this repository ignores .trusty-mpm/*, which already makes the #4832 framework/manifest.toml layer untrackable here). A file that must be committed cannot live in a directory that exists to hold uncommittable state.

Where a setting appears here, this file is the top of its precedence chain:

SettingPrecedence, highest first
worktree.trusty-mpm.toml → the projects.json registry → built-in true
agent_worktree.trusty-mpm.toml → built-in true
default_model.trusty-mpm.tomlconfig.yaml's default_modelconfig.toml's [models] default → built-in sonnet
prompt_self_improvement.trusty-mpm.tomlconfig.toml's [pm] prompt_self_improvement → built-in false

worktree and agent_worktree answer different questions and neither implies the other. worktree decides where a managed SESSION is placed; agent_worktree decides whether an agent DISPATCHED from a main checkout is given a worktree of its own under ADR-0048 decision 1. agent_worktree reads no registry layer at all — a machine-global record must not decide a project's dispatch workflow. Setting agent_worktree = false suits a repo with no concurrent writers and no build state (a writing or documentation repo): its agents edit in place, commit on the checked-out branch, and push. It exempts the worktree grant only. The ADR-0044 main-checkout write boundary still denies a source-file edit there, and a second concurrent writer in the same checkout is still refused.

default_model is a default: an explicit --model, a per-agent [models.agents] entry, and an agent's own frontmatter are all more specific and still win over it.

prompt_self_improvement (#7688) asks the model what was wrong with the prompt it received. On, the composed PM prompt gains a section requesting a ## Prompt feedback addendum of at most 5 lines — what was unclear, what was unnecessary — and telling the PM to append the same request to every dispatch brief it sends, which is how a dispatched agent is asked. tm hook --prompt-feedback is registered on Stop and SubagentStop only where the flag is on; it extracts that section into ~/.trusty-mpm/prompt-feedback.jsonl, which tm prompt-feedback reads back. Off — the default — nothing is injected and nothing is registered. It belongs on this committed surface because whether a repository wants its prompts critiqued is a property of the repository: a high-churn harness repo is where the signal pays, and a stable consumer project is where five extra lines per response are pure cost. Nothing is written into the deployed agent files, which are one machine-global set shared by every project on the host, so two projects disagreeing about the flag never rewrite each other's copies.

Two settings are deliberately not here. workspace_root decides where a project gets cloned, so it cannot be read from a project that does not exist yet — it stays host-level. auto_resume is a property of the operator's supervisor rather than of the repository.

Unknown keys

This file is parsed with serde(deny_unknown_fields). worktre = false is an error, not a silently-ignored key. At spawn time a rejected file contributes nothing at all — resolution falls through to the next layer and the parse error is logged at error level naming the offending key. Nothing in a file that failed to parse is trusted, because a typo means the author's intent is unknown rather than partially known, and because a committed file is shared: one bad push must not brick every operator's session launches.

The two host config files are not strict, on purpose. Both answer a parse failure by returning defaults, so denying unknown fields there would upgrade "one key is ignored" into "the entire file is ignored" — a worse failure than the one it fixes. They keep the lenient parse and instead log a warning naming every key they dropped.

Relationship to the legacy ~/.trusty-mpm/config.toml

trusty-mpm's older ~/.trusty-mpm/config.toml (agent sources, per-agent model overrides, PM toggles) is unchanged and still read. The YAML convention above is additive — it carries its own settings without disturbing the existing TOML.

Adding this convention to a crate

Crate maintainers implementing this convention in a new trusty-* crate should see config-convention-internal.md for the shared trusty-common helper and the serde_yaml dependency notes.