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, andDonework 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
.teamlease; do not duplicate Project Status,draft-pr, orready-prthere. - 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.
| Information | Source of truth | Why |
|---|---|---|
| Priority, planned owner, work stage | GitHub Project | Easy for people to inspect in board and table views |
| Context, objective, completion criteria, exclusions | GitHub Issue | Keeps decisions and discussion attached to the unit of work |
| Diff, review, CI, merge state | Pull Request | Directly connected to Git revisions and review state |
| Owner, session, lease | .team | Supports machine-enforced temporary ownership |
| Allowed paths and shared resources | .team | Rejects 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.
Recommended state transitions
Create three Project Status values.
| Status | Meaning |
|---|---|
Open | The Issue is open and no PR is currently awaiting review |
In Review | A PR is linked to the Issue and is reviewable |
Done | The 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.
| Trigger | Action |
|---|---|
| A matching open Issue is created or updated | Add it to the Project |
| An item is added to the Project | Set Open |
| A PR is linked to an Issue | Set In Review |
| An item is closed | Set Done |
| A PR is merged | Set Done |
| An item is reopened | Set 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.
Keep task:link-pr focused on validation
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
.teamalone.
Introduce the model in stages
When adding this model to an existing Issue and PR workflow:
- Create
Open,In Review, andDonein the Project. - Auto-add matching open Issues, and set added or reopened items to
Open. - Move linked PRs to
In Reviewand closed or merged work toDone. - Add
.teamleases and path exclusion, and acquire them throughtask:start. - Remove duplicated Project Status, Draft, and Ready states from the custom registry.
- Reduce
task:link-prto PR and branch-revision validation. - Reduce
task:finishto 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
.teamis 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.Openalone does not distinguish unclaimed work from active implementation. Inspect.teamortask:statusfor 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.