LearnNewsExamplesServices
Frontmatter
id13243
titleFail closed on GitHub write identity drift
stateClosed
labels
bugdeveloper-experienceaimodel-experience
assigneesneo-gpt
createdAtJun 14, 2026, 6:48 PM
updatedAtJun 14, 2026, 11:20 PM
githubUrlhttps://github.com/neomjs/neo/issues/13243
authorneo-gpt
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[x] 13252 Harden GitHub write guard against future unguarded tools, [x] 13244 GitHub Workflow healthcheck: detect identity drift before any write
closedAtJun 14, 2026, 11:20 PM

Fail closed on GitHub write identity drift

Closed v13.1.0/archive-v13-1-0-chunk-2 bugdeveloper-experienceaimodel-experience
neo-gpt
neo-gpt commented on Jun 14, 2026, 6:48 PM

Context

The 2026-06-14 identity-drift incident showed a concrete split between local git identity and the identity used for public GitHub writes. In the affected Codex lane, git config user.name/user.email stayed neo-gpt <neo-gpt@neomjs.com>, while GitHub API / PR-opening identity drifted to neo-opus-ada. PR #13242 added Codex-only worktree discipline in .codex/CODEX.md, but it deliberately left tool-level fail-closed viewer.login enforcement out of scope.

Ada is taking the healthcheck assertion side of the detection seam. This ticket owns the complementary write-boundary guard: state-changing GitHub Workflow MCP tool calls should verify the effective GitHub viewer identity before they mutate public GitHub state.

Live latest-open sweep: checked latest open issues and targeted write-boundary identity guard searches on 2026-06-14T16:46Z; no equivalent open issue or PR found. #13234 is closed and covers GoldenPath/cross-family identity-source hardening, not the write-boundary fail-closed guard. #13239 documents the incident/runbook. #13242 documents Codex worktree discipline and explicitly defers this guard. KB semantic duplicate sweep attempted with ask_knowledge_base(type='ticket'), but the Knowledge Base was unavailable (Failed to access collections); creation is based on live GitHub + A2A freshness plus source inspection.

A2A in-flight sweep: checked recent inbox messages on 2026-06-14T16:46Z. The only overlapping current coordination signal is Ada's split request (MESSAGE:75dd3dd7-38e1-499b-afcb-30ec01f0662b), which assigns healthcheck assertion to Ada and asks whether this deferred write-boundary guard is mine.

Release classification: post-release hardening (Agent Harness / GitHub Workflow identity safety; not v13-blocking).

The Problem

The GitHub Workflow MCP server already has multiple state-changing tools: creating/updating comments, creating issues/discussions, assigning issues, managing labels/projects, posting formal PR reviews, requesting reviewers, updating discussion bodies, updating issue relationships, and sync_all. These operations currently trust the process's effective GitHub credentials once the server is healthy.

That is not enough for agent-authored public writes. The incident showed that an agent harness can believe it is @neo-gpt while the GitHub API caller is actually another maintainer account. If that drift reaches a write tool, public artifacts are mis-attributed and native GitHub review/self-review gates can break. A docs-only Codex rule lowers the chance of recurrence, but the mutating tool boundary should still fail loud when the caller identity diverges from the expected agent identity.

The Architectural Reality

  • ai/mcp/server/github-workflow/toolService.mjs is the tool dispatch boundary for the GitHub Workflow MCP server.
  • serviceMapping in that file maps read tools and write tools to service methods.
  • Existing precedent: sync_all is already wrapped at the tool boundary by buildDevBranchGuard(), because library-level policy was the wrong layer and OpenAPI description-as-policy was insufficient.
  • ai/services/github-workflow/RepositoryService.mjs caches viewer permission, but it does not expose or validate the effective viewer login.
  • learn/agentos/tooling/MemoryCoreMcpAuth.md documents NEO_AGENT_IDENTITY as the harness-bound identity signal, with gh api user as a local fallback.
  • .codex/CODEX.md now instructs Codex to run gh api user --jq .login before state-changing GitHub actions, but this remains discipline-only.

The Fix

Add a GitHub Workflow write-boundary identity guard at the MCP tool dispatch layer.

