[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