Allow Projects API boards to include cross-repository issues #39

Closed AtlantisPleb opened this 6h ago

Outcome

Allow a repository-scoped Projects V2 board to track issues from other repositories. Keep the repository that owns the project as the project authorization boundary, and treat each issue repository as a separate source and visibility boundary.

This limitation currently prevents the Cloud computer platform project from including OpenAgentsInc/openagents.com#37 and OpenAgentsInc/openagents.com#38.

Current behavior

POST /api/v3/repos/{project_owner}/{project_repo}/projectsV2/{project_number}/items accepts only issue_number. The implementation always resolves that number inside the repository that owns the project.

The current implementation reinforces that assumption:

  • OpenAgents.Projects.create_project_item/3 queries the issue with repository_id: project.repository_id.
  • OpenAgentsWeb.ProjectController.create_item/2 casts only issue_number.
  • project_items.repository_id and its composite foreign keys require the project and issue to share one repository.
  • Project item list and get queries scope both the project and item through the same repository.

This design conflates project ownership with source-issue identity.

API contract

Keep the existing request backward compatible. A request with only issue_number continues to resolve the issue in the repository that owns the project:

{
  "issue_number": 37,
  "values": {
    "Status": "To Do"
  }
}

Add a canonical cross-repository request shape:

{
  "issue": {
    "owner": "OpenAgentsInc",
    "repo": "openagents.com",
    "number": 37
  },
  "values": {
    "Status": "To Do"
  }
}

Return the source identity in project item responses so API and CLI clients do not need to interpret an internal issue ID:

{
  "issue": {
    "owner": "OpenAgentsInc",
    "repo": "openagents.com",
    "number": 37,
    "url": "https://openagents.com/api/v3/repos/OpenAgentsInc/openagents.com/issues/37",
    "html_url": "https://openagents.com/OpenAgentsInc/openagents.com/issues/37"
  }
}

Preserve existing response fields during the compatibility period.

Data model and migration

Separate project tenancy from source-issue tenancy.

Prefer a normalized project_id foreign key and issue_id foreign key, with repository identities derived through those records. If tenant-scoped composite foreign keys remain necessary, store distinct project_repository_id and issue_repository_id columns instead of one ambiguous repository_id.

The migration must:

  • Preserve every existing project item.
  • Remove the constraint that requires a project and issue to share a repository.
  • Keep an explicit foreign key from the item to its project and source issue.
  • Enforce one item per project_id and issue_id.
  • Define cleanup behavior when the project, source issue, or source repository is deleted.
  • Support a safe rollback.

Authorization and visibility

Use both authorization boundaries:

  • Require writable membership in the repository that owns the project to add, update, or remove a project item.
  • Require read access to the source issue repository to add its issue. Project write access plus source read access is sufficient; changing the issue still requires source-repository write access.
  • Return 404 for a source repository or issue that the actor cannot read.
  • Never make a private source issue visible because it appears on a public or otherwise readable project.
  • On project reads, omit an inaccessible source item or return a typed inaccessible placeholder that contains no repository name, issue number, title, body, labels, assignees, or other source metadata. Choose one behavior, document it, and test it consistently.
  • Apply the same visibility rules to HTML, JSON, LiveView events, PubSub payloads, logs, and analytics.

Web and serialization behavior

  • Preload each source issue and its repository without assuming that they match the project repository.
  • Render cross-repository items as owner/repo#number and link to the source issue.
  • Keep project field values, status updates, ordering, and item removal scoped to the project.
  • Do not expose private repository names or issue metadata through counts, empty states, errors, or live updates.

Documentation and CLI coverage

  • Update docs/openagents-cli/api.md with local and cross-repository openagents api --input examples.
  • Update docs/github-api-issues-projects-assessment.md to describe the new contract, authorization rules, response shape, and remaining compatibility differences.
  • Keep the generic CLI API path working with nested JSON. A named project command is not required for this issue.

Acceptance criteria

  • A project can add and list a public issue from another repository.
  • A project writer with read-only access to a private source repository can add that issue.
  • A project reader cannot add an issue even when they can write to the source repository.
  • An actor without source-repository read access receives 404 and learns no private metadata.
  • A viewer who loses source access cannot retrieve private issue metadata through the project.
  • The same issue number in two repositories resolves to the explicit owner, repo, and number.
  • Adding the same source issue twice does not create duplicate project items.
  • A nonexistent source repository or issue returns 404.
  • The legacy issue_number request continues to add an issue from the project repository.
  • Project item JSON includes the source repository identity and issue number.
  • The web project board labels and links cross-repository issues correctly.
  • Controller, context, migration, authorization, LiveView, and repository-isolation tests cover these cases.
  • mix precommit passes.

Related work

  1. AtlantisPleb opened this issue 6h ago
  2. closed this as completed 5h ago
Sign in with GitHub to comment on this issue.