Skip to content

Epic: scope every destructive Neo4j statement on the can:// id prefix; retire _module #50

Description

@rahlk

Spec: docs/design/specs/2026-09-02-prune-scope-on-can-id-prefix.md

Summary

Every statement that deletes stops matching on labels plus an internal _module property, and starts matching on the can:// id prefix of the application being pushed. The id is already a hierarchical path — can://<lang>/<app>/<file>/<type>/<signature> — so a prefix match is containment: language, application and module scoping all come from identity that already exists, instead of from a denormalized property and a hand-maintained label list kept correct by hand.

_module retires along with every index on it.

Why

Three defects in six weeks, all the same shape — a delete whose scope was expressed by something other than identity:

codeanalyzer-typescript#116 an unanchored wipe deleted the java and python graphs outright. Surfaced only as an OOM, because the delete was big enough to exhaust dbms.memory.transaction.total.max and roll back. A smaller foreign graph would have gone silently. Fixed in c1c27f3.
codeanalyzer-java#213 an unlabelled _module match deleted a sibling analyzer's nodes wherever a file key collided across languages. Fixed.
open, in all three the per-module purge is application-blind. _module is a bare project-relative path, so two applications in the same language sharing src/main/java/Foo.java delete each other's nodes for it.

The first two were fixed by adding labels to the match. That treats the symptom. The third is what remains afterwards — and it is the one a label cannot fix, because both applications' nodes carry identical labels. Only identity separates them.

What each analyzer does

  • scope both the per-module purge and the orphan prune on the application's can:// id prefix
  • drop _module from its projection and its graph catalog, and any index on it
  • carry a language-namespace marker label (JCanNode, PyCanNode, TSCanNode, JSCanNode) purely as an index anchor — Neo4j property indexes are label-scoped, so a prefix seek needs one
  • MAJOR graph-contract bump: a property is removed

No lockstep. Each analyzer changes its own writers and cuts its own release, because no analyzer reads another's nodes — which is the entire point of the change.

Not decided here

Left open in the spec §9: whether --eager becomes the universal gate on deletion (typescript already refuses to delete without it; java and python delete unconditionally), and whether any polyglot consumer needs a shared cross-language label alongside the per-language markers.

Order

Cheapest verification first: typescript (already prefix-scopes its prune and gates on --eager; the work is retiring _module and app-scoping the purge), then java (largest gap), then python. Docs follow the last analyzer.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions