Skip to content

fix: YAML anchors, aliases and numbers in model files survive a save (#157) - #158

Merged
liam-machine merged 2 commits into
developfrom
fix/157-yaml-aliases
Oct 9, 2026
Merged

liam-machine merged 2 commits into
developfrom
fix/157-yaml-aliases

Conversation

@liam-machine

Copy link
Copy Markdown
Owner

Closes #157

What was wrong

Model files are hand-edited YAML, and YAML lets a value be written once with an anchor (&x) and reused with an alias (*x). Several save paths didn't handle that, and a save also rewrote text nobody changed:

  1. An alias inside a relationships: entry (role: *r) made every save of that model fail with "this relationship entry cannot be read".
  2. A key ERD Studio doesn't manage (x-note: *g) silently followed a canvas edit of the anchored value, and clearing that value failed the save with Unresolved alias.
  3. Move Relationships to Model Library re-rendered the list and dropped an anchor inside it, so description: *r elsewhere read as empty.
  4. description: true was rewritten as "true" on any save.
  5. Numbers were rewritten on any save: 007 → 7 (a meta value then read "7"), 1e3 → 1e+3, .5 → 0.5, +1 → 1.
  6. A plain value becoming a map or list (meta: ~ # c) dropped the line's comment.
  7. A reused number written into a text field took two saves to settle.

Fix (src/services/logicalModelService.ts, src/services/minimalEdits.ts)

  • Relationship entries are read with their aliases resolved against the real document (a detached copy, not a clone).
  • Every alias in the file is kept as it reads before the save, not only those under managed keys. One whose anchor the save rewrites, removes or moves after it is written out as the value it read, keeping its comment. An alias whose anchor didn't change stays an alias.
  • Documents parsed for editing print a number as the text it was written with (keepNumberText custom tags; a value changed by setScalarValue has no text left and prints as usual). A scalar the reader already reads as the wanted value is left as written, which replaces the old "pin to a quoted string" step.
  • A scalar replaced by a map/list carries its comments, as an alias already did.
  • relationshipsRewriteLoses flags a relationships: list holding a YAML anchor or alias, so Move leaves the file for the user and names it ("comments, YAML anchors or entries ERD Studio cannot read").

Tests

  • Targeted regression tests for each item (logicalModelService.test.ts, minimalEdits.test.ts, moveRelationshipsToLibrary.test.ts).
  • The issue's proposed property test: logicalModelAliases.property.test.ts, seeded. It runs 1500 generated files with anchors and aliases over managed, unmanaged and relationship keys plus number-looking text, then 0–3 canvas edits. It asserts the save doesn't throw, the result parses, it reads back exactly as the model, unmanaged keys read unchanged, a no-edit save is byte-identical, and a second save is byte-identical.
  • Without the fix, the property test fails at seed 1 and 12 targeted tests fail.
  • One existing test changed: an unchanged name: 007 is now left as 007 (byte-identical), not quoted as "007".
  • Full suite 3700 passed / 1 skipped; compile, build, CLI smoke and the mcp-server checks pass.

Also tidies the ## Unreleased changelog (one ### Added section, stray blank line removed).

Out of scope, seen while testing: a single save that both renames and reorders columns can't match the renamed column, so its unknown keys are rebuilt. The canvas never sends those together.

🤖 Generated with Claude Code

liam-machine and others added 2 commits October 10, 2026 08:37
…157)

- An alias inside a relationships: entry (role: *r) no longer blocks every
  save: the entry is read with its aliases resolved against the file.
- Every alias in the file, not only those under managed keys, is kept as it
  reads before the save; one whose anchor the save rewrites, removes or
  moves after it is written out as the value it read, keeping its comment,
  so an unmanaged key never follows an edit and the save never fails with
  "Unresolved alias".
- A number prints as the text it was written with (007, 1e3, .5, +1), and a
  scalar the reader already reads as the wanted value is left as written,
  so description: true is not quoted and a reused number settles in one save.
- A plain value that becomes a map or list keeps the line's comment.
- Move Relationships to Model Library leaves (and names) a model file whose
  relationships: list holds a YAML anchor or alias, rather than re-render it
  and drop the anchor.
- A seeded randomised test covers anchors and aliases across managed,
  unmanaged and relationship keys (1500 files, edits, idempotence).
- CHANGELOG: one Added section, stray blank line removed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…e reader did not take as text (#157)

Review findings on the #157 fix:
- Keeping and settling every alias called Alias.resolve, which walks the
  whole document, once per alias: a save of a file with thousands of aliases
  took seconds to minutes. Both passes now resolve every alias in one walk.
- A relationship entry's keys are read only as text, so `role: true` holds
  no role; the "already reads the same" check let a canvas role "true" leave
  the boolean in place and the edit was lost. Those keys must now match as
  text exactly.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@liam-machine
liam-machine merged commit 1c8fc2e into develop Oct 9, 2026
5 checks passed
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.

1 participant