The goal was `aliases.isPartOf`, the deepest thing on the migration list:
mutual recursion over two trees at once, reaching `sym`, `typ`, `intVal` and
`isDeepConstExpr`. It does not migrate, and the reason is worth more than the
migration would have been.
`bnode.sym` IS NOT IDEMPOTENT for object fields. Two calls on the SAME token
yield two different `skField` `PSym`s with consecutive item ids — field uses
bypass the nav's memo and go to `loadFieldStub`, which mints per use because
two distinct fields can share a name AND a position across types, so one shared
stub would mistype one of them. `isPartOf` compares `a[1].sym.id != b[1].sym.id`
to decide whether two accessor chains touch the same field, so on a cursor it
answers `arNo` where the AST answers `arYes`: wrong alias analysis feeding NRVO
and observable-store decisions.
The grinder caught it on the first run after the migration, on a self-comparison
`isPartOf(n, n)` — a shape production never passes, which is exactly why it was
worth grading. The property was then confirmed directly rather than inferred:
call `sym` twice on one token and print both, and the ids differ.
So `aliases.nim` is reverted, and what stays is the knowledge:
* `bnode.sym` says which symbols it is stable for and which it is not, what
codegen actually consumes for a field instead (the name it re-navigates the
reclist with, plus the position for tuples — the same tolerance the grinder
applies), and why this module cannot fix it alone: a stable field identity
needs the token's own position as a key, and `nifcore.Cursor` keeps that
pointer private.
* The grinder asserts idempotence for every NON-field sym at every node. It
passes, and admitting fields makes it fail immediately — so the check states
a precise boundary rather than a vague warning, and the day fields join it
will be visible.
`getInt` moves into a template so `bnode` can instantiate it (the `canRaiseImpl`
pattern), and `astalgo.sameValue` becomes `AnyNode` and is graded — both are
literal-only and unaffected by the field problem. `preventNrvo` records the
OTHER kind of blocker while it is fresh: its alias analysis is fine, but the
`warnObservableStores` message renders the node, and rendering is a capability
the seam does not have at all.
Verified: grind clean (67_857 nodes, 0 disagreements); 215/215 byte-identical
`.c` against HEAD on the default path; all four build configurations compile.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XEF7FJvUkGKvG9LSGuEaNR