Proof of concept, not meant to merge as is. A file can be branched
into an identity-preserving copy, edited, and merged back.
- common: app.common.files.merge does an attribute-level three-way
merge (shape attrs grouped by ctk/sync-attrs), reports conflicts,
and supports excluding changes for selective merges.
- backend: file_branch table and create, list, diff, merge and
delete RPCs. Branches reuse duplicate-file, which keeps shape and
component ids. A merge snapshots the core file first.
- frontend: branches panel in the right sidebar (list, open,
create, delete, review, resolve, selective merge). The dashboard
hides branch files and shows a branch count on the core file.
Not done: feature flag, i18n, keep-both resolution, component
touched rules, branch media relinking.