fix(matching): conflict_resolution picks freshest (last_seen_at) value, deterministic (#1539) #1722

Merged
lekss361 merged 1 commit from fix/conflict-resolution-freshness-1539 into main 2026-06-17 19:49:52 +00:00
Owner

Проблема (#1539)

conflict_resolution.py _resolve/first_non_null итерировал candidates.values() в порядке вставки dict — недетерминированно.

Решение (freshness — выбор владельца)

_freshest_non_null(candidates, timestamps): среди non-null берёт значение источника с max last_seen_at. Применён к first_non_null-правилу, unknown-fallback и priority-list fallback. Явные priority-списки по-прежнему авторитетны (ранжир бьёт freshness). Threading через опциональный timestamps: dict|None (source→last_seen_at) — shape результата не изменён, старые callers работают.

Tie-break детерминированный: равный last_seen_at → лексикографически меньшее имя источника; нет timestamps → fallback по sorted-name (вместо insertion order).

Verify

ruff clean; pytest -k "conflict or resolution or matching"99 passed (+11 тестов: fresher-wins, order-independence, tie, partial, ranking-beats-freshness).

NB

resolve_* пока не вызывается из прода (update_canonical_fields — no-op stub; полная арбитрация — Stage 8.x). Сигнатура готова к проводке last_seen_at.

Closes #1539

## Проблема (#1539) `conflict_resolution.py` `_resolve`/`first_non_null` итерировал `candidates.values()` в порядке вставки dict — недетерминированно. ## Решение (freshness — выбор владельца) `_freshest_non_null(candidates, timestamps)`: среди non-null берёт значение источника с max `last_seen_at`. Применён к `first_non_null`-правилу, unknown-fallback и priority-list fallback. Явные priority-списки по-прежнему авторитетны (ранжир бьёт freshness). Threading через опциональный `timestamps: dict|None` (source→last_seen_at) — shape результата не изменён, старые callers работают. **Tie-break детерминированный:** равный last_seen_at → лексикографически меньшее имя источника; нет timestamps → fallback по sorted-name (вместо insertion order). ## Verify `ruff` clean; `pytest -k "conflict or resolution or matching"` → **99 passed** (+11 тестов: fresher-wins, order-independence, tie, partial, ranking-beats-freshness). ## NB `resolve_*` пока не вызывается из прода (`update_canonical_fields` — no-op stub; полная арбитрация — Stage 8.x). Сигнатура готова к проводке `last_seen_at`. Closes #1539
lekss361 added 1 commit 2026-06-17 19:44:55 +00:00
fix(matching): conflict_resolution picks freshest (last_seen_at) value, deterministic (#1539)
All checks were successful
CI / changes (pull_request) Successful in 8s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
f30f6acbf7
lekss361 merged commit f25e89767b into main 2026-06-17 19:49:52 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#1722
No description provided.