feat(vitess): treat VSchema deletions as unsafe changes - #1075
Conversation
There was a problem hiding this comment.
Pull request overview
This PR extends SchemaBot’s safety gating for Vitess/PlanetScale by classifying VSchema structural removals (vindex definitions, table routing entries, and column-vindex associations) as unsafe changes, so they’re disclosed in plans and blocked on apply unless explicitly acknowledged with --allow-unsafe.
Changes:
- Add structural VSchema diffing (
pkg/vschema.Deletions) to detect removals and fail closed on unparseable VSchema. - Persist detected removals in PlanetScale plan metadata and surface them via
PlanResponse.UnsafeChanges()alongside unsafe table DDL. - Update CLI gating to block applies on
UnsafeChanges()(not just lint error severity), plus docs + E2E coverage for the end-to-end unsafe gate.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| pkg/vschema/deletions.go | Implements structural deletion detection between current/desired VSchema. |
| pkg/vschema/deletions_test.go | Unit tests covering deletion detection and fail-closed parsing behavior. |
| pkg/engine/planetscale/plan.go | Records VSchema deletions into plan metadata during PlanetScale planning. |
| pkg/engine/planetscale/plan_vschema_test.go | Validates PlanetScale metadata encoding/decoding for VSchema deletions. |
| pkg/cmd/commands/rollback.go | Adjusts rollback output path for unsafe warnings (now always evaluated). |
| pkg/cmd/commands/apply.go | Gates apply on UnsafeChanges() and adjusts unsafe-warning display behavior. |
| pkg/apitypes/vschema.go | Adds metadata encoding/decoding and unsafe-change projection for VSchema deletions. |
| pkg/apitypes/vschema_test.go | Verifies unsafe-change aggregation includes VSchema deletions and fails closed on corrupt metadata. |
| pkg/apitypes/apitypes.go | Extends PlanResponse.UnsafeChanges() to include VSchema-derived unsafe changes. |
| e2e/local/vitess_test.go | E2E test proving plan disclosure + apply refusal without --allow-unsafe + success with it. |
| docs/namespaces.md | Documents that VSchema removals are unsafe and require --allow-unsafe. |
| docs/lint-and-safety-levels.md | Defines what “unsafe” means for VSchema changes and how removals are detected. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Removing a vindex, a table routing entry, or a column-vindex association changes Vitess query routing the moment the VSchema is applied: the vindex stops being used for routing, lookup rows stop being maintained, and queries that depended on it can fail or scatter. That is as dangerous as destructive DDL, but VSchema changes previously bypassed the unsafe gate entirely. The engine now compares the current and desired VSchema structurally (pkg/vschema Deletions, via the Vitess keyspace proto) and records any removals in plan change metadata. UnsafeChanges() surfaces them alongside unsafe table changes, so the existing PR comment and CLI allow-unsafe opt-in gates apply. Detection fails closed: an unparseable VSchema fails the plan, and undecodable deletion metadata reports an unsafe change rather than passing silently. Additions-only VSchema changes remain safe. The CLI apply gate now keys on UnsafeChanges() rather than lint error severity, matching the webhook gates. This closes two CLI gaps: VSchema removals carry no lint finding, and the DROP-fallback unsafe classification never consulted lint either. The plan comment appends namespace-level VSchema removals to the Issues section in the sharded view too, where the per-shard unsafe derivation would otherwise omit them. A new preview scenario (Vitess plan with an unsafe VSchema removal) renders the section in TEMPLATES.md. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
d77c562 to
1b92832
Compare
Tolerate unknown VSchema fields in deletion detection so a newer Vitess does not fail planning, keep the fail-closed decode reason on one line for CLI rendering, and have rollback disclose unsafe changes without claiming an --allow-unsafe flag it does not have. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
🤖 Review findings - created by Kiran's code review agent - for schemabot/pull/1075, 24f7071. Verdict: 2 findings — 1 blocking (column-vindex reassociation evades detection), 1 general suggestion (vindex type/params changes not flagged). Blocking
General suggestions
The one thing that could have broken, verifiedThe riskiest question: can the new structural-removal detection in Verified correct
This review was generated by Claude Code (claude-sonnet-5). |
A column-vindex association is identified by the vindex name plus the ordered columns it covers, not the name alone. Moving a vindex to different columns (or reordering a multi-column vindex) removes the old columns' association — routing for queries on them changes immediately — so it now gets the same unsafe disclosure as removing the vindex from the table. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
🤖 Both findings addressed:
Reply by Claude (Fable 5). |
Removing a vindex, a table routing entry, or a column-vindex association changes Vitess query routing the moment the VSchema is applied: the vindex stops being used for routing, lookup rows stop being maintained, and queries that depended on it can fail or scatter. Deleting a vindex is as dangerous as dropping a table, but VSchema changes previously bypassed the unsafe gate entirely.
pkg/vschema.Deletionsstructurally diffs the current and desired VSchema (via the Vitess keyspace proto) and reports removed vindex definitions, table routing entries, and column-vindex associations. Lookup-family vindex removals name the backing table whose rows go stale.PlanResponse.UnsafeChanges()surfaces them alongside unsafe table changes, so the existing PR comment and CLI--allow-unsafegates apply.schemabot applygated on lint error severity (HasErrors()), which never fires for VSchema removals or the DROP-fallback unsafe classification. It now gates onUnsafeChanges(), matching the webhook gates. No weakening: engines already mark every lint-error change unsafe, soUnsafeChanges()is a superset.Warnings by removal kind
Each detected removal renders in the⚠️ Issues section as
<keyspace>/vschema.jsonwith one of these reasons (sample identifiers):email_lookupis removed: Vitess immediately stops maintaining its rows in backing tableemail_lookup, queries routed through it can fail or scatter, and the lookup data goes staleemail_lookupis removed: Vitess immediately stops maintaining its lookup rows and queries routed through it can fail or scatterregion_idxis removed: Vitess immediately stops using it for routing and lookups, and queries that depend on it can fail or scatterordersis removed from the VSchema: Vitess loses its routing entry and queries against it can failusersno longer uses vindexemail_lookup: routing for queries on its columns changes immediately and lookup rows stop being maintainedusersmoves vindexemail_lookupfrom columns (email) to (phone): routing for queries on the old columns changes immediately and lookup rows for them stop being maintainedDocs:
lint-and-safety-levels.md(what unsafe means for VSchema) andnamespaces.md(cross-reference). TEMPLATES.md gains a preview scenario (Vitess plan with an unsafe VSchema removal). E2E coverage exercises the full gate: plan disclosure, refusal without the flag, and completion with it.Opened by Claude (Fable 5).