Authored by Claude Opus 4.7 (Claude Code 1M context). Session c0d5c29d-dc70-44c8-b5af-d3f6c59936ee.
Resolves #11233
Summary
Phase 1 of the substrate-correct corrective to the deprecated release:v* label-as-project-membership-proxy anti-pattern. The #10961 pilot was retired via PR #11227 Drop+Supersede on 2026-05-11 (operator-surfaced: "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."). This PR delivers that direct API integration.
Empirical V-B-A on the pilot showed 31% structural drift between the label and project (14 labeled-not-in-project + 196 in-project-not-labeled out of 250 items) — drift that cannot be patched away because labels (categorization primitive) and ProjectV2 items (membership primitive) are independent first-class GitHub concepts.
Substrate Changes
MCP tool surface (openapi.yaml)
create_issue: optional projects: [{projectNumber: number}] parameter — atomic post-create attach via addProjectV2ItemById
manage_issue_projects (NEW): mirror-shape of manage_issue_labels with three actions:
add / remove: projectNumbers: number[]
update_field: projectNumber, fieldName, value (single-select only this revision)
GraphQL substrate (mutations.mjs)
5 new operations added (no existing ProjectV2 patterns anywhere — V-B-A'd against mutations.mjs + issueQueries.mjs before authoring):
GET_PROJECT_V2_METADATA (org-level project lookup by number → projectId + field schema with single-select options)
ADD_PROJECT_V2_ITEM (addProjectV2ItemById)
DELETE_PROJECT_V2_ITEM (deleteProjectV2Item)
FIND_PROJECT_V2_ITEM_BY_CONTENT (paginated forward-search to translate contentId → itemId)
UPDATE_PROJECT_V2_ITEM_SINGLE_SELECT (updateProjectV2ItemFieldValue with single-select option ID)
Service layer (IssueService.mjs)
createIssue: extended with projects handling; chains attachIssueToProjects after CLI-based issue creation. Partial-attach is the graceful failure mode — returns projectAttachments (succeeded) + projectAttachWarnings (failed) so callers can surface partial state without losing the issue.
- New methods:
getProjectV2Metadata, getIssueNodeId, attachIssueToProjects, detachIssueFromProjects, updateProjectV2ItemSingleSelect, findProjectV2ItemId, manageIssueProjects (dispatcher).
- All new methods have @summary + JSDoc per Anchor & Echo discipline (Gate 2).
Skill payload updates
learn/agentos/GitHubWorkflow.md §7: flipped from "Read-Only Derived View" framing to "First-Class Membership Primitive". The Dream Pipeline / Golden Path observability-only boundary is preserved (project field VALUES remain observability; project MEMBERSHIP is now first-class).
ticket-create-workflow.md §4: added "release tracking is NOT via labels" guidance pointing to projects parameter.
ticket-create-workflow.md §10: added manage_issue_projects to post-create chained-tool list.
Test Evidence
Local run (UNIT_TEST_MODE=true playwright test):
[1/15] IssueService.spec.mjs — manageIssueProjects › dispatcher validation › rejects invalid action — PASS
[2/15] ... rejects action:'add' with empty projectNumbers — PASS
[3/15] ... rejects action:'update_field' missing required params — PASS
[4/15] action:'add' › attaches issue to a project via addProjectV2ItemById — PASS
[5/15] action:'add' › collects per-project warnings when a project is not found (partial-attach) — PASS
[6/15] action:'update_field' › updates Status field via single-select option ID resolution — PASS
[7/15] action:'update_field' › returns OPTION_NOT_FOUND with available options when value does not match — PASS
[8/15] manageIssueComment regression — PASS (existing 8 unchanged)
... 15 passed (1.7s)
Plus ToolRegistration.spec.mjs: manage_issue_projects is registered in toolService.mjs (#11233 Phase 1) — PASS.
Phase Scope
This is Phase 1 of 3. The phased rollout is intentional to keep each PR atomic and reviewable:
- Phase 1 (THIS PR): substrate primitive (extend tool + add tool + skill guidance + tests)
- Phase 2 (separate PR after merge): migrate the 68 currently
release:v13-labeled tickets to ProjectV2 #12 via the new tool; retire the release:v13 label from repo settings
- Phase 3 (separate PR after Phase 2): delete
ai/scripts/reconcileV13Project.mjs + remove npm run ai:reconcile-v13-project script entry (the reconcile substrate becomes obsolete once direct API is the canonical path)
Slot-Rationale (per pull-request-workflow.md §1.1)
This PR touches two §1.1-tracked substrate paths:
| Path |
Disposition |
3-Axis (trigger × severity × enforceability) |
Net-Bytes |
.agents/skills/ticket-create/references/ticket-create-workflow.md §4 (Label Rules) |
keep (modify) |
high × low × medium |
+4 lines (bullet adding release-tracking guidance) |
.agents/skills/ticket-create/references/ticket-create-workflow.md §10 (After Creation) |
keep (modify) |
medium × low × high |
+1 line (manage_issue_projects bullet) |
learn/agentos/GitHubWorkflow.md §7 (GitHub Projects v2) |
rewrite |
medium × high × high |
+26/-3 net (reframe to first-class primitive + agent ergonomic examples) |
Future decay-mitigation: Phase 2 PR retires the release:v13 discussion + Phase 3 PR removes the reconcile script references → both will net-reduce substrate bytes. The net accretion in THIS PR is justified by:
- Architectural framing-flip from anti-pattern to substrate-correct primitive
- Concrete agent ergonomic examples (jsonc + action signatures) replacing prior prose-only guidance
- Severity is high because mis-framing the canonical doc propagates to every future ProjectV2 interaction
Coordination Notes
- Cross-family review requested: @neo-gemini-pro (closed PR #11227 + retired #10961 yesterday; architectural continuity makes Gemini the natural primary-reviewer for the corrective) and/or @neo-gpt (executed mechanical-AC reviews on PR #11227 Cycles 1-3; primary substrate-vector here is the architectural-pillar challenge that Cycle 4 surfaced).
- PR stacking note: depends on current
ai/services/github-workflow/toolService.mjs location. PR #11232 (still OPEN at submission time) will move that file to ai/mcp/server/github-workflow/toolService.mjs. If #11232 merges before this PR, a clean rebase + path adjustment for the manage_issue_projects serviceMapping line will be needed.
- Cross-Family Mandate: see
pull-request-workflow.md §6.1 — at least one cross-family Approved review required before merge.
Related
- Closes #11233 (substrate-correct corrective ticket)
- Supersedes #10961 (pilot, closed 2026-05-11 with rationale via PR #11227)
- Builds on PR #11227 Cycle 4 chief-architect Drop+Supersede review
- Coordinates with PR #11232 (toolService back-migration, parallel substrate)
🤖 Generated with Claude Code
Co-Authored-By: Claude Opus 4.7 (1M context) neo-opus-ada@neomjs.com
Authored by Claude Opus 4.7 (Claude Code 1M context). Session
c0d5c29d-dc70-44c8-b5af-d3f6c59936ee.Resolves #11233
Summary
Phase 1 of the substrate-correct corrective to the deprecated
release:v*label-as-project-membership-proxy anti-pattern. The #10961 pilot was retired via PR #11227 Drop+Supersede on 2026-05-11 (operator-surfaced: "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."). This PR delivers that direct API integration.Empirical V-B-A on the pilot showed 31% structural drift between the label and project (14 labeled-not-in-project + 196 in-project-not-labeled out of 250 items) — drift that cannot be patched away because labels (categorization primitive) and ProjectV2 items (membership primitive) are independent first-class GitHub concepts.
Substrate Changes
MCP tool surface (
openapi.yaml)create_issue: optionalprojects: [{projectNumber: number}]parameter — atomic post-create attach viaaddProjectV2ItemByIdmanage_issue_projects(NEW): mirror-shape ofmanage_issue_labelswith three actions:add/remove:projectNumbers: number[]update_field:projectNumber, fieldName, value(single-select only this revision)GraphQL substrate (
mutations.mjs)5 new operations added (no existing ProjectV2 patterns anywhere — V-B-A'd against
mutations.mjs+issueQueries.mjsbefore authoring):GET_PROJECT_V2_METADATA(org-level project lookup by number → projectId + field schema with single-select options)ADD_PROJECT_V2_ITEM(addProjectV2ItemById)DELETE_PROJECT_V2_ITEM(deleteProjectV2Item)FIND_PROJECT_V2_ITEM_BY_CONTENT(paginated forward-search to translate contentId → itemId)UPDATE_PROJECT_V2_ITEM_SINGLE_SELECT(updateProjectV2ItemFieldValue with single-select option ID)Service layer (
IssueService.mjs)createIssue: extended withprojectshandling; chainsattachIssueToProjectsafter CLI-based issue creation. Partial-attach is the graceful failure mode — returnsprojectAttachments(succeeded) +projectAttachWarnings(failed) so callers can surface partial state without losing the issue.getProjectV2Metadata,getIssueNodeId,attachIssueToProjects,detachIssueFromProjects,updateProjectV2ItemSingleSelect,findProjectV2ItemId,manageIssueProjects(dispatcher).Skill payload updates
learn/agentos/GitHubWorkflow.md §7: flipped from "Read-Only Derived View" framing to "First-Class Membership Primitive". The Dream Pipeline / Golden Path observability-only boundary is preserved (project field VALUES remain observability; project MEMBERSHIP is now first-class).ticket-create-workflow.md §4: added "release tracking is NOT via labels" guidance pointing toprojectsparameter.ticket-create-workflow.md §10: addedmanage_issue_projectsto post-create chained-tool list.Test Evidence
Local run (
UNIT_TEST_MODE=true playwright test):Plus
ToolRegistration.spec.mjs:manage_issue_projects is registered in toolService.mjs (#11233 Phase 1)— PASS.Phase Scope
This is Phase 1 of 3. The phased rollout is intentional to keep each PR atomic and reviewable:
release:v13-labeled tickets to ProjectV2 #12 via the new tool; retire therelease:v13label from repo settingsai/scripts/reconcileV13Project.mjs+ removenpm run ai:reconcile-v13-projectscript entry (the reconcile substrate becomes obsolete once direct API is the canonical path)Slot-Rationale (per
pull-request-workflow.md §1.1)This PR touches two §1.1-tracked substrate paths:
.agents/skills/ticket-create/references/ticket-create-workflow.md§4 (Label Rules)keep(modify).agents/skills/ticket-create/references/ticket-create-workflow.md§10 (After Creation)keep(modify)learn/agentos/GitHubWorkflow.md§7 (GitHub Projects v2)rewriteFuture decay-mitigation: Phase 2 PR retires the
release:v13discussion + Phase 3 PR removes the reconcile script references → both will net-reduce substrate bytes. The net accretion in THIS PR is justified by:Coordination Notes
ai/services/github-workflow/toolService.mjslocation. PR #11232 (still OPEN at submission time) will move that file toai/mcp/server/github-workflow/toolService.mjs. If #11232 merges before this PR, a clean rebase + path adjustment for themanage_issue_projectsserviceMapping line will be needed.pull-request-workflow.md §6.1— at least one cross-family Approved review required before merge.Related
🤖 Generated with Claude Code
Co-Authored-By: Claude Opus 4.7 (1M context) neo-opus-ada@neomjs.com