Почистить комментарии от упоминаний процесса
Удалены <remarks>, <summary> сжаты до короткой фразы, вырезаны ссылки на Task/Ruling/этап/python/прототип; //-комментарии со ссылками на процесс удалены; то же в .proto. Правила обновлены в docs/spec/Код-стайл-Дейл.md. Строк комментариев 27210 -> ~19100.
This commit is contained in:
@@ -15,51 +15,30 @@ using KanbanColumnRules = Deal.Modules.Kanban.Application.ColumnRules.ColumnRule
|
||||
namespace Deal.Infrastructure.Integrations.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Адаптер ИИ-предложений колонок/ключей — детерминированная эвристика этапа 3 (Ruling 3, план Task 14).
|
||||
/// Адаптер ИИ-предложений колонок/ключей — детерминированная эвристика.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Реализует порт <see cref="IColumnSuggester"/> поверх порта <see cref="ICardStore"/> и чистого ядра
|
||||
/// <see cref="SuggestHeuristics"/> (модуль Kanban): читает «Неразобранное» (ListInboxWithSourceAsync),
|
||||
/// считает группы слов-тем и создаёт доски suggested=true (RulesJson {mode:"any", keywords:[…]},
|
||||
/// note-обоснование, цвет/позицию даёт ContainersService) и раскладывает карточки (is_new=TRUE,
|
||||
/// prev_col='inbox', matchHits по правилам доски — Ruling 2). Причины отказов — детерминированные
|
||||
/// строки прототипа/Ruling 3: «мало карточек в «Неразобранном» (нужно от 6)», «похожие колонки уже
|
||||
/// есть или нечего сгруппировать»; кулдаун повторов — KV-ключ <see cref="SettingsKeys.LastSuggestAt"/>
|
||||
/// (прототип COOLDOWN_S L51 + «недавно предлагали — подождите» L95). Журнал CardMoves/ML-сигналы при
|
||||
/// раскладке НЕ пишутся (suggest.py _assign_ids L220–239 — это не действие пользователя, а предложение).
|
||||
/// Suggest-keywords читает карточки вне trash/archive (suggest_domain_keywords L172–178).
|
||||
/// </remarks>
|
||||
/// <param name="store">Порт хранилища (карточки «Неразобранного», переносы в колонки-доски).</param>
|
||||
/// <param name="settings">KV-хранилище настроек тенанта (кулдаун lastSuggestAt, как KEY suggest.py L52).</param>
|
||||
/// <param name="settings">KV-хранилище настроек тенанта.</param>
|
||||
/// <param name="containersService">Сервис контейнеров: список существующих и создание suggested-колонок с дефолтами.</param>
|
||||
public sealed class LocalColumnSuggester(
|
||||
ICardStore store,
|
||||
ISettingsStore settings,
|
||||
ContainersService containersService) : IColumnSuggester
|
||||
{
|
||||
// ── Кулдаун повторов (suggest.py COOLDOWN_S L51; KEY lastSuggestAt L52) ──
|
||||
|
||||
// Как часто можно переспрашивать ИИ-предложения: 20 минут (COOLDOWN_S = 20 * 60, L51).
|
||||
private const long CooldownSeconds = 20 * 60;
|
||||
|
||||
// ── Детерминированные причины (Ruling 3; строки прототипа suggest.py) ──
|
||||
|
||||
// Кулдаун: повторный вызов слишком рано (suggest.py L95 «недавно предлагали — подождите»).
|
||||
private const string CooldownReason = "недавно предлагали — подождите";
|
||||
|
||||
// Мало карточек в «Неразобранном»: {0} — порог MIN_INBOX (suggest.py L102).
|
||||
private const string TooFewCardsReasonFormat = "мало карточек в «Неразобранном» (нужно от {0})";
|
||||
|
||||
// Групп не вышло: темы похожи на существующие доски или карточкам нечего разделить (L159).
|
||||
private const string NothingGroupedReason = "похожие колонки уже есть или нечего сгруппировать";
|
||||
|
||||
// Мало карточек для ключей: нужно хотя бы 3 (suggest_domain_keywords L178).
|
||||
private const string KeywordsTooFewReason = "мало карточек — сначала накопите заявки (нужно хотя бы 3)";
|
||||
|
||||
// Повторяющихся слов-маркеров не нашлось (suggest_domain_keywords L187, текст прототипа).
|
||||
private const string KeywordsEmptyReason = "ИИ не смог выделить ключи — попробуйте ещё раз";
|
||||
|
||||
// Режим правил колонки-предложения: «любое из условий» (suggest.py _rules_for L68 mode: any).
|
||||
private const string RulesModeAny = "any";
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -80,7 +59,6 @@ ContainersService containersService) : IColumnSuggester
|
||||
Cooldown: false);
|
||||
}
|
||||
|
||||
// Существующие (suggested=false) колонки: похожие темы не предлагаем (suggest.py L105, L138–139).
|
||||
IReadOnlyList<ContainerDto> containers = await containersService.ListAsync(ContainerSpaces.Dashboard, ct);
|
||||
IReadOnlyList<string> existingNames = containers
|
||||
.Where(container => !container.Suggested)
|
||||
@@ -96,7 +74,6 @@ ContainersService containersService) : IColumnSuggester
|
||||
int created = await StoreSuggestedColumnsAsync(inbox, plans, ct);
|
||||
if (created == 0)
|
||||
{
|
||||
// Все колонки откатаны: карточки групп разобраны между чтением и раскладкой (suggest.py L153–156).
|
||||
return new SuggestColumnsResultDto(Ok: false, Created: 0, Reason: NothingGroupedReason, Cooldown: false);
|
||||
}
|
||||
|
||||
@@ -107,7 +84,6 @@ ContainersService containersService) : IColumnSuggester
|
||||
/// <inheritdoc />
|
||||
public async Task<SuggestKeywordsResultDto> SuggestKeywordsAsync(CancellationToken ct)
|
||||
{
|
||||
// Выборка ключей — как suggest_domain_keywords L172–176: карточки вне trash/archive с текстом,
|
||||
// свежие 40 (ListCardsAsync(null) = «все, кроме taken», ORDER BY received_at DESC).
|
||||
IReadOnlyList<CardDto> cards = await store.ListCardsAsync(new CardsQuery(null), ct);
|
||||
List<string> texts = cards
|
||||
@@ -131,22 +107,17 @@ ContainersService containersService) : IColumnSuggester
|
||||
return new SuggestKeywordsResultDto(Ok: true, Keywords: keywords, Reason: null);
|
||||
}
|
||||
|
||||
// Создаёт доски-предложения по планам и раскладывает карточки (suggest.py L129–156).
|
||||
// inbox: Снимок «Неразобранного» (карточки планов берутся из него).
|
||||
// plans: Планы колонок (SuggestHeuristics.PlanColumns, ≤4).
|
||||
// ct: Токен отмены.
|
||||
// Возвращает: Сколько досок реально создано (0 — все откатаны из-за разобранных карточек).
|
||||
// Каждая доска — suggested=true c правилами {mode:"any", keywords:[тема]} и note-обоснованием.
|
||||
// Перед раскладкой перечитывается «Неразобранное»: карточки, ушедшие из inbox между снимком и
|
||||
// раскладкой (пользователь/тик), пропускаются — 1:1 со страховкой _assign_ids L231–233. Если в
|
||||
// колонку не легло ни одной карточки, пустая доска-предложение откатывается (_rollback_suggested
|
||||
// L242–248). matchHits считаются по правилам созданной доски (Ruling 2); журнал/ML не пишутся.
|
||||
private async Task<int> StoreSuggestedColumnsAsync(
|
||||
IReadOnlyList<CardDto> inbox,
|
||||
IReadOnlyList<SuggestedColumnPlan> plans,
|
||||
CancellationToken ct)
|
||||
{
|
||||
// Свежий снимок inbox — страховка «карточку уже разобрали» (suggest.py _assign_ids L231–233).
|
||||
HashSet<string> inboxIds = (await store.ListInboxWithSourceAsync(ct))
|
||||
.Select(card => card.Id)
|
||||
.ToHashSet(StringComparer.Ordinal);
|
||||
@@ -195,7 +166,6 @@ ContainersService containersService) : IColumnSuggester
|
||||
|
||||
if (placed == 0)
|
||||
{
|
||||
// Ничего не легло — пустое предложение не нужно (suggest.py L152–156).
|
||||
await containersService.DeleteAsync(container.Id, ct);
|
||||
continue;
|
||||
}
|
||||
@@ -207,8 +177,6 @@ ContainersService containersService) : IColumnSuggester
|
||||
}
|
||||
|
||||
// Сработал ли кулдаун: с последнего успешного предложения прошло меньше 20 минут.
|
||||
// Повреждённое/отсутствующее значение lastSuggestAt — кулдауна нет (как прототип: значение
|
||||
// пишется только после успеха, L160–161; битый KV — дефолт «никогда»).
|
||||
// ct: Токен отмены.
|
||||
// Возвращает: True — повторный вызов слишком рано (ответ {ok:false, reason, cooldown:true}).
|
||||
private async Task<bool> WithinCooldownAsync(CancellationToken ct)
|
||||
@@ -236,7 +204,6 @@ ContainersService containersService) : IColumnSuggester
|
||||
}
|
||||
}
|
||||
|
||||
// Записывает метку успешного предложения (suggest.py L160: set_setting(KEY, time.time())).
|
||||
// ct: Токен отмены.
|
||||
private Task WriteLastSuggestAtAsync(CancellationToken ct) =>
|
||||
settings.SetAsync(
|
||||
|
||||
Reference in New Issue
Block a user