A comment in a nodes section can belong to the side (// Left side), to a prefix group (// prefix group rl_fl), or to the vertex right below it. Today nothing records which, so transformation has to guess, and it guesses differently depending on what else is in the file. The visible effect is that a comment sometimes moves away from the nodes it describes, and sometimes disappears.
This issue is to settle the rule. Two proposals below, and they are alternatives rather than steps.
What the code does today
A prefix group is not a separate concept from a vertex tree. VertexForest = Map VertexTreeType (OMap1 VertexTreeKey VertexTree), so each prefix group is its own VertexTree and its comment lives in that tree's tComments.
The side has no storage of its own. addSideComment prepends the side comment onto the first tree's tComments (OMap1.uncons), so // Left side and // prefix group rl_fl end up in one list on one tree, distinguished only by order.
Three behaviours worth knowing before choosing. Build a file with two prefix groups in one tree, each headed by a comment, and vary how many of the second group's nodes qualify as support nodes:
| Input |
Result |
| Both groups keep their nodes |
Both comments kept |
| One node leaves the group, including its first |
Comment stays with the remaining nodes |
| Every node in the group leaves |
Comment is dropped |
The last row is the loss. The comment headed a tree that no longer exists, and nothing carries it.
Proposal 1: decide by the blank line against the first vertex
The 2026-08-16 comment on #69 proposed deciding by position: the first comment block before a vertex tree belongs to the tree, the second belongs to the first vertex. Two things were left open there. "A second block after it" can be read as after the tree rather than after the first block. And the rule says nothing about a tree that stops existing, which is exactly the dropped-comment case above.
There is also a signal that comment ruled out too early. It rejected spacing because cHadNewlineBefore cannot tell the two kinds apart, which is true: that field holds whether there was a blank line before the comment, and both kinds have one. The blank line after the block is different. The tool's own output uses it consistently: // Left side is followed by a blank line, // prefix group rl_fl sits directly on top of its first vertex (examples/transformed_jbeam/frame-cfg-default.jbeam, lines 18 to 21).
The catch is that the parser drops it. lastSeparatorHadBlankLine is only ever stored onto comments, as cHadNewlineBefore, so the gap between a comment block and a following vertex is not in the AST. Using this signal means recording blank-line-before on nodes generally, not only on comments.
Worth stating plainly: the tool writes a distinction it cannot read back. That is an idempotency problem, not only a preservation one.
Proposal 2: write no comments unless they are configured
The alternative is to stop generating comments at all unless the config asks for them, per tree type. Then every comment in a file is the author's, and one rule covers all of them: a comment above a vertex belongs to that vertex. The three-level ownership question does not need answering, because the tool no longer contributes two of the three levels.
It also removes the deduplication logic, and it makes the tool behave like a formatter rather than one that inserts editorial text into someone else's file.
Two things to weigh. // Support nodes marks a relocation the tool performed, and without it a reader cannot tell why a node moved to the bottom of the file, so its default deserves separate thought. And configuring per prefix group is awkward because --update-names rewrites the prefixes that would be the config keys, while per tree type is stable.
Found along the way, fixable on its own
commentsExists = any (notNull . tComments . OMap1.head) asks whether the first tree has any comment, not whether it has the side comment. So a hand-written comment above the first group suppresses // Left side for that whole side, while the other sides still get theirs. That is worth fixing whichever proposal wins.
A comment in a nodes section can belong to the side (
// Left side), to a prefix group (// prefix group rl_fl), or to the vertex right below it. Today nothing records which, so transformation has to guess, and it guesses differently depending on what else is in the file. The visible effect is that a comment sometimes moves away from the nodes it describes, and sometimes disappears.This issue is to settle the rule. Two proposals below, and they are alternatives rather than steps.
What the code does today
A prefix group is not a separate concept from a vertex tree.
VertexForest = Map VertexTreeType (OMap1 VertexTreeKey VertexTree), so each prefix group is its ownVertexTreeand its comment lives in that tree'stComments.The side has no storage of its own.
addSideCommentprepends the side comment onto the first tree'stComments(OMap1.uncons), so// Left sideand// prefix group rl_flend up in one list on one tree, distinguished only by order.Three behaviours worth knowing before choosing. Build a file with two prefix groups in one tree, each headed by a comment, and vary how many of the second group's nodes qualify as support nodes:
The last row is the loss. The comment headed a tree that no longer exists, and nothing carries it.
Proposal 1: decide by the blank line against the first vertex
The 2026-08-16 comment on #69 proposed deciding by position: the first comment block before a vertex tree belongs to the tree, the second belongs to the first vertex. Two things were left open there. "A second block after it" can be read as after the tree rather than after the first block. And the rule says nothing about a tree that stops existing, which is exactly the dropped-comment case above.
There is also a signal that comment ruled out too early. It rejected spacing because
cHadNewlineBeforecannot tell the two kinds apart, which is true: that field holds whether there was a blank line before the comment, and both kinds have one. The blank line after the block is different. The tool's own output uses it consistently:// Left sideis followed by a blank line,// prefix group rl_flsits directly on top of its first vertex (examples/transformed_jbeam/frame-cfg-default.jbeam, lines 18 to 21).The catch is that the parser drops it.
lastSeparatorHadBlankLineis only ever stored onto comments, ascHadNewlineBefore, so the gap between a comment block and a following vertex is not in the AST. Using this signal means recording blank-line-before on nodes generally, not only on comments.Worth stating plainly: the tool writes a distinction it cannot read back. That is an idempotency problem, not only a preservation one.
Proposal 2: write no comments unless they are configured
The alternative is to stop generating comments at all unless the config asks for them, per tree type. Then every comment in a file is the author's, and one rule covers all of them: a comment above a vertex belongs to that vertex. The three-level ownership question does not need answering, because the tool no longer contributes two of the three levels.
It also removes the deduplication logic, and it makes the tool behave like a formatter rather than one that inserts editorial text into someone else's file.
Two things to weigh.
// Support nodesmarks a relocation the tool performed, and without it a reader cannot tell why a node moved to the bottom of the file, so its default deserves separate thought. And configuring per prefix group is awkward because--update-namesrewrites the prefixes that would be the config keys, while per tree type is stable.Found along the way, fixable on its own
commentsExists = any (notNull . tComments . OMap1.head)asks whether the first tree has any comment, not whether it has the side comment. So a hand-written comment above the first group suppresses// Left sidefor that whole side, while the other sides still get theirs. That is worth fixing whichever proposal wins.