Multi-Project Workflow

Coordinate work across linked projects with the project registry, ait projects, and cross-repo task creation

When you run more than one aitasks project — say a frontend app and the backend service it talks to — work in one often needs to reach into the other: file a coordination task there, point at one of its files, or run a command inside it. The multi-project layer replaces brittle ../some-repo/ disk paths with logical project names that resolve to wherever each project currently lives.

Why logical project names

Hardcoding a sibling path like ../backend/ breaks the moment the on-disk layout differs — a teammate’s machine, a cloud agent, or a fresh re-clone into a different directory. A per-user registry maps a logical name (backend) to a path instead, and every cross-repo command resolves the name at call time rather than trusting a recorded path. For the idea in brief, see Cross-repo references.

Per-project identity

Each aitasks project declares its logical identity in aitasks/metadata/project_config.yaml:

project:
  name: backend
  git_remote: https://example.com/acme/backend.git
  project_group: storefront
FieldRequiredNotes
nameNo — defaults to the directory basenameLogical key. Lowercase [a-z0-9_-], unique across your registered projects.
git_remoteNoCanonical clone URL. Shown in listings, and used by ait projects doctor --clone to re-clone a stale project.
project_groupNoOptional fallback group slug used when the per-user registry has no group for this project. Slugs match ^[a-z0-9][a-z0-9_-]*$.

ait projects add reads this block; if name is omitted it falls back to the project directory’s basename.

The project registry

Registered projects live in a per-user index at ~/.config/aitasks/projects.yaml (override the location with the AITASKS_PROJECTS_INDEX environment variable). It is a flat list:

projects:
  - name: frontend
    path: /home/you/work/frontend
    git_remote: https://example.com/acme/frontend.git
    last_opened: 2026-05-31
    project_group: storefront
  - name: backend
    path: /home/you/work/backend
    last_opened: 2026-05-31
    project_group: storefront

The file is gitignored — it is per-user and machine-specific. Manage it with ait projects add rather than editing it by hand.

Project groups

A project-group keeps related registered projects together in the TUI switcher and Stats TUI. For example, frontend and backend can both belong to the storefront group while another set of repos belongs to internal_tools.

The registry’s project_group field is the normal source of truth for your machine. If a registry entry has no group, aitasks falls back to that repo’s project.project_group value from project_config.yaml. Clearing a group with ait projects group unset writes a reserved - marker in the registry, which means “explicitly ungrouped” and prevents fallback to the repo config.

Manage membership with the CLI:

ait projects group list
ait projects group set frontend storefront
ait projects group set backend storefront
ait projects group unset backend
ait projects group rename storefront customer_app
ait projects group sync

rename updates every registry entry in the old group; if the new group already exists, the two groups are merged. sync fills in only missing registry groups from each repo’s project.project_group; it does not overwrite an explicit group or an explicit ungrouped marker.

How a name resolves to a path

When a command needs the path for a logical name, the resolver tries three sources in order:

  1. Live tmux scan — currently running project sessions (matched by project name, then session name).
  2. Per-user index — the projects.yaml registry above.
  3. Process environment — an AITASKS_PROJECT_<name> variable set in the calling shell, useful as a manual override in non-tmux contexts (CI, remote agents).

Resolution reports one of three states, which surface throughout the tooling:

StateMeaning
RESOLVED (listed as LIVE / OK)The name maps to a valid aitasks project on disk.
NOT_FOUNDNo registered project matched the name.
STALEThe name is registered, but its path no longer holds an aitasks project (moved or deleted).

A name that does not resolve is reported, never repaired for you. A command that needs it stops and prints how to register the project (cd /path/to/backend && ait projects add). Nothing clones a repository to make a reference resolve: you named the project, and only you know which checkout it means. Cloning happens only when you ask for it, with ait projects doctor --clone.

The ait projects command

ait projects manages the registry and resolves names:

