Repository navigation
Conversation
`geo.entities.delete(...)` and the deprecated `Graph.deleteEntity(...)` now throw `ProtectedEntityError` when the target is the space's `page`/home entity or one of the Avatar / Cover images that entity points at. Those entities carry the space itself rather than content in it, and a caller iterating a space's entities cannot tell them apart from anything else it is deleting, so one stray loop takes the space's name, description and profile image with it. Skill-level guards do not help here: an agent that writes its own delete loop bypasses them entirely, so the invariant belongs where the ops are built. Anchors resolve inside the request `deleteEntity` already makes, so the guard costs no extra round-trip on a path that is called once per entity. The error carries `entityId`, `spaceId` and a `reason` of 'page' | 'avatar' | 'cover' so a bulk caller can skip anchored entities instead of aborting. Pass `deleteAnchored: true` to delete one deliberately. `ProtectedEntityError`, `anchoredEntityIds()` and `spaceAnchorsQueryField()` are exported so callers can filter a work list before deleting anything. BEHAVIOR CHANGE: a call that previously deleted one of these entities now throws until `deleteAnchored: true` is added.
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.
Follow-up to the personal-space wipe of many curators . This implements the SDK-level guard from the deletion-safety recommendations.
Why this belongs in the SDK
The incident deleted nothing through a skill. It ran a hand-authored TypeScript loop calling the delete path directly, in a different repo. Every skill-level protection - anchored-entity checks, dry-run expiry, the
publishOpscircuit-breaker - was bypassed because the caller read the docs and then wrote its own version without them.An agent that freelances its own loop bypasses 100% of caller-side protection, so the invariant has to live where the ops are built.
What it does
geo.entities.delete(...)and the deprecatedGraph.deleteEntity(...)now throwProtectedEntityErrorwhen the target is a space's anchored entity: itspage/home entity, or the Avatar or Cover image that entity points at.These are the entities a caller iterating a space cannot tell apart from content - they're ordinary Image entities and one Page entity sitting in the same result set as everything else. That's why one stray loop takes the space's identity with it.
API
ProtectedEntityErrorcarriesentityId,spaceId, and areasonof'page' | 'avatar' | 'cover', so a bulk caller can skip an anchor and keep going rather than aborting at entity 400 of 11,000:anchoredEntityIds()andspaceAnchorsQueryField()are exported too, so acaller can resolve the anchor set up front and filter a work list before
deleting anything.
No extra round-trip
The anchors resolve inside the query
deleteEntityalready makes. This matters: the failure mode is a loop over thousands of entities, and a guard that doubled the request count is a guard people switch off.Testing
protected-entities.test.ts, plus a block indelete-entity.test.ts), 579 total passing,biomeandtscclean.deleteAnchored: trueWhat this does not cover
Recommendation 3 - the bulk-destruction threshold.
deleteEntitysees one entity at a time, so it structurally cannot know it's the 400th call in a runaway loop. That check belongs where the full ops array is visible (personalSpaces.publishEdit/daoSpaces.proposeEdit), mirroring thepublishOps>50-destructive circuit-breaker. Separate PR, happy to open it.