graphql-hive / graphql-hive/console

Schema Proposals

Open
#6,777 2 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
483
Forks
145
Avg merge
2d 5h
Merged PRs (30d)
65

Description

# Hive Console — Schema Proposals

## 1. What the feature is — current functionality

Schema Proposals is "a Pull Request for your GraphQL schema": you propose *schema* changes (not implementation), discuss and approve them in Hive, and the proposal completes when all changes are published to the registry. It inverts the usual workflow — propose first, get approval, then implement and open a PR, and only then does the schema check give a green light.
The feature is opt-in (per-organization feature flag), since it changes how people check and publish schemas.

### 1.1 The intended workflow (from the RFC + lifecycle doc)

1. **Create.** A developer picks a target (typically the one representing `main`), creates a proposal, picks one or more subgraphs, and edits their SDL in an in-app editor (IDE-like: autocomplete, hover info, error highlighting). Saving runs the same pipeline as a Schema Check against the target's latest schema: composition status, linting (schema policy), and affected operations/clients for reviewer context.
2. **Review.** The author marks the draft "ready for review"; reviewers get notified, inspect changes and usage data, and comment / approve / request changes.
3. **Implement.** After approval, the developer implements the changes in code and opens a GitHub PR. CI runs `hive schema:check --schemaProposalId ...`; the check is linked to the proposal. If all changes in the check are covered by approved proposals, the check passes.
4. **Complete.** CI/CD runs `hive schema:publish`; Hive marks proposed changes as published. When all of a proposal's changes are published, its stage becomes **Implemented**.

Proposal stages: `DRAFT → OPEN → APPROVED → IMPLEMENTED`, with `CLOSED` possible at any point before implementation. `IMPLEMENTED` is system-set only.

### 1.2 Key design decisions already made (but at this stage, anything is open for discussion)

- **A proposal is composed of schema checks.** There is no separate "proposal version" entity — the check cursor *is* the proposal version. Every save creates a regular Schema Check linked to the proposal (`schema_checks.schema_proposal_id`).
- **Changes, not full schemas, are the unit of matching.** A check attached to a proposal stores a full unfiltered change list (`schema_checks.schema_proposal_changes` JSONB) so the SDL can be reconstructed by patching (`@graphql-inspector/patch`).
- **Approval requirements** (lifecycle doc): (a) each schema check in the proposal must pass (manually overridable); (b) the patch must not conflict with what has landed since; (c) the *patched latest schemas* must still compose — so an approved proposal can never produce a broken supergraph, even when implemented one subgraph at a time.
- **Matching is many-to-many.** A proposal's changes can be implemented across several checks/publishes; one check can match several proposals.
- **Background composition** runs per proposal (graphile-worker task), publishing status over a GraphQL subscription. Contracts are currently ignored by proposals.
- **Conflict handling**: only changes that *cannot be applied* by the patch step are flagged (requiring manual ignore)

### 1.3 What works today (as demoed in the Feb 2026 Loom)

UI at target level: proposals list (stage + author filters), create/editor view (Monaco with per-service tabs, diff view, prettify), proposal detail with tabs — Details (breaking/dangerous/safe changes), Schema (diff), Supergraph, Checks, Edit. Stage transitions via review mutation. CLI: `hive schema:check --schemaProposalId ` attaches a check to a proposal. Permissions: `schemaProposal:describe` / `schemaProposal:modify` for members and access tokens. Enabling: per-org `feature_flags.schemaProposals` or the `FEATURE_FLAGS_SCHEMA_PROPOSALS_ENABLED` env var (self-hosters); @jdolle also documented the `forceLegacyCompositionInTargets` flag recipe for gradual native-composition migration, which demos depend on.

---

## 2. Current state of the code

Everything lives in `graphql-hive/console`. Backend module: `packages/services/api/src/modules/proposals/` (~2,500 LOC incl. a 1,000-line `module.graphql.ts`). Frontend: `packages/web/app/src/components/target/proposals/` + `pages/target-proposal*.tsx` (~4,500 LOC). Background tasks: `packages/services/workflows/src/tasks/`.

### 2.1 GraphQL API surface

