Projects
A project is two halves, a shared record in the tracker and a local file on this machine, with no field in both; which fields each holds, which value wins, and where runs build.
A project is two halves: a shared record in the tracker and a local file on this machine. No field is in both, so nothing is merged or overridden between them.
01two halves#
The record lives in the tracker (smith's own, or a serves tracker), not on disk. It holds checks, branch, color, repo_remote, sync_mode, root_hosts.
records/<project name>.json is the name propose_config_edit takes for the record. It is a virtual name for the tracker record, not a file: there is no records/ folder. The operator's click writes the tracker record, and the card says so.
The local half is a file: <desk dir>/projects/<tracker>/<project id>.json, keyed by tracker and project id, never by slug, and never inside a repo. It holds path, workspace, assets, main_asset, max_parallel, mcp_servers, overrides, discord, tools, areas. See assets: assets lists the project's member assets, and main_asset is the one launch-card suggestions never offer.
The projects tool with a name returns settings (both halves together), record (the tracker record as read), detected (what smith would detect from the repo), and halves (this summary).
renaming
propose_project_rename takes a project, a new name and an optional new summary, and drafts a card. Nothing changes until the operator clicks it. The click renames the record on its tracker (smith's own or a serves tracker), and the project list shows the new name without a restart.
Only the tracker's display name changes, and its summary if the card gives one. The desk's own name in the local half, the slug, id, repo path, work clone, tickets and chats stay as they are, so links and chats keep the old name.
02which wins#
Per field, since no field is in both halves.
settings.branchis the record's branch. When the record's branch is empty it falls back to the repo's detected current branch.record.branchempty withsettings.branchmainmeans not set in the record. Setting it in the record pins it.- With no record settings at all (
project.Settings == nil),settings.checksareDetectChecksoutput from the repo and are not saved until adopted. workspaceunset defaults to<work home>/<project name>.- Runs build in worktrees at
<workspace>.runs/<first 12 characters of the launch id>(RunWorktreesDirininternal/service/run_worktree.gois the checkout plus.runs). Asset runs use<workspace>.runs/asset-<12 characters>. - A launch card drafted before a settings change must be drafted again (see launch#draft-and-go).
03area brief#
An area's brief (areas[].brief in harness.json) is text the run reads as "your area is …", after the project's brief. It is capped at 2,000 characters (counted as characters, not bytes): the areas page save, propose_area_edit and the settings page refuse a longer brief, and the error names the area and its length.
04local identity#
Three keys say which project a local file belongs to (LocalHalf).
trackeris the tracker that holds the project.project_idis the project's id in that tracker.nameis the desk's name for the project, the one its conversations carry.
05areas#
areas is a list of named sets of paths (Area). Areas may overlap; an overlap is reported, never refused. For the harness.json form of an area see harness-json.
namenames the area.pathsare globs, one each.**matches any directories and a leading!leaves matching files out.briefis text for the run (see the area brief section above for the 2,000 character limit).fenceisflag,holdoroff; empty isflag. What each does: not checked here.checksis a list of checks that belong to the area. Each hasid,executable,argsandtimeout, the same shape as a project check (ProjectCheck). See checks and harness-json.
06mcp servers#
mcp_servers in the local half registers external MCP servers for this project. An entry has the same shape as one in harness.json: id, then either command with args (a stdio server smith spawns) or url (a streamable http server), plus headers, secrets, tools, mutating and access.
- A project entry replaces the global entry with the same
id. secretsmaps an env var the stdio server gets to anenv:NAMEreference; aheadersvalue is an optional literal prefix and one trailingenv:NAMEreference.toolsis the allow-list; unset exposes every tool the server lists.mutatingnames the tools thataccessset toreadhides.
The field-by-field rules are in harness-json.
07tools#
tools in the local half lists the commands this project's tool nodes run, by id. It replaces harness.json's whole list; unset (nil) inherits it. An entry is a Tool: id, command, args, timeout, env, secrets, output_schema, retries and workspace (empty is a scratch folder, read makes the run's workspace the working directory). A secrets value must be an env:NAME reference. See harness-json.
08overrides#
overrides holds only what this project changes from the global settings (Overrides). An empty map, empty string or nil means inherited. See harness-json for presets.
modelsmaps a role (intake,plan,work,review,close) to its model.presetis the preset a launch card starts on.budgetsreplace the budgets of every preset:max_output_tokensandmax_searches. A zero field is inherited.personasmaps a step (plan,work,review,close) to the role it runs as: a role name, ornonefor no persona.rolesmaps a role name to this project's own version of it, field by field (text,model,gate,assets,skills,tools). A name with no library role defines a role only this project has. A list field replaces the global list. See roles.on_fullis what the project's manager chat does at the compact point:ask,compactorfresh.
09record fields kept for old files#
gitea_token_refis unused. It stays in the record only so an old tracker answer, page save or card draft still decodes. The desk ignores it, warns that the key is no longer used, and drops it on the next save.root_hostsis a list of hosts a root card may run on. Each has aname(the name the card uses; every host needs one and names must be unique) and ansshtarget (localhostmeans this machine with no ssh; a target cannot start with-). The checks are in; the shape isRootHost.
10archive#
Archive is a flag in the local half: archived_at is when, and archived_chats lists the chats the archive took with it. It hides the project from the project list, the switcher, search, home, the discord menu and the pickers, archives its unarchived chats, and drops its discord binding live. The half keeps its discord config, so unarchive rebinds. It makes no tracker call and leaves the discord server, the repo, memory and shared assets alone, and nothing is deleted. An archived project still resolves by name, so its old chats and runs keep their label, and its settings page stays reachable.
Archive is refused (409 live_runs, naming the run ids) while a run is live in the project's work clone or on one of its chats. Unarchive restores exactly the chats in archived_chats; a chat archived by hand stays archived. Both are the settings page's danger-zone button, the sidebar's "archived" list, or a card: propose_project_archive and propose_project_unarchive.
11delete#
Delete is only for an archived project, from the delete row on its settings page or from a propose_project_delete card. It is refused while a run is live (409 live_runs) and unless the project's exact name is typed (400 name_mismatch); only the operator's typed name on the settings page or on the card deletes, so a click from discord or autopilot is refused, and clio can't fill the name in. The confirm shows, word for word, what goes and what stays, worked out by the desk (GET /api/desk/project/delete-plan).
The order is fixed: the project and its tickets on the tracker first (no undo; if the tracker refuses, nothing local is touched, and a tracker that no longer has the project counts as done), then the project's chats (moved to the desk's trash), then the work clone and <clone>.runs, and the local settings file last. A stop part-way therefore leaves an archived project that can be deleted again.
What stays: the repo folder, the memory dir ~/.smith/memory-projects/<name>, library assets (an asset only this project held is left unplugged), the discord server and channels, and run journals and proofs.
12when you're the conductor#
Edit the record with propose_config_edit on records/<project name>.json. Edit the local half with projects/<tracker>/<project id>.json, using the file the projects tool returns.