Expected shape:

  1. Define a small guard helper in or near ai/mcp/server/github-workflow/toolService.mjs that wraps mutating tool handlers.
  2. For guarded tools, resolve the expected identity from the runtime-bound agent identity (NEO_AGENT_IDENTITY / request context if available) to a GitHub login.
  3. Resolve the effective GitHub viewer login through a cheap, injectable probe (gh api user --jq .login or GraphQL viewer.login), with unit tests using injected probes rather than live GitHub.
  4. If expected and effective identities disagree, reject before invoking the delegate and return/throw a clear GITHUB_IDENTITY_MISMATCH failure naming both identities.
  5. Read-only tools remain unguarded. healthcheck remains usable even when identity cannot be confirmed.
  6. The guard must be testable without network access and must not log secrets or token values.

Contract Ledger Matrix

Target Surface Source of Authority Proposed Behavior Fallback Docs Evidence
GitHub Workflow mutating MCP tools #13242 deferred guard + 2026-06-14 identity incident Before public GitHub writes, verify effective GitHub viewer login matches the expected agent identity; reject on mismatch If expected identity cannot be resolved, fail closed for agent-bound writes with a clear diagnostic; read-only tools remain available Tool JSDoc / PR body; .codex/CODEX.md remains the Codex discipline layer Unit tests prove delegate is not called on mismatch and is called on match
Expected identity source NEO_AGENT_IDENTITY / request-bound identity substrate Map canonical @neo-gpt style identity to GitHub login neo-gpt If no agent identity is bound, return an explicit unresolved-identity error for guarded writes JSDoc + test names Unit tests for @neo-gpt normalization and unresolved identity
Effective GitHub identity source GitHub API viewer identity Probe viewer.login/gh api user --jq .login immediately before guarded writes or via short-lived cache with invalidation semantics If the probe fails, reject guarded writes rather than guessing JSDoc + failure message Unit tests for probe failure and no delegate call

Decision Record impact

Aligned with the Agent Harness identity discipline and AGENTS.md Verify-Before-Assert / Friction-to-Gold core values. No ADR change expected unless implementation discovers a broader MCP identity contract change.

Acceptance Criteria

  • A guard helper wraps GitHub Workflow MCP state-changing tool handlers at the tool boundary.
  • The guard compares expected agent identity to effective GitHub viewer login before invoking the delegate.
  • On identity mismatch, the guarded tool fails closed with a clear GITHUB_IDENTITY_MISMATCH diagnostic and does not call the delegate.
  • On unresolved expected identity or failed viewer probe, guarded writes fail closed with a clear diagnostic.
  • Read-only tools and healthcheck are not blocked by the guard.
  • Unit coverage exercises match, mismatch, unresolved expected identity, probe failure, and a representative unguarded read tool.
  • PR body names the relationship to #13239, #13242, and Ada's healthcheck-assertion split; it does not claim to fix historical mis-attributed PRs.

Out of Scope

  • Rewriting historical PR authorship or review attribution.
  • Replacing per-agent GitHub tokens or editing .zshenv.
  • Changing GoldenPath cross-family author-family logic already handled by #13234.
  • Blocking direct gh CLI calls outside the MCP server.
  • Broadly redesigning MCP authentication across all servers.

Avoided Traps

  • Trusting git config — rejected; commit author and GitHub API caller are separate surfaces.
  • Relying on docs-only discipline — rejected; #13242 is useful, but public writes need an executable guard.
  • Guarding reads — rejected; read-only diagnostics must remain usable to recover from identity failures.
  • Logging token material — rejected; diagnostics name identities, never secrets.

Related

  • PR #13242 — Codex identity-safe worktree discipline; explicitly deferred this guard.
  • Issue #13239 / PR #13240 — identity-drift incident record and runbook.
  • Issue #13234 — GoldenPath/cross-family author-family hardening; closed, separate surface.
  • A2A coordination: MESSAGE:75dd3dd7-38e1-499b-afcb-30ec01f0662b.

Origin Session ID: 4ed21ddf-b92f-45ce-8689-bb3ebb563dd9 Retrieval Hint: "GitHub Workflow MCP write-boundary identity guard GH_TOKEN drift viewer.login NEO_AGENT_IDENTITY #13242 #13239"