Skip to content

fix: comment-frame ids draw from the node-id space (no pin-id collision) (#203)#210

Merged
apotema merged 1 commit into
mainfrom
fix/comment-id-namespace-203
Jun 9, 2026
Merged

fix: comment-frame ids draw from the node-id space (no pin-id collision) (#203)#210
apotema merged 1 commit into
mainfrom
fix/comment-id-namespace-203

Conversation

@apotema

@apotema apotema commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Closes #203.

commentNodeId used base 0x100_0000_0000 (2^40), which is INSIDE the pin id space (pinId = node_id<<30 | hash reaches 2^40 at node id ~1024), so a comment frame could collide with a pin in a 1024+ node graph (rendering-only — persisted .flow.jsonc was fine).

Fix — shared node-id counter

Comment frames now draw their persisted id from the SAME nextNodeId/max_node_id counter as nodes, so commentNodeId(id) == id — small, below the 2^30 pin floor, and unique vs node ids by construction (one counter). The separate nextCommentId/max_comment_id + 2^40 base are removed. Cleaner than carving a free gap: single id source, disjointness falls out instead of needing a maintained range invariant.

Load-time reassignment (parse): seed max_node_id from BOTH node and comment ids, then reassign a fresh nextNodeId() to any comment id that is 0, a duplicate (the #188/#201 dedup, preserved), OR equal to a node id (the new #203 cross-namespace constraint).

Tests

#201 comment-id tests preserved (assertions moved to the shared counter). New: a comment id colliding with a node id is reassigned on load; commentNodeId(n) is disjoint from pinId for node ids {1,1024,2048,100000}. zig build test 465/465, gui-test 26/26 (agent run; CI confirms).

…203)

`commentNodeId` mapped a comment's stable id into the node-editor id
space by offsetting it 2^40 (`comment_id_base`). That base sits INSIDE
the pin id space — `pinId` packs `(node_id << 30) | hash`, which reaches
2^40 once a node id hits ~1024 — so a flow with ~1024+ nodes could have a
comment frame's node-editor id alias a pin id and bleed drag/selection
state. (Rendering-only; persisted `.flow.jsonc` was unaffected.)

No bit region is free (pins own bits 0-62, links own bit 63), so allocate
comment node-editor ids from the NODE-ID space instead of a separate
2^40-based counter:

- Comments now draw their persisted id from the shared `nextNodeId` /
  `max_node_id` counter (dropped `nextCommentId` / `max_comment_id`), so
  `commentNodeId(id) == id` — small, below the 2^30 pin floor, and unique
  vs node ids by construction.
- On load, seed `max_node_id` from BOTH node and comment ids, then
  re-assign any comment id that is 0, a duplicate of another comment
  (preserves #188/#201 dedup), OR collides with a node id (new #203
  constraint).

Tests: added a node-id collision reassignment test and a
`commentNodeId` vs `pinId`/node-id disjointness assertion; updated the
existing #201 comment-id tests to assert against the shared `max_node_id`
counter. `zig build`, `zig build test` (465/465) and `zig build gui-test`
(26/26) all green.
@cursor

cursor Bot commented Jun 9, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Changes editor id allocation and load-time comment id rewriting; wrong logic could reassign persisted ids or corrupt canvas state, but scope is editor-only with strong test coverage and no codegen impact.

Overview
Fixes #203: comment group frames in the node editor could share ids with pin ids on large graphs because commentNodeId used a 2^40 offset inside the pinId space (node_id << 30 | hash).

Shared id counter: Removes max_comment_id / nextCommentId. Comment frames now use nextNodeId / max_node_id with nodes. commentNodeId(id) is the identity map (u64(id)) instead of 2^40 + id, keeping editor ids small and below the pin-id floor.

Load path: After parsing comments, max_node_id includes comment ids. On load, comments still get fresh ids for 0, duplicate comment ids, and new: any id that matches an existing node id.

Editor: appendComment, renderComments, and defensive id assignment call nextNodeId. commentNodeId, pinId, and PinDir are pub for tests.

Tests cover the shared counter, node–comment collision reassignment, and commentNodeId vs pinId disjointness.

Reviewed by Cursor Bugbot for commit e54968d. Bugbot is set up for automated code reviews on this repo. Configure here.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request unifies the ID namespace for nodes and comment frames to resolve ID collisions (labelle-gui#203). The separate max_comment_id counter and comment_id_base offset have been removed, and comments now share the max_node_id counter with nodes. The parser has been updated to detect and resolve collisions between comment IDs and node IDs on load, and corresponding unit tests have been added to verify this behavior. There are no review comments, so I have no feedback to provide.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@apotema apotema requested a review from Copilot June 9, 2026 14:43
@apotema apotema merged commit 2813d90 into main Jun 9, 2026
5 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a flow-editor rendering/interaction bug where comment frame node-editor IDs could collide with pin IDs in large graphs by moving comment frame IDs into the shared node-id space and hardening load-time ID reassignment.

Changes:

  • Remove the separate comment-id counter and allocate comment frame IDs from nextNodeId() / max_node_id.
  • Update load-time parsing to seed max_node_id from both node + comment IDs and to reassign comment IDs that are 0, duplicate, or collide with a node ID.
  • Extend/adjust unit tests to cover cross-namespace collisions and the new shared-counter behavior.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
src/tests.zig Updates existing comment-id tests for the shared counter and adds coverage for node/comment ID collisions and pin-id disjointness.
src/modules/flow_doc.zig Changes commentNodeId to identity mapping, switches comment creation/repair to use nextNodeId, and exposes helpers used by tests.
src/flow_io.zig Removes max_comment_id/nextCommentId, seeds max_node_id from comments too, and reassigns clashing comment IDs during parse.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/modules/flow_doc.zig
Comment on lines +1644 to +1646
/// id from the SAME counter as nodes (`nextNodeId`/`max_node_id`), so the
/// identity map below is both small (below the 2^30 pin floor) and unique
/// against node ids by construction — no separate region, no collision.
Comment thread src/flow_io.zig
Comment on lines +750 to +754
/// Highest id seen across BOTH nodes and comments — the editor
/// allocates fresh ids above this. Nodes and comment frames share one
/// id counter (labelle-gui#203) so a comment's node-editor id
/// (`commentNodeId(id) == id`) can never collide with a node's id or,
/// transitively, with a pin id (pins pack `node_id << 30 | hash`).
Comment thread src/tests.zig
try expect.toBeTrue(doc.comments[0].id > 5);
}

test "commentNodeId is disjoint from node ids and pin ids (#203)" {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Flow editor: harden comment frame node-editor id namespace (pin-id collision in huge graphs)

2 participants