Problem
The Neo4j label PyCFGNode names a control-flow concept, but it holds every entry of a
callable's body{} at every level: call nodes from L1, statement/return/branch/
entry/exit from L3, and formal_in/formal_out/actual_in/actual_out from L4.
Most of those are not CFG vertices. A nested call is explicitly a dataflow satellite that
never joins the spine — .claude/SCHEMA_DECISIONS.md records that decision — and param
vertices are not control flow at all. Since #120 the same node also carries call-site
detail, so the label is now doubly inaccurate.
It is also a projection divergence: analysis.json calls the container body{} while the
graph calls its contents CFG nodes, so the two projections use different words for one
thing.
Scope boundary
Renames the node label and its containment edge. Does not rename the genuinely
control-flow relationships (PY_CFG_NEXT, PY_CDG, PY_DDG) or the cfg/cdg/ddg
edge lists on the callable — those are correctly named. Does not change any emitted fact.
Goals
Caveats and known risks
- Breaking graph change: nodes in an existing database strand under the old label, and the
pycfgnode_id uniqueness constraint renames with it. Needs a stated migration, not just
a version bump.
docs/handoff/schema.neo4j.json must NOT be regenerated — it is a frozen bundle pinned
to codeanalyzer-python==1.0.1.
CHANGELOG.md entries describing past releases must NOT be rewritten; add a new entry.
- Risk of confusion with "body" meaning source text (
get_method_body returns a span
slice). Judged acceptable — body{} is already the JSON field name.
Definition of done
- No
PyCFGNode or PY_HAS_CFG_NODE in codeanalyzer/, test/, README.md, CLAUDE.md
or the root schema.neo4j.json
--emit neo4j produces :PyBodyNode merged on the same global ordinal id as before
PY_CFG_NEXT and friends still connect body nodes, with only the endpoint label changed
- Full suite green
Problem
The Neo4j label
PyCFGNodenames a control-flow concept, but it holds every entry of acallable's
body{}at every level:callnodes from L1,statement/return/branch/entry/exitfrom L3, andformal_in/formal_out/actual_in/actual_outfrom L4.Most of those are not CFG vertices. A nested call is explicitly a dataflow satellite that
never joins the spine —
.claude/SCHEMA_DECISIONS.mdrecords that decision — and paramvertices are not control flow at all. Since #120 the same node also carries call-site
detail, so the label is now doubly inaccurate.
It is also a projection divergence:
analysis.jsoncalls the containerbody{}while thegraph calls its contents CFG nodes, so the two projections use different words for one
thing.
Scope boundary
Renames the node label and its containment edge. Does not rename the genuinely
control-flow relationships (
PY_CFG_NEXT,PY_CDG,PY_DDG) or thecfg/cdg/ddgedge lists on the callable — those are correctly named. Does not change any emitted fact.
Goals
PyCFGNode→PyBodyNode(node label and merge label)PY_HAS_CFG_NODE→PY_HAS_BODY_NODEPY_CFG_NEXT,PY_CDG,PY_DDG,PY_PARAM_IN,PY_PARAM_OUT,PY_SUMMARY,PY_RESOLVES_TO_cfg_ref→_body_refinneo4j/project.py, so the code stops saying CFG where it means bodyschema.neo4j.jsonregenerated;README.md,CLAUDE.mdand.claude/SCHEMA_DECISIONS.mdupdatedcodeanalyzer-typescriptalready usesTSBodyNode/TS_HAS_BODY_NODE; this adopts that name rather than coining a thirdCaveats and known risks
pycfgnode_iduniqueness constraint renames with it. Needs a stated migration, not justa version bump.
docs/handoff/schema.neo4j.jsonmust NOT be regenerated — it is a frozen bundle pinnedto
codeanalyzer-python==1.0.1.CHANGELOG.mdentries describing past releases must NOT be rewritten; add a new entry.get_method_bodyreturns a spanslice). Judged acceptable —
body{}is already the JSON field name.Definition of done
PyCFGNodeorPY_HAS_CFG_NODEincodeanalyzer/,test/,README.md,CLAUDE.mdor the root
schema.neo4j.json--emit neo4jproduces:PyBodyNodemerged on the same global ordinal id as beforePY_CFG_NEXTand friends still connect body nodes, with only the endpoint label changed