[CompatGuard] BREAKING: GraphQL User.email removed — add @deprecated alias per MR !91 precedent
## CompatGuard Owner Issue — Contract 2 **Severity:** 🔴 HIGH — BREAKING **MR:** [!4 — Proof: dynamic OSS contract gate](https://gitlab.com/gitlab-ai-hackathon/transcend/34816704/-/merge_requests/4) **Pipeline:** [#2623964361](https://gitlab.com/gitlab-ai-hackathon/transcend/34816704/-/pipelines/2623964361) **Owners notified:** `@frontend-team`, `@mobile-team`, `@api-platform` --- ### What broke `samples/shopstack/head/api/schema.graphql` removes `email: String!` from `type User` and replaces it with `primaryEmail: String!`. No `@deprecated` directive, no alias, no version notice. Two runtime GraphQL consumers fail immediately: | Consumer | Owner | Failure mode | |---|---|---| | `frontend/ProfileQuery.graphql` | `@frontend-team` | Server rejects query: `Cannot query field "email" on type "User"` — hard validation error, no partial data | | `mobile/UserSettings.graphql` | `@mobile-team` | Same hard rejection — mobile clients with compiled/cached queries break on every settings screen load | > **GraphQL semantics:** Unlike REST, GraphQL servers perform schema validation at query execution time. A missing field is a hard rejection — there is no silent `undefined` path. Every client that has compiled or cached a query selecting `email` breaks immediately on schema deployment. ### Prior incident / project precedent > 🔴 **Merged MR `!91`** — *Keep deprecated GraphQL user fields for mobile clients* — HIGH severity > This MR established the project-specific precedent: GraphQL `User` field removals require a `@deprecated` alias for at least one release before hard removal. Mobile clients cannot be force-updated. That precedent is being violated here. > Prior MR URL: `https://gitlab.example/shopstack/-/merge_requests/91` ### Dynamic replay result Both probes **FAILED**: - Probe 2-A: `User.email` not in `{ id, primaryEmail, displayName }` → ❌ (hard GraphQL validation error) - Probe 2-B: Same → ❌ (mobile clients break on every settings screen load) ### Required actions (P0 — must complete before MR !4 merges) - [ ] `@api-platform` — Restore `email: String! @deprecated(reason: "Use primaryEmail. Will be removed in next major release.")` on GraphQL `User` type - [ ] `@mobile-team` — Confirm mobile clients can tolerate the deprecation period; coordinate hard removal timeline - [ ] `@frontend-team` — Update `frontend/ProfileQuery.graphql` to select `primaryEmail` during the alias window ### Suggested tests ```bash # Static SDL validation — no server needed: node -e " const { buildSchema } = require('graphql'); const sdl = require('fs').readFileSync('samples/shopstack/head/api/schema.graphql','utf-8'); const fields = Object.keys(buildSchema(sdl).getType('User').getFields()); if (!fields.includes('email')) { console.error('FAIL: email absent'); process.exit(1); } console.log('PASS'); " npx jest tests/compat/graphql.user.email.test.ts --no-coverage ``` Minimum assertions: ```ts it("User.email is present and @deprecated in head schema", () => { const field = schema.getType("User").getFields()["email"]; expect(field).toBeDefined(); expect(field.deprecationReason).toMatch(/primaryEmail/); // MR !91 precedent check }); it("ProfileQuery.graphql validates against head schema", () => { expect(validate(schema, parse(profileQuerySrc))).toHaveLength(0); }); it("UserSettings.graphql validates against head schema", () => { expect(validate(schema, parse(userSettingsQuerySrc))).toHaveLength(0); }); ``` ### Orbit graph gaps (follow-up) - `frontend/ProfileQuery.graphql` and `mobile/UserSettings.graphql` are absent from the Orbit `owners` flat dict — add CODEOWNERS or Orbit owner annotations - No pipeline entries for these files in the Orbit `pipelines` dict — index against `web-unit`, `web-e2e`, `mobile-integration` --- *Auto-created by CompatGuard MR Gatekeeper · Pipeline #2623964361 · Commit 84728869*
issue