Skip to content

app: an entity may have an edge to its own type - #2

Merged
lesomnus merged 1 commit into
mainfrom
select-contains-itself
Oct 9, 2026
Merged

lesomnus merged 1 commit into
mainfrom
select-contains-itself

Conversation

@lesomnus

@lesomnus lesomnus commented Oct 9, 2026

Copy link
Copy Markdown
Member

Fixes #1.

What was wrong

xMsgSelect selects each edge's far end with that entity's own Select, so an entity with an edge to its own type asks for its Select while writing it. defineMsg caches a message only after its body returns, and the second ask arrives through a fresh fileWork (withEntity makes one per reference), so neither cache saw it and generation recursed until the stack ran out. Two entities in one file that point at each other were the same cycle one step longer.

What this changes

  • app/work.go: the names being defined are kept on the shared work; asking for one while its body is being written answers its name. A protobuf message may contain itself, and the definition already in progress is the one that is emitted.
  • app/app.go: every entity's output path is registered before any entity is written. For the pair, the first of the two could not otherwise say where the second's messages are (generated filepath to the entity not found).
  • proto/apptest/tree.proto: a Node with a parent edge to itself, and a Left/Right pair pointing at each other, with the contract they generate.
  • internal/apptest: a test that the contract has NodeSelect parent = 16, that the pair select each other, that NodeSelect is written once, and that the file does not import itself.

The four existing contracts do not change.

Checked

What CI runs, locally: buf generate twice with no diff (the second pass compiles the new contract), gofmt, go build, go vet, go test ./... -count=1.

🤖 Generated with Claude Code

A Select selects each edge's far end with that entity's own Select, so an
entity with an edge to its own type -- a tree's parent -- asks for its
Select while writing it. defineMsg caches a message only once its body
returns, and the second ask arrives through a fresh fileWork, since
withEntity makes one per reference; neither cache saw it, and generation
recursed until the stack ran out. Two entities of one file with edges to
each other were the same cycle one step longer.

The names being defined are now kept on the work they share, and asking
for one of them answers its name: a message may contain itself, and the
definition in progress is the one that is emitted.

The pair also needed every entity's output path before any entity is
written. Paths were registered as each entity's turn came, so the first
of two that point at each other could not say where the second's
messages are; they are now all registered first.

Fixes #1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@lesomnus
lesomnus merged commit e28a221 into main Oct 9, 2026
1 check passed
@lesomnus
lesomnus deleted the select-contains-itself branch October 9, 2026 20:39
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.

A self-referential edge overflows the stack in xMsgSelect

1 participant