← Back to knowledge

github / git / ai-agent

Coordinating Multiple AI Agents with GitHub Projects, Issues, PRs, and a Custom .team Registry

A task workflow that keeps the human-facing lifecycle in GitHub while using a custom .team registry only for active leases and path-conflict prevention.

Published
2026-08-22
Updated
2026-09-05
Applies to
GitHub Projects / GitHub Issues / GitHub Pull Requests

Overview

Workflow update (2026-09-05): Company now treats Issues as optional and has retired its shared registry, leases, and task:* commands. This article records the former design. The procedures and code examples below are not the current Company workflow.

Delegating development work to multiple AI agents introduces conflicts that ordinary task tracking does not handle well. Two agents may edit the same files, an agent may silently take over an expired task, or a pull request from the wrong branch may be linked to an issue.

Building a large custom task manager to solve these problems creates a different risk: GitHub Projects, Issues, Pull Requests, and the custom system all begin storing the same progress state.

This article describes a hybrid design. GitHub remains the source of truth for the human-facing work lifecycle, while a .team registry on a dedicated branch provides only the active lease and path-level exclusion that GitHub does not directly provide.

Conclusions

  • Use GitHub Projects to display priority and the Open, In Review, and Done work lifecycle.
  • Use Issues as work contracts containing context, objectives, completion criteria, exclusions, and writable paths.
  • Use Pull Requests as the source of truth for implementation diffs, reviews, CI, and merge state.
  • Keep only the owner, session, lease, branch, claimed paths, and PR identity checks in .team.
  • Determine whether implementation is active from the .team lease; do not duplicate Project Status, draft-pr, or ready-pr there.
  • Let built-in GitHub Projects workflows handle the transitions they support, and do not update the Project from task:start.

Do not make one system own everything

Projects, Issues, PRs, and a coordination registry each serve a different purpose.

InformationSource of truthWhy
Priority, planned owner, work stageGitHub ProjectEasy for people to inspect in board and table views
Context, objective, completion criteria, exclusionsGitHub IssueKeeps decisions and discussion attached to the unit of work
Diff, review, CI, merge statePull RequestDirectly connected to Git revisions and review state
Owner, session, lease.teamSupports machine-enforced temporary ownership
Allowed paths and shared resources.teamRejects conflicts before implementation and before commit

In this split, Project Status is the source of truth for the human-facing work lifecycle. .team owns the active implementation lease, while the PR itself owns Draft, Ready, and merged state. .team is a development safety mechanism, not a second Project.

Create three Project Status values.

StatusMeaning
OpenThe Issue is open and no PR is currently awaiting review
In ReviewA PR is linked to the Issue and is reviewable
DoneThe Issue is closed or the PR is merged

The standard flow becomes:

Issue created
  -> Project: Open
  -> task:start: claim lease and paths
     Project stays Open; inspect .team for active implementation
  -> PR created with "Closes #123"
  -> Project: In Review
  -> PR merged / Issue closed
  -> Project: Done
  -> task:finish: sync main and release lease

Open intentionally covers both unclaimed and actively implemented work. Inspect the .team active record or task:status to distinguish them. task:finish should not set the Project to Done. GitHub handles the merge and Issue closure, and a Project workflow reflects that state. task:finish focuses on syncing the local main branch and releasing the temporary claim.

Treat the Issue as a work contract

At minimum, an Issue should contain the context, objective, completion criteria, and exclusions. Include the agent's writable scope in a machine-readable block.

## Task contract

