Follow-up polish from the duplicate-subgraph review:
- BaseSqliteStore.clone() now requires an opt-in `_cloneable` flag (default
off), so a new or secret-bearing store can never be cloned by accident —
defence-in-depth on top of the endpoint's `_DUPLICABLE_KINDS` restriction.
Only value-source and colour-strip stores are flagged.
- Fix `_check_name_unique` annotation (`str | None = None`).
- Add tests: `layers[].brightness_source_id` remap, the warnings safety net
firing, the clone allowlist invariant, and clone() refusing a non-cloneable
store.
A secret-safe equivalent of blueprint import: the graph editor's overflow menu
gains "Duplicate selection", which deep-clones the selected value and
colour-strip sources server-side (full config preserved, never crossing the
wire) and rewires references that point within the selection — shared
dependencies (devices, HA sources, …) stay shared.
- graph_schema.remap_refs: write-twin of extract_refs (same dot/list/bindable
grammar) that rewrites only in-selection ids; 8 unit tests.
- BaseSqliteStore.clone(): faithful deep-copy clone (no schema round-trip, so no
field is lost), prefix-preserving fresh id; reusable by any store.
- POST /api/v1/graph/duplicate: two-pass clone-then-rewire restricted to value /
colour-strip sources (no inline secrets), with a safety net flagging any
unremapped reference; 7 integration tests vs real stores.
- Frontend: duplicateSubgraph (+cache invalidation), graphDuplicateSelection
(reload + reselect the new cluster), overflow-menu item, i18n (en/ru/zh).