VerbWhat it does
ait projects listList every registered project with its status (LIVE / OK / STALE).
ait projects add [<path>]Register the project at <path> (default: current directory). Idempotent — re-running refreshes the entry.
ait projects resolve <name>Print the resolver result for <name>: RESOLVED:<path>, NOT_FOUND:<name>, or STALE:<name>:<path>.
ait projects exec <name> -- <command>Resolve <name>, cd into its root, then run <command> there.
ait projects remove <name> [--force]Drop an entry from the registry. Prompts for confirmation unless --force.
ait projects update <name> <new_path>Repoint a moved project to a new on-disk root (refreshes last_opened, keeps git_remote).
ait projects prune [--dry-run] [--yes]Drop every STALE entry at once. Prompts per entry unless --yes; --dry-run lists matches without changing anything.
ait projects doctor [--clone]Walk every STALE entry interactively, offering prune / update / clone / keep / skip-all per entry. Cloning is opt-in via --clone and only offered when the entry has a git_remote.
ait projects group listShow registered projects grouped by effective project-group, with (ungrouped) last.
ait projects group set <name> <group>Assign a project to a project-group slug.
ait projects group unset <name>Explicitly clear a project’s group.
ait projects group rename <old> <new>Rename a group; merges if <new> already exists.
ait projects group syncBackfill missing registry groups from repo configs.

A few examples:

# Register the project in the current directory, then confirm it is listed
cd /home/you/work/backend
ait projects add
ait projects list

# Run a one-off command inside a sibling project without leaving this one
ait projects exec backend -- ./.aitask-scripts/aitask_ls.sh -v 5

# Put related projects in the same switcher/stats navigation group
ait projects group set frontend storefront
ait projects group set backend storefront

# Clean up after a project moves or is deleted
ait projects update backend /home/you/work/services/backend
ait projects prune --dry-run

Creating a task in a sibling project

ait create --batch --project <name> creates a task in another registered project without leaving the one you are in. It resolves <name>, then runs that project’s own task creation so the new task is numbered and committed in the sibling repo.

# From inside `frontend`, file a coordination task in `backend`
ait create --batch --project backend \
    --name bump_shared_protocol --type bug --priority high \
    --desc "Bump the shared wire protocol version after the frontend change" \
    --commit

Two constraints:

  • --project requires --batch (non-interactive creation).
  • --project can be combined with --parent, but the parent is resolved in the target project: ait create --batch --project backend --parent 42 creates a child of backend’s task 42. A task can never be made a child of a parent in a different project: a parent and its children always live in one project, and a change that spans two repos is two parents, one per repo.

Sending a note to a sibling project’s task

When a task in another project needs context rather than new work, send it a task note with --project:

ait note 42 --project mobile --from 349 --text "..."

The name resolves as described in how a name resolves to a path, except that every source is checked and a conflict between them refuses the note instead of picking one. The other project’s own tooling writes and commits it. The resolution rules, the recorded sender and every refusal are documented once, in ait note.

Referring to cross-project tasks and files

When a plan, commit message, or task description needs to point at another project’s task or file, write the reference with the project’s logical name:

backend#835_3             # task 835_3 in the `backend` project (preferred)
backend#t835_3            # the leading `t` is also accepted
backend:src/protocol.rs   # a file inside the `backend` project
  • For tasks: <project>#<id>, where <id> is a parent (835) or a parent-child pair (835_3). The t prefix is optional.
  • For files: <project>:<path>, where <path> is relative to the project root.

Writing references this way keeps them unambiguous and machine-resolvable, instead of a disk path that only works on one machine.

Once names resolve, see Cross-Project Dependencies to block tasks on another project’s work, read and update its tasks with --project, surface cross-repo links on the board, and plan a change that spans two repos.

Switching between projects

The ait TUI’s project switcher lists registered projects even when their tmux session is not currently running. Selecting an inactive project spawns its tmux session and teleports you into it. A project whose path has gone stale is shown dimmed with a (stale) marker; selecting it offers to prune the entry or repoint it to the project’s new location.

When project-groups are configured, the switcher uses two navigation axes:

  • Left / Right cycles the current ring: projects in the selected project-group, plus any live session outside that group so active work remains reachable.
  • [ / ] changes the selected project-group and rebuilds the ring.

The Stats TUI uses the same project-group navigation for session-aware panes. On the agent ranking panes, [ / ] keep their time-window meaning instead.

ait monitor is intentionally unchanged — its multi-project view stays scoped to live tmux sessions only. Registered-but-inactive projects appear in the switcher, not in the monitor.

Recipe: register a linked project and spawn a task in it

# 1. Make sure the linked project declares its identity
#    (aitasks/metadata/project_config.yaml → project.name: backend)

# 2. Register it from its own directory
cd /home/you/work/backend
ait projects add

# 3. Back in your current project, confirm the name resolves
ait projects list
ait projects resolve backend        # → RESOLVED:/home/you/work/backend

# 4. File a coordination task in the linked project
ait create --batch --project backend \
    --name add_pagination_endpoint --type feature --priority medium \
    --desc "Add the paginated list endpoint the new frontend screen needs" \
    --commit