Skip to content

chore(hub): drop vestigial settings IS NOT NULL guards on spaces - #2221

Open
wa0x6e wants to merge 4 commits into
masterfrom
fix/2220
Open

chore(hub): drop vestigial settings IS NOT NULL guards on spaces#2221
wa0x6e wants to merge 4 commits into
masterfrom
fix/2220

Conversation

@wa0x6e

@wa0x6e wa0x6e commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Drops the four vestigial spaces.settings IS NOT NULL predicates and tightens spaces.settings to NOT NULL in the schema. The NULL state is unreachable: the sequencer write path rejects spaces without settings.name and writes settings in the same INSERT. Verified on prod: 0 of 91,766 spaces have NULL settings.

Closes #2220

Deployment note

Prod requires a manual ALTER TABLE spaces MODIFY settings JSON NOT NULL; — safe to run before or after deploy since no NULL rows exist.

Test plan

  • Behavior is unchanged for all existing rows: no space has NULL settings, so the dropped predicates filtered nothing

Sequencer write path rejects spaces without settings.name and writes
settings in the same INSERT, so NULL settings is unreachable. Verified
on prod: 0 of 91,766 spaces have NULL settings. Schema tightened to
NOT NULL to match.

Closes #2220

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR tightens the Hub’s spaces.settings column to be non-nullable (matching the current invariant enforced by the sequencer write path) and removes now-redundant settings IS NOT NULL SQL predicates from several GraphQL operations.

Changes:

  • Update Hub schema (schema.sql) to declare spaces.settings JSON NOT NULL.
  • Remove spaces.settings IS NOT NULL guards from space/proposal lookups in votes and proposal resolvers.
  • Simplify subscriptions query filtering by dropping the settings IS NOT NULL predicate (retaining the existing dynamic WHERE building approach).

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
apps/hub/src/helpers/schema.sql Makes spaces.settings non-nullable in the Hub schema definition.
apps/hub/src/graphql/operations/votes.ts Removes redundant settings IS NOT NULL filtering from space/proposal fetches.
apps/hub/src/graphql/operations/subscriptions.ts Drops the settings IS NOT NULL clause while keeping dynamic filtering via WHERE 1=1.
apps/hub/src/graphql/operations/proposal.ts Removes redundant settings IS NOT NULL filtering from the proposal fetch query.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@chai3-bot chai3-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

apps/sequencer/test/schema.sql still declares spaces.settings as nullable. The sequencer integration workflow initializes MySQL from that file (.github/workflows/sequencer-test.yml), so the write path that is being used to justify this invariant is still tested against the old schema and would not catch a future insert of NULL settings.

Please update that schema to settings JSON NOT NULL as well, so both schema definitions agree and the sequencer integration tests exercise the production constraint.

@tony8713

Copy link
Copy Markdown
Collaborator

Tested this locally end to end (MySQL 8.0, applied this PR's schema.sql, seeded a space + proposal + vote + subscription, and ran the three actual resolvers, not just the SQL by hand). All good, nothing blocking.

What I checked

  • Write path really is the only way NULL could appear, and it can't. addOrUpdateSpace (apps/sequencer/src/helpers/actions.ts:25) is the sole INSERT INTO spaces; it early-returns on !settings?.name and always writes settings: JSON.stringify(...), never SQL NULL. Every other spaces writer is an UPDATE that never touches settings. So the IS NOT NULL guards were genuinely dead, matching your prod check.
  • Real resolvers pass on the NOT NULL schema. proposal.ts (WHERE p.id = ?), subscriptions.ts (WHERE 1=1 ${queryStr}), and votes.ts (both dropped guards in the space + proposal sub-queries) all return the seeded rows, with the nested space name and proposal title resolving through votes.ts:67 and votes.ts:109. buildWhereQuery prepends AND, so WHERE 1=1 ${queryStr} composes correctly whether or not a filter is passed.
  • Constraint is enforced. INSERT ... settings = NULL is rejected with ER_BAD_NULL_ERROR under the new schema, consistent with the write path.
  • Only behavioral delta is confirmed harmless. On a nullable-column copy, old-vs-new diverge for exactly one case: a NULL-settings row (old filtered it, new returns it). Since no such row exists and can no longer be created, behavior is unchanged for all real rows.

Minor

  • WHERE 1=1 here matches the house style in follows.ts, proposals.ts, users.ts, etc., so it reads consistently. (The pre-existing WHERE 1 = 1 in votes.ts:47 is the odd one out, not worth touching here.)

One deployment note to be explicit about: the guard-drop and the ALTER are independent (code doesn't rely on the constraint, constraint doesn't rely on the code), so the ordering really is free as you said. Nothing else came up.

@wa0x6e
wa0x6e requested review from ChaituVR and bonustrack July 11, 2026 10:38

@chai3-bot chai3-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed at ab5057273796cdbaf781bdca29d09cda92655de8, including the failed CI check. No code findings.

The lint-build-test job passed install, build, typecheck, lint, and unused-code checks, then failed only because the unrelated UI test apps/ui/src/helpers/utils.test.ts:320 timed out after 10 seconds while resolving boorger.shib. This PR changes only Hub query/schema files and the Sequencer test schema; the dedicated Hub and Sequencer test workflows both pass. This appears to be a flaky external name-resolution timeout, not a regression from this PR. A maintainer will need to rerun it because this bot lacks rerun permission.

@tony8713 tony8713 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. Verified locally end-to-end (see my earlier comment): the guards were genuinely dead, all three resolvers pass on the NOT NULL schema, the constraint is enforced, and behavior is unchanged for every real row. The CI red was a flaky unrelated apps/ui test (live mainnet RPC lookup for boorger.shib timing out) — reran and it's green now.

@ChaituVR ChaituVR left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utAck, not sure about schema change on prod

id VARCHAR(64) NOT NULL,
name VARCHAR(64) NOT NULL,
settings JSON,
settings JSON NOT NULL,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is same on prod DB? 🤔

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this will need a schema update on the prod database

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deploy request: https://app.planetscale.com/snapshot/snapshot-hub/deploy-requests/22

@bonustrack confirm, then we can merge

Already changed on testnet

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

chore(hub): drop vestigial settings IS NOT NULL guards on spaces

5 participants