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.

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).

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.

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.

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.

09record fields kept for old files#

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.