Skip to content

GitHub mutation transport v2 — REST-first field creation

  • Status: Accepted for implementation
  • Accepted: 2026-08-04 by @nomed
  • Governing issue: #102
  • Supersedes: v1 only for create_project_field

Decision

create_project_field uses the GitHub REST API version 2026-03-10. It never uses or falls back to GraphQL. The fixed endpoints are:

  • POST /orgs/{owner}/projectsV2/{project_number}/fields;
  • POST /users/{owner}/projectsV2/{project_number}/fields.

This follows ADR-0002 and the GitHub Project fields REST contract. The v1 GraphQL contract remains unchanged for mutation kinds without a reviewed REST replacement.

Binding and request

The transport accepts only an internal create_project_field discriminator and a validated binding obtained from the fresh REST snapshot:

  • owner kind exactly orgs or users;
  • owner login matching the bounded GitHub login grammar;
  • positive Project number;
  • field name and supported data type;
  • the complete single-select option list when applicable.

Callers cannot supply a URL, method, header, API version, document, query, credential, retry policy, or receipt shape. The request body uses the REST field names name, data_type, and optional single_select_options. Presentation metadata defaults remain GRAY and an empty description when policy does not declare them.

Organization-owned Projects support GitHub App installation tokens with Projects write permission and remain the recommended profile. GitHub documents the user-owned endpoints as incompatible with GitHub App and fine-grained PAT tokens; a classic OAuth/PAT profile with Project authority is therefore a declared changed credential profile for a user-owned Project.

Budget and failure behavior

The shared ledger reserves exactly one REST request before HTTP. Field creation consumes zero GraphQL requests and zero GraphQL points. An unavailable REST reserve denies before network access.

One invocation performs zero or one POST. It never retries, redirects, polls, sleeps, falls back, or repairs a partial outcome. Authentication, authorization, rate exhaustion, transport failure, provider rejection, malformed response, response limit, and receipt mismatch retain stable redacted codes. Invalid local input and binding are invariant failures; HTTP 422 or an otherwise rejected provider response is a provider failure.

Receipt and verification

Only HTTP 201 JSON of at most 2 MiB is accepted. The receipt must contain a bounded field node ID and reproduce the exact requested name, data type, option order, option names, colors, and descriptions. A transport receipt means only providerAccepted; the executor must invalidate the schema snapshot and prove fresh zero-operation convergence independently.

Provider bodies, messages, URLs, IDs, headers, request metadata, credentials, ETags, cache state, and option values never enter public diagnostics or audit.

Authority boundary

This contract authorizes implementation, deterministic fixtures, and a reviewable pull request only. It does not authorize live provider validation, mutation, release publication, deployment, apply, or consumer migration.