Context
Operator-surfaced 2026-05-11 (this session) on PR #11227 chief-architect audit:
"v13 label are noise. this is NOT how github v2 projects work. tickets have labels and projects. direct api mapping. either we have a ticket to include projects into our create ticket tool, or we already did that. i seriously want to see this hacky label getting removed. not a script to better use the failed approach more."
V-B-A'd: no existing ticket for ProjectV2-in-create_issue. The github-workflow MCP tool's create_issue schema has only title/body/labels/assignees — no addToProjects parameter. The release:v13 label is a hacky workaround for missing project-membership API integration.
The Problem
Current shape (wrong):
- Agent creates ticket with
create_issue({labels: ["release:v13", ...]})
release:v13 label is used as project-membership proxy
- A separate
ai/scripts/reconcileV13Project.mjs script reconciles "labeled but not in project" + "in project but not labeled" drift
- Drift is structural — labels and projects are independent GitHub primitives; the proxy is always going to drift
GitHub Projects v2 reality (per V-B-A on GitHub API docs + the operator's framing):
- Tickets have LABELS and PROJECTS as independent first-class properties
- Direct API:
addProjectV2ItemById mutation adds a ticket to a project
- Direct API:
removeProjectV2Item mutation removes a ticket from a project
- No label-as-proxy needed; projects are not derived from labels
Why the current approach was taken (best-guess root cause): create_issue MCP tool was built before ProjectV2 mutation support existed; label was the simplest substrate to add for "v13 release tracking" without extending the tool.
Why it's substrate-failure:
- Drift between label and project IS structural (PR #11227 V-B-A showed 14 "labeled-not-in-project" + 196 "in-project-not-labeled" — 31% drift)
- Reconcile script (PR #11227's approach) treats the SYMPTOM (drift detection), not the CAUSE (wrong substrate)
- Label
release:v13 is noise on every ticket — pollutes label-as-categorization-tool semantics
- PR #11227's pagination fix doesn't fix the substrate; just makes the wrong-shape script more efficient
The Architectural Reality
Per GitHub Projects v2 API:
addProjectV2ItemById(input: {projectId: ID!, contentId: ID!}) — adds existing issue to project
addProjectV2DraftItem(input: {projectId: ID!, title: String!, body: String}) — creates new draft item
Per Neo MCP tool surface (V-B-A'd):
create_issue in ai/mcp/server/github-workflow/openapi.yaml — accepts title/body/labels/assignees only
- NO
addProjectV2* operations exposed
- NO service file references
projectV2 mutation logic
The Fix
Phase 1: Extend create_issue + add project-membership tools
Extend create_issue schema with optional projects parameter:
projects:
type: array
items:
type: object
properties:
projectNumber: { type: integer }
status: { type: string, description: "Optional initial status (e.g., 'Todo', 'In Progress')" }
description: |
(Optional) Array of ProjectV2 memberships to add. Each entry specifies a project number
and optional initial status. Uses `addProjectV2ItemById` GraphQL mutation after issue creation.
Add manage_issue_projects tool (mirror of manage_issue_labels):
action: 'add' → addProjectV2ItemById
action: 'remove' → removeProjectV2Item
action: 'update_field' → updateProjectV2ItemFieldValue (status, priority, etc.)
Update ticket-create-workflow.md skill payload — replace release:v13 label guidance with projects: [{projectNumber: 12, status: 'Todo'}] pattern.
Phase 2: Retire release:v13 label
- Migrate all currently-labeled tickets: add them to ProjectV2 #12 via the new tool, then remove the label
- Update all skill payloads that mention
release:v13 label to use project-membership pattern instead
- Close + retire the
release:v13 label from repo settings
Phase 3: Drop reconcile script
ai/scripts/reconcileV13Project.mjs becomes obsolete — drift can't happen if tickets are added directly to project at creation
- Delete script + remove
npm run ai:reconcile-v13-project from package.json
- Close #10961 (pilot ticket) with rationale: pilot proved the wrong-shape approach; correct shape is direct API integration
Acceptance Criteria
Out of Scope
- General label-as-proxy audit across ALL labels (other labels may have legitimate semantic-categorization use)
- ProjectV2 list/query tools beyond what's needed for the create_issue + manage_issue_projects scope
- Cross-org / external-project membership (Neo has one canonical org-level ProjectV2 #12 for v13)
Avoided Traps
- Patch the reconcile script (PR #11227 approach): rejected — treats symptom not cause; structural drift can't be patched away
- Use a different label: rejected — labels are wrong-primitive for project membership regardless of name
- Auto-detect membership from label at runtime: rejected — same as current hack, just hidden in tool implementation
- Project assignment in agent script (post-create): rejected — racy and partial; create_issue should atomically establish project membership
Related
- #10961 Pilot GitHub Projects for v13 — proved the wrong-shape approach; this ticket is the corrective
- #10960 v13 Release Tracking — parent epic; AC tracking depends on accurate project membership
- PR #11227 pagination fix for the wrong-shape reconcile script — drops on AC8 close
- PR #11225 / PR #11232 lessons: same §9.0 Premise Pre-Flight failure pattern — mechanical-AC reviews missed architectural-pillar question. Today is the second cluster surfaced.
- GitHub Projects v2 API: addProjectV2ItemById + removeProjectV2Item
Origin Session ID
c0d5c29d-dc70-44c8-b5af-d3f6c59936ee (operator-surfaced chief-architect friction this session)
Handoff Retrieval Hints
query_raw_memories(query="release:v13 label hack ProjectV2 API create_issue project membership")
ask_knowledge_base(query="github projects v2 api ticket membership label proxy")
- File anchors:
ai/mcp/server/github-workflow/openapi.yaml (create_issue schema), ai/services/github-workflow/IssueService.mjs (implementation), .agents/skills/ticket-create/references/ticket-create-workflow.md (skill guidance)
🤖 Generated with Claude Code
Context
Operator-surfaced 2026-05-11 (this session) on PR #11227 chief-architect audit:
V-B-A'd: no existing ticket for ProjectV2-in-create_issue. The github-workflow MCP tool's
create_issueschema has onlytitle/body/labels/assignees— noaddToProjectsparameter. Therelease:v13label is a hacky workaround for missing project-membership API integration.The Problem
Current shape (wrong):
create_issue({labels: ["release:v13", ...]})release:v13label is used as project-membership proxyai/scripts/reconcileV13Project.mjsscript reconciles "labeled but not in project" + "in project but not labeled" driftGitHub Projects v2 reality (per V-B-A on GitHub API docs + the operator's framing):
addProjectV2ItemByIdmutation adds a ticket to a projectremoveProjectV2Itemmutation removes a ticket from a projectWhy the current approach was taken (best-guess root cause): create_issue MCP tool was built before ProjectV2 mutation support existed; label was the simplest substrate to add for "v13 release tracking" without extending the tool.
Why it's substrate-failure:
release:v13is noise on every ticket — pollutes label-as-categorization-tool semanticsThe Architectural Reality
Per GitHub Projects v2 API:
addProjectV2ItemById(input: {projectId: ID!, contentId: ID!})— adds existing issue to projectaddProjectV2DraftItem(input: {projectId: ID!, title: String!, body: String})— creates new draft itemPer Neo MCP tool surface (V-B-A'd):
create_issueinai/mcp/server/github-workflow/openapi.yaml— acceptstitle/body/labels/assigneesonlyaddProjectV2*operations exposedprojectV2mutation logicThe Fix
Phase 1: Extend create_issue + add project-membership tools
Extend
create_issueschema with optionalprojectsparameter:projects: type: array items: type: object properties: projectNumber: { type: integer } status: { type: string, description: "Optional initial status (e.g., 'Todo', 'In Progress')" } description: | (Optional) Array of ProjectV2 memberships to add. Each entry specifies a project number and optional initial status. Uses `addProjectV2ItemById` GraphQL mutation after issue creation.Add
manage_issue_projectstool (mirror ofmanage_issue_labels):action: 'add'→addProjectV2ItemByIdaction: 'remove'→removeProjectV2Itemaction: 'update_field'→updateProjectV2ItemFieldValue(status, priority, etc.)Update
ticket-create-workflow.mdskill payload — replacerelease:v13label guidance withprojects: [{projectNumber: 12, status: 'Todo'}]pattern.Phase 2: Retire
release:v13labelrelease:v13label to use project-membership pattern insteadrelease:v13label from repo settingsPhase 3: Drop reconcile script
ai/scripts/reconcileV13Project.mjsbecomes obsolete — drift can't happen if tickets are added directly to project at creationnpm run ai:reconcile-v13-projectfrom package.jsonAcceptance Criteria
create_issueMCP tool schema extended withprojectsparameterai/services/github-workflow/IssueService.mjsor similar) implementsaddProjectV2ItemByIdcall after issue creationmanage_issue_projectstool added with add/remove/update_field actionsticket-create-workflow.mdskill payload updated to useprojectsparameter instead ofrelease:v13labelrelease:v13-labeled tickets added to ProjectV2 #12 + label removed (~68 tickets currently labeled per V-B-A)release:v13label retired from repo settings (after AC5)ai/scripts/reconcileV13Project.mjsdeleted +npm run ai:reconcile-v13-projectremoved from package.jsonOut of Scope
Avoided Traps
Related
Origin Session ID
c0d5c29d-dc70-44c8-b5af-d3f6c59936ee(operator-surfaced chief-architect friction this session)Handoff Retrieval Hints
query_raw_memories(query="release:v13 label hack ProjectV2 API create_issue project membership")ask_knowledge_base(query="github projects v2 api ticket membership label proxy")ai/mcp/server/github-workflow/openapi.yaml(create_issue schema),ai/services/github-workflow/IssueService.mjs(implementation),.agents/skills/ticket-create/references/ticket-create-workflow.md(skill guidance)🤖 Generated with Claude Code