feat(mcp): pre-push concurrency detection + 3-way merge - #79
Conversation
AI ReviewAdds pre-push concurrency detection with a 3-way node-level merge to Checklist
Build & test detail (U5)
Issues found[warning — U6] Empty-titled nodes become blanket conflicts in the 3-way merge
In practice, Corezoid processes often leave auto-generated nodes untitled (the Start event node and some error-cluster nodes frequently have This is conservative (never silently merges the wrong thing) and the code comment calls it out explicitly ("duplicate title — cannot merge safely"), so it is intentional. But it means Suggested fix (advisory): treat This review was generated automatically. A human maintainer should still make the merge decision. |
AI ReviewRe-review after synchronize: adds pre-push concurrency detection with 3-way node-level merge to Checklist
Build & test detail (U5)
Fixed since last review ✅[U6] Empty-titled nodes becoming blanket conflicts in the 3-way merge — RESOLVED
Issues found[warning — U6, residual] Ordinal-based matching shifts when an untitled node is inserted mid-sequence
Scenario: process has two untitled end nodes ( This is substantially rarer than the original blanket-collision bug (requires an untitled node insertion before an existing one, not merely the presence of multiple untitled nodes), and the conservative behaviour is safe (never silently merges the wrong content). Advisory: consider noting this limitation in the conflict-block user message so operators know why untitled nodes appear in the conflict bucket on a seemingly clean concurrent edit. This review was generated automatically. A human maintainer should still make the merge decision. |
AI ReviewRe-review after synchronize: adds pre-push concurrency detection with 3-way node-level merge to Checklist
Build & test detail (U5)
Fixed since last review ✅[U6, residual] Ordinal-based matching shifts for untitled node insertion — RESOLVED The previous advisory noted that
This is the exact operator-facing message the advisory requested. The new test Issues foundNo issues found. This review was generated automatically. A human maintainer should still make the merge decision. |
9b95700 to
6d80d89
Compare
AI ReviewRe-review after synchronize: adds pre-push concurrency detection with 3-way node-level merge to Checklist
Build & test detail (U5)
Fixed since last review ✅[U6, residual] Ordinal-based matching shifts for untitled node insertion — RESOLVED Commit
Issues foundNo issues found. This review was generated automatically. A human maintainer should still make the merge decision. |
pull-process/pull-folder record the server version each process was pulled at in a per-folder .corezoid-baseline.json sidecar. Before a push mutates the live process, it compares the live server version against that baseline: if someone else changed the process since the pull, the push is BLOCKED (a plain push would silently drop their edits via DeleteNotUsedNodes) with a report of what changed, who changed it, and the impact (server nodes the push would delete). Reuses the existing force flag to override (the auto-snapshot keeps their version recoverable); re-pull reconciles. No baseline (never-pulled/legacy files) is advisory, not a block. After a successful push the baseline is refreshed so the next push starts current. The API has no optimistic lock, so this is a client-side read-compare-before-write. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Builds on the version-drift block (previous commit): when the server moved since pull, push now offers a real 3-way merge, not only detect/force. - pull captures the pulled scheme as a merge ancestor (.corezoid-baseline/); post-push refreshes it too so push->edit->push keeps 3-way instead of degrading to a delete-only report - merge=true grafts the non-overlapping server changes into the local file (nodes matched by title, links rewired across id regeneration), keeps true conflicts as yours; advances baseline only when clean - block report shows who last changed it on the server (commit list, falling back to the latest snapshot user_name) plus three buckets -- your edits / server changes / overlap -- and a clear merge / re-pull / force choice stating what each keeps and drops - unit tests for every classification case + live E2E on the sandbox; node bodies (code, conditions, options, links) round-trip through the export API and merge fully Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Addresses the automated review on the PR: canonicalizeNodes keyed every node by its title, so all untitled nodes (title:"") collapsed onto one "" key and were flagged Ambiguous -> clsConflict. Real Corezoid processes routinely leave Start events and error finals untitled, so a process with 2+ untitled nodes forced every one of them into the conflict bucket on any concurrent push, weakening merge=true. Now nodes match by a stable key (matchKeys): the title for titled nodes, or obj_type + ordinal-among-untitled-of-that-type for untitled ones, so untitled nodes reconcile 1:1 instead of colliding. Link references are canonicalised and rewired to that key too, so links pointing at untitled targets no longer collapse. Only a genuine duplicate non-empty title still yields an Ambiguous conflict. The report shows untitled nodes as "(untitled start|end|...)". Tests: untitled nodes untouched -> not conflicts and all represented; one-sided untitled edit -> mergeable graft. Full suite green with -race. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Follow-up to the automated review on the concurrency PR. - force=true no longer promises the server version is "recoverable" unconditionally: the auto-snapshot is best-effort (it can fail, e.g. a transient server error), so the block now says a snapshot is attempted first and is recoverable only if it succeeds — with a pointer to check the push result. - The conflict report now notes, when an untitled node lands in the overlap bucket, that untitled nodes are matched by position and an insertion/removal on one side can surface a false overlap — so operators understand why an untitled node appears conflicted on a seemingly clean edit, and know that titling nodes avoids it. Test: TestMerge_UntitledConflictShowsPositionNote. Full suite green (-race). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> 🤖 Generated with [Claude Code](https://claude.com/claude-code)
6d80d89 to
1758e32
Compare
AI ReviewRe-review after synchronize: adds pre-push concurrency detection with 3-way node-level merge to Checklist
Build & test detail (U5)
Fixed since last review ✅No new issues were raised in the previous review (review #4, 2026-07-17T10:22:52Z reported "No issues found"). This synchronize is a rebase-only update — content is unchanged. Carrying all prior resolutions forward:
Issues foundNo issues found. This review was generated automatically. A human maintainer should still make the merge decision. |
Problem
push-processwas last-write-wins. On push,ProcessJSONfetches the current server scheme andDeleteNotUsedNodesdrops any server node absent from the local copy — so if someone edited the process on the server between your pull and your push, their edits are silently overwritten. Corezoid's API has no optimistic lock, so this is caught client-side (read-compare-before-write); the existing auto-snapshot covers the small TOCTOU window.What this adds
pull-process/pull-folderrecord the pulled server version (.corezoid-baseline.json) and a copy of the pulled scheme (.corezoid-baseline/, the 3-way merge ancestor). Both gitignored; post-push refreshes them sopush -> edit -> pushkeeps 3-way.user_name) and when;merge=true— real 3-way merge: grafts the non-overlapping server changes into your file (nodes matched by title, links rewired across the server's id regeneration), keeps true conflicts as yours and lists them; advances the baseline only when there is no conflict, so the follow-up push deploys cleanly.force=true— deploy your version, dropping the server changes (auto-snapshotted first, recoverable).Scope / non-goals (v1)
Detect + assisted merge, matched by node title (a rename reads as delete+add). No server-side lock (API can't). No new MCP tool — everything is folded into pull/push.
Testing
go test -race ./...green.merge=truegrafts + rewires links + advances baseline + clean re-push; a both-edit-same-node conflict is kept as mine +forceresolves; who/when line verified live. Node bodies (code, conditions, options, links) confirmed to round-trip through the export API and merge fully.🤖 Generated with Claude Code