Repository navigation
Enforce Core/System field protection on update_schema removes and renames, validate rename grammar before persisting - #2304
Merged
mstomar125 merged 1 commit intoAug 27, 2026
Conversation
…ames, and validate rename grammar before persisting (closes #2194, #2195, #2208, #2213) handle_update_schema's remove_fields deleted schema fields by name alone, and NodeService::rename_schema_field/update_schema_field_friendly_name rekeyed and relabeled fields by name alone — neither consulted SchemaField::protection despite SchemaNode::can_delete_field/ can_modify_field existing for exactly this. A Core-protected field like task.status (or any System-protected field) could be permanently removed or rekeyed via a single update_schema call, corrupting a core schema with no recovery path through the API. Add a protection-level guard in two places: handle_update_schema's Phase 0 (before any mutation runs, checked against the pre-mutation schema snapshot) and the NodeService primitives themselves, so a future second caller of rename_schema_field/update_schema_field_friendly_name can't reintroduce the bypass by skipping the upstream check. Only User-protected fields may be removed, renamed, or relabeled. Also move rename destination grammar validation (validate_schema_field_name) into that same Phase 0 pass. Previously a rename ran in Phase 1 (migrating every node's property data and rewriting the schema immediately) and grammar was only checked in Phase 2, after the migration had already committed — so a malformed destination name (e.g. containing a space) would be durably persisted to the schema and every node instance before the call reported failure, wedging the schema until manually renamed back. Adds regression tests covering: Core- and System-protected field removal rejection, User-protected fields remaining removable/renameable on core schemas, whole-batch rejection when one entry is protected, rejection of both identity renames and friendly-name-only relabels of protected fields, and the grammar-before-persist ordering (schema and node data both verified untouched by a rejected rename, with a subsequent valid update on the same schema still succeeding). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This was referenced Aug 27, 2026
mstomar125
deleted the
worktree-issue-2194-2195-2208-2213-schema-protection
branch
August 27, 2026 07:21
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #2194, #2195, #2208, #2213 — a real cluster of bugs in
update_schema's field-removal/rename path.handle_update_schema'sremove_fieldsdeleted schema fields by name alone (fields.retain), with noSchemaProtectionLevelcheck.NodeService::rename_schema_fieldandupdate_schema_field_friendly_namerekeyed/relabeled fields by name alone, also with no protection check — even thoughSchemaNode::can_delete_field/can_modify_fieldexist specifically to guard this.Together, a single
update_schemacall could permanently remove or rename a Core- or System-protected field (e.g.task.status) on a core schema, migrating/losing every existing node's data with no recovery path through the API (#2194, #2195, #2213).Separately,
rename_fieldsexecuted the rename (migrating every node's property data and rewriting the schema) in Phase 1, and only validated the destination name's grammar in Phase 2 — after the migration had already committed. A malformed destination (e.g. containing a space) would be durably persisted to the schema and every node instance before the call reported failure, wedging the schema until manually renamed back (#2208).Fix
handle_update_schema's Phase 0, checked against the pre-mutation schema snapshot, before any mutation executes:remove_fieldsandrename_fields(source field) are rejected if the field is notUser-protected.NodeServiceprimitives (rename_schema_field,update_schema_field_friendly_name) as defense-in-depth, so a future second caller can't reintroduce the bypass by skipping the upstream check.validate_schema_field_name) into that same Phase 0 pass, before Phase 1 runs, so a malformed name is rejected before any migration or schema rewrite happens — never partially/durably applied.Tests
New regression tests cover:
Test plan
bun run test:all— all green (1215/1215 core unit tests, full frontend/skill/rust suite)bun run quality:fix— clean, no warningstest:all+nodespacedbuild + e2e + Tauri-seam tests) — passed🤖 Generated with Claude Code