- **Queries:** `schemaProposals` (paginated, stage filter), `schemaProposal(id)`
- **Mutations:** `createSchemaProposal`, `reviewSchemaProposal` (stage transitions; coordinate-anchored reviews accepted but ignored), `replyToSchemaProposalReview` (**stub**)
- **Subscription:** `schemaProposalComposition` (SUCCESS/ERROR; the published `reason` isn't exposed on the event type)
- **Types:** `SchemaProposal` (incl. `checks`, `rebasedSchemaSDL`, `rebasedSupergraphSDL`, `compositionStatus`), `SchemaProposalReview`, `SchemaProposalComment`, and a ~80-member `SchemaChangeMeta` union so the UI can reconstruct inspector `Change` objects for diff/patch rendering.

### 2.2 Database

- `schema_proposals` (stage enum, title, description, author, target_id, composition status columns)
- `schema_proposal_reviews` (stage_transition NOT NULL, line_text / schema_coordinate — never written yet)
- `schema_proposal_comments` (**fully dormant** — no read/write path)
- `schema_checks.schema_proposal_id` + `schema_checks.schema_proposal_changes` (JSONB)
- `proposal_approved_changes` (added by PR #7960 — see below)

### 2.3 Integration points

There is no separate "proposal check" — proposals piggyback on the ordinary `schemaCheck` mutation via `SchemaCheckInput.schemaProposalId`. The publisher validates the proposal belongs to the target, computes a separate unfiltered diff for patching, stores it on the check, and kicks off background composition (patch latest subgraphs with each check's changes → compose → store status).

### 2.4 PR #7960 — approved-change tracking (open, needs finishing)

Implements @jdolle's "Schema Proposal Change Tracking" design: a `proposal_approved_changes` table (hash + change JSONB + proposal_id + nullable schema_version_id + target_id, with partial indexes for the three lookup paths). Hash-then-verify matching: `generateChangeHash` for an indexed prefilter, `isChangeEqual` for exact comparison (both from an **alpha prerelease** of `@graphql-inspector/compare-changes`). `schema_version_id IS NULL` encodes "approved but not implemented".

What it adds: on transition to APPROVED, a background task snapshots the proposal's changes into the table; on publish, a task (enqueued transactionally) matches the new version's changes against unimplemented approved changes and stamps `schema_version_id`; a new `SchemaChange.schemaProposalChangeDetails` field (proposal + implementedBy version) resolved through a DataLoader, with three resolution paths (proposal view / history view / unrelated-check matching); UI badges linking schema history ↔ proposals; the missing permission check on `schemaCheck.schemaProposalId`.

Rough edges to resolve before merge: `@todo rollback if this fails` on approval-job scheduling (proposal can end up APPROVED with zero tracked changes); approval-matching logic copy-pasted between api and workflows packages; leftover scaffold comments and raw `Error` throws in the new resolver; a lossy `path`-from-hash reconstruction hack; re-approval would insert duplicate rows (no unique constraint on `(proposal_id, hash)`); `proposal_approved_changes.proposal_id` lacks an `ON DELETE` clause (inconsistent with the cascade used elsewhere); a commented-out `checkRun` field silently changed the GitHub check response; no changeset.

### 2.5 Known bugs and functional holes (main branch)

1. **Comments/replies are entirely unimplemented** — table dormant, `replyToSchemaProposalReview` is a stub, comment-only reviews throw `Not implemented`, and `reviews.stage_transition` is NOT NULL so comment-only reviews are structurally impossible.
2. **Coordinate/line-anchored reviews unimplemented** — input accepted and ignored; columns never written.
3. **`rebasedSchemaSDL` doesn't actually rebase** (returns raw check SDL, `@todo patch schema changes onto latest`); **`rebasedSupergraphSDL` returns `''`**.
4. **`SchemaProposal.reviews` is broken server-side** — fetches paginated reviews then discards them.
5. **The org feature-flag gate in storage is a no-op**: `assertSchemaProposalsEnabled` checks the wrong flag (`appDeployments` instead of `schemaProposals`) *and* returns instead of throwing, and callers discard the result. Effective gating today is only permissions + nav-item visibility (routes themselves aren't gated).
6. **Pagination inconsistencies** — GraphQL defaults say `first: 30`, storage clamps to 20; "deal with pagination" `@todo`s throughout the frontend.
7. **Authorization smells** — `reviewProposal` asserts `describe` (not `modify`) even for stage transitions; authz check is skipped when a proposal doesn't exist.
8. **Housekeeping** — `updated_at` never updated (no trigger/app write); `comments_count` never incremented; a migration `name` field mismatched with its filename; `SchemaProposalStage` union hacks (`as any[]`); `throw new Error('uh oh')` placeholders in resolvers.
9. **Contracts are ignored everywhere** (composition task, `ContractCheck` resolver hardcodes null details).
10. **Test coverage is thin** — 4 integration scenarios on main (create/read/subscribe permission paths) + 1 end-to-end change-tracking test on the PR; nothing for reviews, stage transitions, pagination, rebasing, composition errors, or the flag gate; no unit or frontend tests.
11. **No user-facing docs** — the only operator doc is the env-var row in the server README.

## Possible next steps for a collaborator:

- [ ] Review PR #7960 and see if they can get it to the finish line
- [ ] Implement [@mish-elle’s suggestions](https://github.com/graphql-hive/console/issues/6777#issuecomment-5035503850):
- [ ] Approved proposals never reach "Implemented" - Maybe implement it already as part of PR #7960
- [ ] CLI fetch of proposed schemas - Great idea. We like it and in general, we were thinking to expose as much as possible from the functionality in API. That way you can also integrate other tools into that workflow in the future and get more flexibility and integration into teams existing processes and tools
- [ ] Comments / discussion - yes please ;)
- [ ] Conflict-detection gaps
- [ ] Notifications / webhooks — lifecycle events (created, edited, commented on, state changes) so responders can turn around quickly and to integrate the workflow into existing tools
- [ ] "New Service" directive parsing bug
- [ ] New Service doesn’t appear on the details tab
- [ ] Improve test coverage
- [ ] Docs
- [ ] Sync meeting with The Guild and think about next steps

## Possible roadmap and ideas
- [ ] Define and integrate with other workflows (schema promotions, experiments, feature flags, Contracts)
- [ ] Usage-data-informed review: show affected operations (Operations, Trusted Documents, Generated MCP Tools and generated REST Endpoints) and clients inline on each proposed change - Hive already has the data
- [ ] Notifications management
- [ ] Reviewer governance - Reviewer groups + ownership routing: Minimum approvers, required-reviewer groups with ≥1 approval per group, plus "SUBGRAPHOWNERS" — auto-require review from affected subgraph owners when a change touches shared surface (Query/Mutation namespace fields, shareable fields, entity keys)
- [ ] Registry previews: publish a proposal's composed supergraph to the CDN as a preview artifact + hosted GraphiQL/mock endpoint, so client teams can develop against the proposal before implementation
- [ ] Improvements to SDL editor (autocomplete, hover, validation, multi-subgraph, add-new-subgraph, on-demand or on-change composition, lint panel)
- [ ] Collaborative editor
- [ ] Revisions as commits with Markdown summaries
- [ ] Inline comments with @mentions
- [ ] Stale-approval dismissal as opt-in: Auto-dismiss of approvals when the proposal changes after sign-off, with a bot comment explaining what changed.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.