<!-- task-contract
allowed_paths:
  - apps/web/content/knowledge/**
  - tools/task-workflow/**
shared_resources:
  - apps/web/pnpm-lock.yaml
required_checks:
  - format:check
  - typecheck
branch_slug: project-team-hybrid
-->

allowed_paths describes normal writable scope. shared_resources covers files such as lockfiles that multiple areas may need to change. If a new claim overlaps another active task, split the Issue or adjust the claimed scope before implementation begins.

Limit .team to leases and conflict prevention

GitHub Projects is effective for progress visibility, but it does not provide a compare-and-swap lease such as “this session owns this directory for four hours,” nor does it reject overlapping path claims.

A dedicated branch can therefore contain .team/index.yml and one active record per task. The files may use a YAML extension while containing JSON-compatible data to keep parsing simple.

{
  "schema_version": 1,
  "issue": 123,
  "owner": {
    "kind": "agent",
    "name": "implementation-agent",
    "session_id": "session-uuid"
  },
  "branch": "codex/issue-123-project-team-hybrid",
  "base_branch": "main",
  "base_sha": "commit-sha",
  "allowed_paths": ["tools/task-workflow/**"],
  "shared_resources": [],
  "started_at": "2026-08-22T00:00:00.000Z",
  "heartbeat_at": "2026-08-22T01:00:00.000Z",
  "lease_expires_at": "2026-08-22T05:00:00.000Z",
  "pr": 456,
  "pr_head_sha": "commit-sha"
}

Do not store Project Status or PR Draft/Ready state in this record. pr and pr_head_sha are not progress indicators. They verify that the registered PR still points to the expected task-branch revision when the task is finished.

Only allow fast-forward updates to the coordination branch. Use the head SHA that was read as the expected ref value. If another agent updates the branch first, do not force-push. Read the latest state and run conflict detection again.

Delegate supported transitions to GitHub Projects

Built-in GitHub Projects workflows can handle these transitions without custom code.

TriggerAction
A matching open Issue is created or updatedAdd it to the Project
An item is added to the ProjectSet Open
A PR is linked to an IssueSet In Review
An item is closedSet Done
A PR is mergedSet Done
An item is reopenedSet Open

Put a closing keyword such as Closes #123 in the PR body. This explicitly links the PR and Issue and automatically closes the Issue when the PR is merged into the default branch.

Divide responsibilities between task:start and Open

task:start checks for a clean worktree, parses the Issue contract, detects conflicts with active tasks, acquires the lease, and prepares the task branch. It does not update the Project; the Status remains Open until a PR is linked to the Issue.

Because work start is not duplicated in the Project, the workflow needs neither a browser interaction nor a Project API token and Project, field, or option IDs. The Project shows the review and completion boundaries to people, while .team exposes the active owner, session, and lease to machines. Keeping these two levels intentionally different prevents the visible lifecycle and the execution claim from drifting apart.

After creating a PR, the coordination command should verify only that:

  • the PR head repository is the expected repository;
  • the PR head branch matches the active record;
  • the PR base branch matches the planned base branch; and
  • the PR head SHA matches both the local and remote task-branch revision.

Store the matching PR number and head SHA. GitHub already exposes Draft and Ready state, so do not copy those states to the custom registry. If new commits are pushed, run the validation again and update the stored head SHA.

Design failure and recovery first

Interrupted work is more important than the happy path in a multi-agent workflow.

  • Do not automatically take an expired lease. Inspect the Issue, branch, PR, and last heartbeat before an explicit takeover.
  • If the coordination ref update conflicts, never force-push. Retry from the latest state.
  • If GitHub deletes the PR head branch after merge, finish only when the saved PR head SHA still matches the local revision.
  • Record the reason in the Issue before releasing an aborted claim.
  • Do not infer file ownership from Project Status alone.
  • Do not report product progress from .team alone.

Introduce the model in stages

When adding this model to an existing Issue and PR workflow:

  1. Create Open, In Review, and Done in the Project.
  2. Auto-add matching open Issues, and set added or reopened items to Open.
  3. Move linked PRs to In Review and closed or merged work to Done.
  4. Add .team leases and path exclusion, and acquire them through task:start.
  5. Remove duplicated Project Status, Draft, and Ready states from the custom registry.
  6. Reduce task:link-pr to PR and branch-revision validation.
  7. Reduce task:finish to merge verification, main synchronization, and lease release.

This order lets the team compare GitHub's visible state with actual work at every stage and roll back a small change if necessary.

Caveats

  • .team is not a GitHub standard. It is an optional coordination registry for multi-agent development and may be unnecessary for solo work or low-conflict teams.
  • Open alone does not distinguish unclaimed work from active implementation. Inspect .team or task:status for the current claim.
  • A lease does not provide an operating-system file lock or eliminate Git conflicts. Validate changed paths again before commit.
  • Auto-add workflows apply to new or updated matching items after the workflow is enabled. Existing items may need to be added separately.
  • A closing keyword closes the Issue when the linked PR is merged into the repository's default branch.
  • Project fields and workflows can change. Periodically compare the operating guide with the actual Project configuration.