@taskless-app/swarm 0.6.45 → 0.6.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -123,7 +123,16 @@ var init_types = __esm({
123
123
  // TASWM-412: дрон вернул сырую задачу автору (см. RunStatus "returned"). Веха, а не step:
124
124
  // прогон на этом кончается, и в истории должно быть видно, что он кончился ВОЗВРАТОМ —
125
125
  // иначе возврат читается как брошенная посреди работы задача.
126
- "returned"
126
+ "returned",
127
+ // Одна находка ревьюера машиночитаемо (TASWM-561). text — JSON:
128
+ // { category, severity: "blocker" | "major" | "nit", path, line, what }.
129
+ // Одно событие = одна находка: только так их можно считать, фильтровать по severity
130
+ // и сводить по прогонам — текст вердикта на PR для этого разбирать нечем.
131
+ "finding",
132
+ // Как ревьюер вынес вердикт (TASWM-561): text = "ran" (тест прогнан) | "read" (судил
133
+ // чтением). Отдельный kind, а не поле находки: режим один на вердикт, а находок ноль
134
+ // или много — «проверено чтением» должно быть видно и когда не найдено ничего.
135
+ "review_mode"
127
136
  ];
128
137
  MILESTONE_KINDS = [
129
138
  "spawned",
@@ -652,15 +661,28 @@ function defaultModelFor(role, providerId = null) {
652
661
  const provider = findProvider(providerId) ?? findProvider(DEFAULT_PROVIDER_ID);
653
662
  return provider.roles[role];
654
663
  }
655
- var MODEL_PROVIDERS, DEFAULT_PROVIDER_ID;
664
+ var HARNESS_DEFAULT_MODEL, MODEL_PROVIDERS, DEFAULT_PROVIDER_ID;
656
665
  var init_modelPolicy = __esm({
657
666
  "../swarm-core/src/modelPolicy.ts"() {
658
667
  "use strict";
668
+ HARNESS_DEFAULT_MODEL = "default";
659
669
  MODEL_PROVIDERS = [
660
670
  {
661
671
  id: "anthropic",
662
672
  label: "Anthropic",
673
+ executorId: "claude-code",
674
+ selectable: true,
663
675
  roles: { executor: "claude-opus-5", review: "claude-sonnet-5" }
676
+ },
677
+ // TASWM-565 · шаг 1 из четырёх: провайдер в реестре ЕСТЬ, выбрать его ещё нельзя.
678
+ // Имён моделей здесь нет СОЗНАТЕЛЬНО: у владельца подписка, состав моделей в ней нам
679
+ // неизвестен, а выдуманное имя хуже маркера «решает Codex».
680
+ {
681
+ id: "openai",
682
+ label: "OpenAI (ChatGPT)",
683
+ executorId: "codex",
684
+ selectable: false,
685
+ roles: { executor: HARNESS_DEFAULT_MODEL, review: HARNESS_DEFAULT_MODEL }
664
686
  }
665
687
  ];
666
688
  DEFAULT_PROVIDER_ID = MODEL_PROVIDERS[0].id;
@@ -3390,7 +3412,7 @@ var init_package = __esm({
3390
3412
  "package.json"() {
3391
3413
  package_default = {
3392
3414
  name: "@taskless-app/swarm",
3393
- version: "0.6.45",
3415
+ version: "0.6.47",
3394
3416
  type: "module",
3395
3417
  description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
3396
3418
  keywords: [
@@ -3441,7 +3463,7 @@ var init_package = __esm({
3441
3463
  execa: "^10.0.0",
3442
3464
  zod: "3.25.76"
3443
3465
  },
3444
- gitHead: "a30a18d26e9b3a6895d350931b294c3df986ca01"
3466
+ gitHead: "561ecd5dfe7de7c9b1a106552bbef19921c65cbf"
3445
3467
  };
3446
3468
  }
3447
3469
  });
@@ -7589,13 +7611,15 @@ var SwarmClient = class _SwarmClient {
7589
7611
  const key = this.keyByLocal.get(localId);
7590
7612
  if (key) out[prodKey] = runRef(key);
7591
7613
  }
7592
- enqueueRun(run, fields) {
7614
+ enqueueRun(run, fields, opts = {}) {
7593
7615
  if (Object.keys(fields).length === 0) return;
7594
7616
  this.enqueue({
7595
7617
  t: "run",
7596
7618
  runKey: run.runKey,
7597
7619
  taskRef: run.task_slug,
7598
7620
  ...run.workspace_id ? { workspaceId: run.workspace_id } : {},
7621
+ create: opts.create === true,
7622
+ // TASWM-562: пометка есть у КАЖДОЙ новой записи — см. resolveArgs
7599
7623
  fields
7600
7624
  });
7601
7625
  }
@@ -7710,7 +7734,7 @@ var SwarmClient = class _SwarmClient {
7710
7734
  // закрепление (TASWM-291): true только у явного --host
7711
7735
  base_branch: d.base_branch
7712
7736
  // базовая ветка PR (TASWM-285), если задана на спавне
7713
- }));
7737
+ }), { create: true });
7714
7738
  const spawnNote = input.resolves_run_id ? `\u0440\u0435\u0437\u043E\u043B\u0432\u0435\u0440 \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u0430 \u0434\u043B\u044F #${input.resolves_run_id}` : input.reviews_run_id ? `\u0440\u0435\u0432\u044C\u044E\u0435\u0440 \u0434\u043B\u044F #${input.reviews_run_id}` : input.verifies_run_id ? `\u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u0434\u043B\u044F #${input.verifies_run_id}` : input.decision ? `\u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u043A \u0441 \u0440\u0435\u0448\u0435\u043D\u0438\u0435\u043C \xAB${input.decision.chosen}\xBB` : `run for ${input.task_slug}`;
7715
7739
  this.addEvent(id, "spawned", spawnNote);
7716
7740
  return this.mat(d);
@@ -7749,7 +7773,7 @@ var SwarmClient = class _SwarmClient {
7749
7773
  runLogKeys(id) {
7750
7774
  const key = this.runs.get(id)?.runKey;
7751
7775
  if (!key) return [];
7752
- const prodId = key.startsWith("p:") ? key.slice(2) : this.prodIdByKey.get(key);
7776
+ const prodId = this.prodIdFor(key);
7753
7777
  if (!prodId) return [key];
7754
7778
  const canonical = `p:${prodId}`;
7755
7779
  const aliases = [key, ...[...this.prodIdByKey.entries()].filter(([, v]) => v === prodId).map(([k]) => k)];
@@ -9487,11 +9511,31 @@ var SwarmClient = class _SwarmClient {
9487
9511
  for (const [full, path] of this.repoPathByFullName) if (path === norm) return full;
9488
9512
  return /^[^/\s]+\/[^/\s]+$/.test(repo) ? repo : void 0;
9489
9513
  }
9514
+ /**
9515
+ * Прод-id прогона по его runKey — ЕДИНСТВЕННЫЙ резолв на пути флаша (TASWM-562) и
9516
+ * в адресах run-лога (runLogKeys). Гидрированный прогон несёт cuid прямо в ключе
9517
+ * (`p:<cuid>`, см. hydrateRun). Карта prodIdByKey ему не подмога: в сайдкар она пишется
9518
+ * только после успешного create-флаша (persistMap), а прогон, РОЖДЁННЫЙ в проде
9519
+ * (claim/hydrate), туда не попадает никогда. Отсюда и брались пустые парные строки:
9520
+ * после рестарта демона recover() планирует флаш ДО первой гидрации, карта пуста —
9521
+ * и лежащий в outbox патч («прогон перешёл в done») заводил в проде второй прогон,
9522
+ * без токенов, цены, времени и PR.
9523
+ */
9524
+ prodIdFor(runKey) {
9525
+ return runKey.startsWith("p:") ? runKey.slice(2) : this.prodIdByKey.get(runKey);
9526
+ }
9490
9527
  async resolveArgs(e) {
9491
9528
  if (e.t === "run") {
9492
9529
  const fields = this.materializeRefs(e.fields);
9493
- const prodId2 = this.prodIdByKey.get(e.runKey);
9530
+ const prodId2 = this.prodIdFor(e.runKey);
9494
9531
  if (prodId2) return { tool: "swarm_run_upsert", args: { runId: prodId2, ...fields } };
9532
+ if (e.create === false) {
9533
+ console.error(
9534
+ `[swarm-client] \u043F\u0430\u0442\u0447 \u043F\u0440\u043E\u0433\u043E\u043D\u0430 \u0432\u044B\u0431\u0440\u043E\u0448\u0435\u043D: \u043F\u0440\u043E\u0434-id \u0434\u043B\u044F runKey=${e.runKey} \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u0435\u043D, \u0430 \u0437\u0430\u0432\u043E\u0434\u0438\u0442\u044C \u043F\u0440\u043E\u0433\u043E\u043D \u0437\u0430\u043D\u043E\u0432\u043E \u043F\u0430\u0442\u0447 \u043D\u0435 \u0432\u043F\u0440\u0430\u0432\u0435 (\u0438\u043D\u0430\u0447\u0435 \u0432 \u043F\u0440\u043E\u0434\u0435 \u043F\u043E\u044F\u0432\u0438\u0442\u0441\u044F \u043F\u0443\u0441\u0442\u043E\u0439 \u0434\u0432\u043E\u0439\u043D\u0438\u043A). \u041F\u043E\u043B\u044F: ${Object.keys(fields).join(", ")}`
9535
+ );
9536
+ this.poisonedRunKeys.add(e.runKey);
9537
+ return "skip";
9538
+ }
9495
9539
  const taskId = await this.resolveTaskCuid(e.taskRef, this.repoFullNameFor(fields.repo), e.workspaceId);
9496
9540
  if (taskId === "transient") {
9497
9541
  throw new Error(`slug\u2192cuid \u0434\u043B\u044F \xAB${e.taskRef}\xBB \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D (\u043F\u0440\u043E\u0434 \u043C\u043E\u0440\u0433\u043D\u0443\u043B) \u2014 retry`);
@@ -9509,7 +9553,7 @@ var SwarmClient = class _SwarmClient {
9509
9553
  console.error(`[swarm-client] ${e.t}-\u0444\u043B\u0430\u0448 \u043F\u0440\u043E\u043F\u0443\u0449\u0435\u043D: create \u043F\u0440\u043E\u0433\u043E\u043D\u0430 runKey=${e.runKey} \u043D\u0435 \u0440\u0435\u0437\u043E\u043B\u0432\u0438\u043B\u0441\u044F \u0440\u0430\u043D\u0435\u0435 \u2014 \u0437\u0430\u043F\u0438\u0441\u044C \u0432\u044B\u0431\u0440\u043E\u0448\u0435\u043D\u0430 \u0438\u0437 outbox`);
9510
9554
  return "skip";
9511
9555
  }
9512
- const prodId = this.prodIdByKey.get(e.runKey);
9556
+ const prodId = this.prodIdFor(e.runKey);
9513
9557
  if (!prodId) throw new Error(`\u043D\u0435\u0442 prod-id \u0434\u043B\u044F ${e.runKey} \u2014 \u043F\u0440\u043E\u0433\u043E\u043D \u0435\u0449\u0451 \u043D\u0435 \u0441\u043E\u0437\u0434\u0430\u043D \u0432 \u043F\u0440\u043E\u0434\u0435`);
9514
9558
  if (e.t === "event") return { tool: "swarm_event_append", args: { runId: prodId, kind: e.kind, text: e.text, actor: e.actor } };
9515
9559
  return { tool: "swarm_ask", args: { runId: prodId, text: e.text } };
@@ -9522,7 +9566,7 @@ var SwarmClient = class _SwarmClient {
9522
9566
  const out = {};
9523
9567
  for (const [k, v] of Object.entries(fields)) {
9524
9568
  if (isRunRef(v)) {
9525
- const cuid = this.prodIdByKey.get(v.__runRef);
9569
+ const cuid = this.prodIdFor(v.__runRef);
9526
9570
  if (cuid) out[k] = cuid;
9527
9571
  } else {
9528
9572
  out[k] = v;
@@ -9562,6 +9606,7 @@ var SwarmClient = class _SwarmClient {
9562
9606
  if (tries >= _SwarmClient.REJECT_LIMIT) {
9563
9607
  this.pending.shift();
9564
9608
  this.rejected.delete(e.seq);
9609
+ if (e.t === "run" && e.create === true) this.poisonedRunKeys.add(e.runKey);
9565
9610
  console.error(
9566
9611
  `[swarm-client] \u043F\u0440\u043E\u0434 \u043E\u0442\u0432\u0435\u0440\u0433 ${call.tool} ${tries} \u0440\u0430\u0437(\u0430) \u2014 \u0412\u042B\u0411\u0420\u0410\u0421\u042B\u0412\u0410\u042E \u044D\u0442\u0443 \u0437\u0430\u043F\u0438\u0441\u044C, \u0438\u043D\u0430\u0447\u0435 \u043E\u043D\u0430 \u0434\u0435\u0440\u0436\u0438\u0442 \u0432\u0441\u044E \u043E\u0447\u0435\u0440\u0435\u0434\u044C. \u041F\u043E\u0442\u0435\u0440\u044F\u043D\u043E: ${e.t}${e.t === "event" ? ` (${e.kind})` : ""}. \u041F\u0440\u0438\u0447\u0438\u043D\u0430 \u043E\u0442\u043A\u0430\u0437\u0430: ${reason}`
9567
9612
  );
package/dist/overmind.js CHANGED
@@ -92,7 +92,16 @@ var init_types = __esm({
92
92
  // TASWM-412: дрон вернул сырую задачу автору (см. RunStatus "returned"). Веха, а не step:
93
93
  // прогон на этом кончается, и в истории должно быть видно, что он кончился ВОЗВРАТОМ —
94
94
  // иначе возврат читается как брошенная посреди работы задача.
95
- "returned"
95
+ "returned",
96
+ // Одна находка ревьюера машиночитаемо (TASWM-561). text — JSON:
97
+ // { category, severity: "blocker" | "major" | "nit", path, line, what }.
98
+ // Одно событие = одна находка: только так их можно считать, фильтровать по severity
99
+ // и сводить по прогонам — текст вердикта на PR для этого разбирать нечем.
100
+ "finding",
101
+ // Как ревьюер вынес вердикт (TASWM-561): text = "ran" (тест прогнан) | "read" (судил
102
+ // чтением). Отдельный kind, а не поле находки: режим один на вердикт, а находок ноль
103
+ // или много — «проверено чтением» должно быть видно и когда не найдено ничего.
104
+ "review_mode"
96
105
  ];
97
106
  MILESTONE_KINDS = [
98
107
  "spawned",
@@ -646,15 +655,28 @@ function defaultModelFor(role, providerId = null) {
646
655
  const provider = findProvider(providerId) ?? findProvider(DEFAULT_PROVIDER_ID);
647
656
  return provider.roles[role];
648
657
  }
649
- var MODEL_PROVIDERS, DEFAULT_PROVIDER_ID;
658
+ var HARNESS_DEFAULT_MODEL, MODEL_PROVIDERS, DEFAULT_PROVIDER_ID;
650
659
  var init_modelPolicy = __esm({
651
660
  "../swarm-core/src/modelPolicy.ts"() {
652
661
  "use strict";
662
+ HARNESS_DEFAULT_MODEL = "default";
653
663
  MODEL_PROVIDERS = [
654
664
  {
655
665
  id: "anthropic",
656
666
  label: "Anthropic",
667
+ executorId: "claude-code",
668
+ selectable: true,
657
669
  roles: { executor: "claude-opus-5", review: "claude-sonnet-5" }
670
+ },
671
+ // TASWM-565 · шаг 1 из четырёх: провайдер в реестре ЕСТЬ, выбрать его ещё нельзя.
672
+ // Имён моделей здесь нет СОЗНАТЕЛЬНО: у владельца подписка, состав моделей в ней нам
673
+ // неизвестен, а выдуманное имя хуже маркера «решает Codex».
674
+ {
675
+ id: "openai",
676
+ label: "OpenAI (ChatGPT)",
677
+ executorId: "codex",
678
+ selectable: false,
679
+ roles: { executor: HARNESS_DEFAULT_MODEL, review: HARNESS_DEFAULT_MODEL }
658
680
  }
659
681
  ];
660
682
  DEFAULT_PROVIDER_ID = MODEL_PROVIDERS[0].id;
@@ -7827,13 +7849,15 @@ var SwarmClient = class _SwarmClient {
7827
7849
  const key = this.keyByLocal.get(localId);
7828
7850
  if (key) out[prodKey] = runRef(key);
7829
7851
  }
7830
- enqueueRun(run, fields) {
7852
+ enqueueRun(run, fields, opts = {}) {
7831
7853
  if (Object.keys(fields).length === 0) return;
7832
7854
  this.enqueue({
7833
7855
  t: "run",
7834
7856
  runKey: run.runKey,
7835
7857
  taskRef: run.task_slug,
7836
7858
  ...run.workspace_id ? { workspaceId: run.workspace_id } : {},
7859
+ create: opts.create === true,
7860
+ // TASWM-562: пометка есть у КАЖДОЙ новой записи — см. resolveArgs
7837
7861
  fields
7838
7862
  });
7839
7863
  }
@@ -7948,7 +7972,7 @@ var SwarmClient = class _SwarmClient {
7948
7972
  // закрепление (TASWM-291): true только у явного --host
7949
7973
  base_branch: d.base_branch
7950
7974
  // базовая ветка PR (TASWM-285), если задана на спавне
7951
- }));
7975
+ }), { create: true });
7952
7976
  const spawnNote = input.resolves_run_id ? `\u0440\u0435\u0437\u043E\u043B\u0432\u0435\u0440 \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u0430 \u0434\u043B\u044F #${input.resolves_run_id}` : input.reviews_run_id ? `\u0440\u0435\u0432\u044C\u044E\u0435\u0440 \u0434\u043B\u044F #${input.reviews_run_id}` : input.verifies_run_id ? `\u0432\u0435\u0440\u0438\u0444\u0438\u043A\u0430\u0442\u043E\u0440 \u0434\u043B\u044F #${input.verifies_run_id}` : input.decision ? `\u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u043A \u0441 \u0440\u0435\u0448\u0435\u043D\u0438\u0435\u043C \xAB${input.decision.chosen}\xBB` : `run for ${input.task_slug}`;
7953
7977
  this.addEvent(id, "spawned", spawnNote);
7954
7978
  return this.mat(d);
@@ -7987,7 +8011,7 @@ var SwarmClient = class _SwarmClient {
7987
8011
  runLogKeys(id) {
7988
8012
  const key = this.runs.get(id)?.runKey;
7989
8013
  if (!key) return [];
7990
- const prodId = key.startsWith("p:") ? key.slice(2) : this.prodIdByKey.get(key);
8014
+ const prodId = this.prodIdFor(key);
7991
8015
  if (!prodId) return [key];
7992
8016
  const canonical = `p:${prodId}`;
7993
8017
  const aliases = [key, ...[...this.prodIdByKey.entries()].filter(([, v]) => v === prodId).map(([k]) => k)];
@@ -9725,11 +9749,31 @@ var SwarmClient = class _SwarmClient {
9725
9749
  for (const [full, path] of this.repoPathByFullName) if (path === norm) return full;
9726
9750
  return /^[^/\s]+\/[^/\s]+$/.test(repo) ? repo : void 0;
9727
9751
  }
9752
+ /**
9753
+ * Прод-id прогона по его runKey — ЕДИНСТВЕННЫЙ резолв на пути флаша (TASWM-562) и
9754
+ * в адресах run-лога (runLogKeys). Гидрированный прогон несёт cuid прямо в ключе
9755
+ * (`p:<cuid>`, см. hydrateRun). Карта prodIdByKey ему не подмога: в сайдкар она пишется
9756
+ * только после успешного create-флаша (persistMap), а прогон, РОЖДЁННЫЙ в проде
9757
+ * (claim/hydrate), туда не попадает никогда. Отсюда и брались пустые парные строки:
9758
+ * после рестарта демона recover() планирует флаш ДО первой гидрации, карта пуста —
9759
+ * и лежащий в outbox патч («прогон перешёл в done») заводил в проде второй прогон,
9760
+ * без токенов, цены, времени и PR.
9761
+ */
9762
+ prodIdFor(runKey) {
9763
+ return runKey.startsWith("p:") ? runKey.slice(2) : this.prodIdByKey.get(runKey);
9764
+ }
9728
9765
  async resolveArgs(e) {
9729
9766
  if (e.t === "run") {
9730
9767
  const fields = this.materializeRefs(e.fields);
9731
- const prodId2 = this.prodIdByKey.get(e.runKey);
9768
+ const prodId2 = this.prodIdFor(e.runKey);
9732
9769
  if (prodId2) return { tool: "swarm_run_upsert", args: { runId: prodId2, ...fields } };
9770
+ if (e.create === false) {
9771
+ console.error(
9772
+ `[swarm-client] \u043F\u0430\u0442\u0447 \u043F\u0440\u043E\u0433\u043E\u043D\u0430 \u0432\u044B\u0431\u0440\u043E\u0448\u0435\u043D: \u043F\u0440\u043E\u0434-id \u0434\u043B\u044F runKey=${e.runKey} \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u0435\u043D, \u0430 \u0437\u0430\u0432\u043E\u0434\u0438\u0442\u044C \u043F\u0440\u043E\u0433\u043E\u043D \u0437\u0430\u043D\u043E\u0432\u043E \u043F\u0430\u0442\u0447 \u043D\u0435 \u0432\u043F\u0440\u0430\u0432\u0435 (\u0438\u043D\u0430\u0447\u0435 \u0432 \u043F\u0440\u043E\u0434\u0435 \u043F\u043E\u044F\u0432\u0438\u0442\u0441\u044F \u043F\u0443\u0441\u0442\u043E\u0439 \u0434\u0432\u043E\u0439\u043D\u0438\u043A). \u041F\u043E\u043B\u044F: ${Object.keys(fields).join(", ")}`
9773
+ );
9774
+ this.poisonedRunKeys.add(e.runKey);
9775
+ return "skip";
9776
+ }
9733
9777
  const taskId = await this.resolveTaskCuid(e.taskRef, this.repoFullNameFor(fields.repo), e.workspaceId);
9734
9778
  if (taskId === "transient") {
9735
9779
  throw new Error(`slug\u2192cuid \u0434\u043B\u044F \xAB${e.taskRef}\xBB \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D (\u043F\u0440\u043E\u0434 \u043C\u043E\u0440\u0433\u043D\u0443\u043B) \u2014 retry`);
@@ -9747,7 +9791,7 @@ var SwarmClient = class _SwarmClient {
9747
9791
  console.error(`[swarm-client] ${e.t}-\u0444\u043B\u0430\u0448 \u043F\u0440\u043E\u043F\u0443\u0449\u0435\u043D: create \u043F\u0440\u043E\u0433\u043E\u043D\u0430 runKey=${e.runKey} \u043D\u0435 \u0440\u0435\u0437\u043E\u043B\u0432\u0438\u043B\u0441\u044F \u0440\u0430\u043D\u0435\u0435 \u2014 \u0437\u0430\u043F\u0438\u0441\u044C \u0432\u044B\u0431\u0440\u043E\u0448\u0435\u043D\u0430 \u0438\u0437 outbox`);
9748
9792
  return "skip";
9749
9793
  }
9750
- const prodId = this.prodIdByKey.get(e.runKey);
9794
+ const prodId = this.prodIdFor(e.runKey);
9751
9795
  if (!prodId) throw new Error(`\u043D\u0435\u0442 prod-id \u0434\u043B\u044F ${e.runKey} \u2014 \u043F\u0440\u043E\u0433\u043E\u043D \u0435\u0449\u0451 \u043D\u0435 \u0441\u043E\u0437\u0434\u0430\u043D \u0432 \u043F\u0440\u043E\u0434\u0435`);
9752
9796
  if (e.t === "event") return { tool: "swarm_event_append", args: { runId: prodId, kind: e.kind, text: e.text, actor: e.actor } };
9753
9797
  return { tool: "swarm_ask", args: { runId: prodId, text: e.text } };
@@ -9760,7 +9804,7 @@ var SwarmClient = class _SwarmClient {
9760
9804
  const out = {};
9761
9805
  for (const [k, v] of Object.entries(fields)) {
9762
9806
  if (isRunRef(v)) {
9763
- const cuid = this.prodIdByKey.get(v.__runRef);
9807
+ const cuid = this.prodIdFor(v.__runRef);
9764
9808
  if (cuid) out[k] = cuid;
9765
9809
  } else {
9766
9810
  out[k] = v;
@@ -9800,6 +9844,7 @@ var SwarmClient = class _SwarmClient {
9800
9844
  if (tries >= _SwarmClient.REJECT_LIMIT) {
9801
9845
  this.pending.shift();
9802
9846
  this.rejected.delete(e.seq);
9847
+ if (e.t === "run" && e.create === true) this.poisonedRunKeys.add(e.runKey);
9803
9848
  console.error(
9804
9849
  `[swarm-client] \u043F\u0440\u043E\u0434 \u043E\u0442\u0432\u0435\u0440\u0433 ${call.tool} ${tries} \u0440\u0430\u0437(\u0430) \u2014 \u0412\u042B\u0411\u0420\u0410\u0421\u042B\u0412\u0410\u042E \u044D\u0442\u0443 \u0437\u0430\u043F\u0438\u0441\u044C, \u0438\u043D\u0430\u0447\u0435 \u043E\u043D\u0430 \u0434\u0435\u0440\u0436\u0438\u0442 \u0432\u0441\u044E \u043E\u0447\u0435\u0440\u0435\u0434\u044C. \u041F\u043E\u0442\u0435\u0440\u044F\u043D\u043E: ${e.t}${e.t === "event" ? ` (${e.kind})` : ""}. \u041F\u0440\u0438\u0447\u0438\u043D\u0430 \u043E\u0442\u043A\u0430\u0437\u0430: ${reason}`
9805
9850
  );
@@ -12314,7 +12359,7 @@ function parkReasonUnretriable(reason) {
12314
12359
  // package.json
12315
12360
  var package_default = {
12316
12361
  name: "@taskless-app/swarm",
12317
- version: "0.6.45",
12362
+ version: "0.6.47",
12318
12363
  type: "module",
12319
12364
  description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
12320
12365
  keywords: [
@@ -12365,7 +12410,7 @@ var package_default = {
12365
12410
  execa: "^10.0.0",
12366
12411
  zod: "3.25.76"
12367
12412
  },
12368
- gitHead: "a30a18d26e9b3a6895d350931b294c3df986ca01"
12413
+ gitHead: "561ecd5dfe7de7c9b1a106552bbef19921c65cbf"
12369
12414
  };
12370
12415
 
12371
12416
  // src/version.ts
@@ -12741,6 +12786,70 @@ async function closePR(prUrl, run = realRunner) {
12741
12786
  async function postPrComment(prUrl, body, run = realRunner) {
12742
12787
  await run(["pr", "comment", prUrl, "--body", body]);
12743
12788
  }
12789
+ var FINDING_CATEGORIES = [
12790
+ "correctness",
12791
+ "security",
12792
+ "reuse",
12793
+ "boundaries",
12794
+ "over-abstraction",
12795
+ "duplication",
12796
+ "spine",
12797
+ "tests",
12798
+ "ui-rules"
12799
+ ];
12800
+ var FINDING_SEVERITIES = ["blocker", "major", "nit"];
12801
+ var FINDINGS_BLOCK_RE = /```findings[^\n]*\n([\s\S]*?)```/gi;
12802
+ var READ_ONLY_PHRASE_RE = /прогнать не мог|окружения нет/i;
12803
+ var FINDING_WHAT_MAX = 400;
12804
+ var FINDING_PATH_MAX = 300;
12805
+ var FINDINGS_MAX = 50;
12806
+ function oneOf(dict, v) {
12807
+ return typeof v === "string" && dict.includes(v) ? v : null;
12808
+ }
12809
+ function normalizeFinding(raw) {
12810
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
12811
+ const r = raw;
12812
+ const what = typeof r.what === "string" ? r.what.trim().slice(0, FINDING_WHAT_MAX) : "";
12813
+ if (!what) return null;
12814
+ const rawLine = typeof r.line === "number" ? r.line : typeof r.line === "string" && r.line.trim() ? Number(r.line) : NaN;
12815
+ return {
12816
+ category: oneOf(FINDING_CATEGORIES, r.category),
12817
+ severity: oneOf(FINDING_SEVERITIES, r.severity),
12818
+ path: typeof r.path === "string" && r.path.trim() ? r.path.trim().slice(0, FINDING_PATH_MAX) : null,
12819
+ // Номер строки — только целый положительный: «7.5», «-1» и NaN значат, что ревьюер
12820
+ // строку не знал, а не что дефект на строке −1 (по такому числу потом ищут в файле).
12821
+ line: Number.isInteger(rawLine) && rawLine > 0 ? rawLine : null,
12822
+ what
12823
+ };
12824
+ }
12825
+ function parseFindingsBlock(body) {
12826
+ const byPhrase = READ_ONLY_PHRASE_RE.test(body) ? "read" : null;
12827
+ const blocks = [...body.matchAll(FINDINGS_BLOCK_RE)];
12828
+ const last = blocks[blocks.length - 1];
12829
+ if (!last) return { findings: null, mode: byPhrase };
12830
+ let payload;
12831
+ try {
12832
+ payload = JSON.parse(last[1]);
12833
+ } catch {
12834
+ console.warn("[gh] \u0431\u043B\u043E\u043A findings \u0432 \u0432\u0435\u0440\u0434\u0438\u043A\u0442-\u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0435 \u043D\u0435 \u0440\u0430\u0437\u043E\u0431\u0440\u0430\u043D (\u0431\u0438\u0442\u044B\u0439 JSON) \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0437\u0430\u0441\u0447\u0438\u0442\u0430\u043D, \u043D\u0430\u0445\u043E\u0434\u043E\u043A \u043D\u0435\u0442");
12835
+ return { findings: null, mode: byPhrase };
12836
+ }
12837
+ const arr = Array.isArray(payload) ? payload : payload && typeof payload === "object" && Array.isArray(payload.findings) ? payload.findings : null;
12838
+ const mode = payload && typeof payload === "object" && !Array.isArray(payload) ? oneOf(["ran", "read"], payload.mode) ?? byPhrase : byPhrase;
12839
+ if (!arr) {
12840
+ console.warn("[gh] \u0431\u043B\u043E\u043A findings \u0432 \u0432\u0435\u0440\u0434\u0438\u043A\u0442-\u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0435 \u043D\u0435 \u0441\u043E\u0434\u0435\u0440\u0436\u0438\u0442 \u043C\u0430\u0441\u0441\u0438\u0432\u0430 \u043D\u0430\u0445\u043E\u0434\u043E\u043A \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0437\u0430\u0441\u0447\u0438\u0442\u0430\u043D, \u043D\u0430\u0445\u043E\u0434\u043E\u043A \u043D\u0435\u0442");
12841
+ return { findings: null, mode };
12842
+ }
12843
+ const capped = arr.slice(0, FINDINGS_MAX);
12844
+ if (arr.length > capped.length) {
12845
+ console.warn(`[gh] \u0432 \u0431\u043B\u043E\u043A\u0435 findings ${arr.length} \u0437\u0430\u043F\u0438\u0441\u0435\u0439 \u2014 \u0431\u0435\u0440\u0443 \u043F\u0435\u0440\u0432\u044B\u0435 ${FINDINGS_MAX}, \u043E\u0441\u0442\u0430\u043B\u044C\u043D\u043E\u0435 \u043E\u0442\u0431\u0440\u043E\u0448\u0435\u043D\u043E`);
12846
+ }
12847
+ const findings = capped.map((raw) => normalizeFinding(raw)).filter((f) => f != null);
12848
+ if (findings.length < capped.length) {
12849
+ console.warn("[gh] \u0432 \u0431\u043B\u043E\u043A\u0435 findings \u043F\u0440\u043E\u043F\u0443\u0449\u0435\u043D\u043E \u0437\u0430\u043F\u0438\u0441\u0435\u0439 \u0431\u0435\u0437 \u0441\u0443\u0442\u0438: " + (capped.length - findings.length));
12850
+ }
12851
+ return { findings, mode };
12852
+ }
12744
12853
  var realCommentsRunner = (prUrl) => runGh(["pr", "view", prUrl, "--json", "comments"], GH_CALL_TIMEOUT_MS);
12745
12854
  var VERDICT_RE = /^[^A-Za-z]*(APPROVED?|LGTM|(?:REQUEST(?:ED)?\s+)?CHANGES(?:\s+REQUESTED)?)\b/i;
12746
12855
  var APPROVE_RE = /^(APPROVE|LGTM)/i;
@@ -12748,11 +12857,15 @@ function verdictLabel(verdict) {
12748
12857
  return verdict === "approve" ? "\u2705 APPROVE" : "\u274C REQUEST CHANGES";
12749
12858
  }
12750
12859
  async function latestVerdictComment(prUrl, sinceIso, run = realCommentsRunner) {
12751
- return scanPrComments(prUrl, sinceIso, run, (body) => {
12860
+ const found = await scanPrComments(prUrl, sinceIso, run, (body) => {
12752
12861
  const m = body.match(VERDICT_RE);
12753
12862
  if (!m) return null;
12754
- return APPROVE_RE.test(m[1]) ? "approve" : "changes";
12863
+ const verdict = APPROVE_RE.test(m[1]) ? "approve" : "changes";
12864
+ const { findings, mode } = parseFindingsBlock(body);
12865
+ return { verdict, findings, reviewMode: mode };
12755
12866
  });
12867
+ if (!found) return null;
12868
+ return { verdict: found.verdict.verdict, createdAt: found.createdAt, findings: found.verdict.findings, reviewMode: found.verdict.reviewMode };
12756
12869
  }
12757
12870
  async function scanPrComments(prUrl, sinceIso, run, pick4) {
12758
12871
  if (!isGithubPrUrl(prUrl)) return null;
@@ -18808,14 +18921,19 @@ function ciTrustLine(prUrl, verifyMode) {
18808
18921
  return `\u041D\u0435 \u043F\u0435\u0440\u0435\u0433\u043E\u043D\u044F\u0439 \u0442\u0435\u0441\u0442-\u0441\u044C\u044E\u0442/typecheck \u0437\u0430\u043D\u043E\u0432\u043E \u2014 \u044D\u0442\u043E \u0443\u0436\u0435 \u0441\u0434\u0435\u043B\u0430\u043B CI \u043D\u0430 \u044D\u0442\u043E\u043C \u0436\u0435 \u043A\u043E\u043C\u043C\u0438\u0442\u0435 (\u0434\u0432\u043E\u0439\u043D\u043E\u0439 \u043F\u0440\u043E\u0433\u043E\u043D \u043D\u0430 \u043C\u0430\u0448\u0438\u043D\u0435 \u0432\u043B\u0430\u0434\u0435\u043B\u044C\u0446\u0430 \u043E\u0442\u043C\u0435\u043D\u0451\u043D, TASWM-249). \u041F\u0440\u043E\u0447\u0438\u0442\u0430\u0439 \u0435\u0433\u043E \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442 \u0441\u0430\u043C: ${readChecks}. \u0414\u0430\u043B\u044C\u0448\u0435 \u0442\u0440\u0438 \u0440\u0430\u0437\u043D\u044B\u0445 \u0441\u043B\u0443\u0447\u0430\u044F: \u2460 \u0445\u043E\u0442\u044C \u043E\u0434\u0438\u043D \u0447\u0435\u043A \u0435\u0449\u0451 PENDING \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0432\u043E\u043E\u0431\u0449\u0435 \u043D\u0435 \u0432\u044B\u043D\u043E\u0441\u0438: \u043D\u0435 \u043F\u043E\u0441\u0442\u0438 REQUEST CHANGES, \u043D\u0435 \u0437\u043E\u0432\u0438 done(), \u043F\u0440\u043E\u0441\u0442\u043E \u0437\u0430\u0432\u0435\u0440\u0448\u0438 \u0441\u0435\u0441\u0441\u0438\u044E \u043A\u0430\u043A \u0435\u0441\u0442\u044C \u2014 \u0440\u043E\u0439 \u0441\u0430\u043C \u0437\u0430\u043C\u0435\u0442\u0438\u0442, \u0447\u0442\u043E \u0442\u044B \u0436\u0434\u0451\u0448\u044C CI, \u0438 \u0440\u0430\u0437\u0431\u0443\u0434\u0438\u0442 \u0434\u043E\u0434\u0435\u043B\u0430\u0442\u044C \u0440\u0435\u0432\u044C\u044E, \u043A\u043E\u0433\u0434\u0430 \u0432\u0441\u0435 \u0447\u0435\u043A\u0438 \u0437\u0430\u0432\u0435\u0440\u0448\u0430\u0442\u0441\u044F (\u044D\u0442\u043E \u0448\u0442\u0430\u0442\u043D\u044B\u0439 \u043F\u0443\u0442\u044C, \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0430 \u0437\u0432\u0430\u0442\u044C \u043D\u0435 \u043D\u0443\u0436\u043D\u043E). \u2461 \u0432\u0441\u0435 \u0447\u0435\u043A\u0438 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0438\u0441\u044C \u0438 \u0435\u0441\u0442\u044C FAIL \u2014 REQUEST CHANGES, \u043D\u0430\u0437\u043E\u0432\u0438 \u043F\u043E\u0438\u043C\u0451\u043D\u043D\u043E, \u0447\u0442\u043E \u0443\u043F\u0430\u043B\u043E. \u2462 \u043D\u0430 \u044D\u0442\u043E\u043C \u043A\u043E\u043C\u043C\u0438\u0442\u0435 \u0447\u0435\u043A\u043E\u0432 \u043D\u0435\u0442 \u0432\u043E\u0432\u0441\u0435 \u2014 \u0442\u043E\u0436\u0435 REQUEST CHANGES: \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u043D\u0435\u0447\u0435\u043C (\u0443 \u044D\u0442\u043E\u0433\u043E \u043F\u0440\u043E\u0435\u043A\u0442\u0430 verifyMode=ci, \u0442\u043E \u0435\u0441\u0442\u044C CI \u043E\u0431\u044F\u0437\u0430\u043D \u0431\u044B\u0442\u044C). ${dontTrustAuthor}`;
18809
18922
  }
18810
18923
  var REVIEW_DEFECT_CLASSES = [
18811
- "\u041A\u043B\u0430\u0441\u0441\u044B \u0434\u0435\u0444\u0435\u043A\u0442\u043E\u0432 \u2014 \u043F\u0440\u043E\u0439\u0434\u0438 \u043F\u043E \u043A\u0430\u0436\u0434\u043E\u043C\u0443 \u0438 \u0434\u0430\u0439 \u043E\u0442\u0432\u0435\u0442 \u0432 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0435; \xAB\u043D\u0435 \u043F\u0440\u0438\u043C\u0435\u043D\u0438\u043C\u043E\xBB \u2014 \u0437\u0430\u043A\u043E\u043D\u043D\u044B\u0439 \u043E\u0442\u0432\u0435\u0442, \u043D\u043E \u043D\u0430\u0437\u0432\u0430\u043D\u043D\u044B\u0439 \u0438 \u0441 \u043F\u0440\u0438\u0447\u0438\u043D\u043E\u0439 \u0432 \u043F\u043E\u043B\u0441\u0442\u0440\u043E\u043A\u0438 (\xAB\u0440\u0443\u0447\u0435\u043A \u043D\u0435 \u0434\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u043E\xBB, \xAB\u043F\u0435\u0440\u0435\u0447\u0438\u0441\u043B\u0435\u043D\u0438\u0439 \u043D\u0435 \u0442\u0440\u043E\u043D\u0443\u0442\u043E\xBB). \u0428\u0435\u0441\u0442\u044C \u0440\u0430\u0437 \xAB\u043D\u0435 \u043F\u0440\u0438\u043C\u0435\u043D\u0438\u043C\u043E\xBB \u0431\u0435\u0437 \u043F\u0440\u0438\u0447\u0438\u043D \u2014 \u043D\u0435 \u0440\u0435\u0432\u044C\u044E, \u0430 \u043E\u0442\u043F\u0438\u0441\u043A\u0430:",
18924
+ "\u041A\u043B\u0430\u0441\u0441\u044B \u0434\u0435\u0444\u0435\u043A\u0442\u043E\u0432 \u2014 \u043F\u0440\u043E\u0439\u0434\u0438 \u043F\u043E \u043A\u0430\u0436\u0434\u043E\u043C\u0443 \u0438 \u0434\u0430\u0439 \u043E\u0442\u0432\u0435\u0442 \u0432 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0435; \xAB\u043D\u0435 \u043F\u0440\u0438\u043C\u0435\u043D\u0438\u043C\u043E\xBB \u2014 \u0437\u0430\u043A\u043E\u043D\u043D\u044B\u0439 \u043E\u0442\u0432\u0435\u0442, \u043D\u043E \u043D\u0430\u0437\u0432\u0430\u043D\u043D\u044B\u0439 \u0438 \u0441 \u043F\u0440\u0438\u0447\u0438\u043D\u043E\u0439 \u0432 \u043F\u043E\u043B\u0441\u0442\u0440\u043E\u043A\u0438 (\xAB\u0440\u0443\u0447\u0435\u043A \u043D\u0435 \u0434\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u043E\xBB, \xAB\u043F\u0435\u0440\u0435\u0447\u0438\u0441\u043B\u0435\u043D\u0438\u0439 \u043D\u0435 \u0442\u0440\u043E\u043D\u0443\u0442\u043E\xBB). \xAB\u041D\u0435 \u043F\u0440\u0438\u043C\u0435\u043D\u0438\u043C\u043E\xBB \u0431\u0435\u0437 \u043F\u0440\u0438\u0447\u0438\u043D\u044B \u043F\u043E \u0432\u0441\u0435\u043C \u043F\u0443\u043D\u043A\u0442\u0430\u043C \u043F\u043E\u0434\u0440\u044F\u0434 \u2014 \u043D\u0435 \u0440\u0435\u0432\u044C\u044E, \u0430 \u043E\u0442\u043F\u0438\u0441\u043A\u0430:",
18812
18925
  "- [ ] \u041F\u0440\u0430\u0432\u0430 \u0430\u043A\u0442\u043E\u0440\u0430 \u0438 \u0432\u043E\u0440\u043A\u0441\u043F\u0435\u0439\u0441: \u043A\u0442\u043E \u0444\u0438\u0437\u0438\u0447\u0435\u0441\u043A\u0438 \u043C\u043E\u0436\u0435\u0442 \u043F\u043E\u0437\u0432\u0430\u0442\u044C \u043D\u043E\u0432\u0443\u044E \u0440\u0443\u0447\u043A\u0443 \u0438\u043B\u0438 \u043D\u0430\u0436\u0430\u0442\u044C \u043A\u043D\u043E\u043F\u043A\u0443 \u2014 \u0447\u0435\u043B\u043E\u0432\u0435\u043A, \u0434\u0440\u043E\u043D \u0440\u043E\u044F, \u0433\u043E\u0441\u0442\u044C, \u0443\u0447\u0430\u0441\u0442\u043D\u0438\u043A \u0427\u0423\u0416\u041E\u0413\u041E \u0432\u043E\u0440\u043A\u0441\u043F\u0435\u0439\u0441\u0430? \u0414\u0435\u0439\u0441\u0442\u0432\u0438\u0435, \u043A\u043E\u0442\u043E\u0440\u043E\u0435 \u0437\u0430\u0434\u0430\u0447\u0430 \u0437\u0430\u0432\u043E\u0434\u0438\u043B\u0430 \u0440\u0430\u0434\u0438 \u0420\u0415\u0428\u0415\u041D\u0418\u042F \u0427\u0415\u041B\u041E\u0412\u0415\u041A\u0410 (\u043F\u0443\u0431\u043B\u0438\u043A\u0430\u0446\u0438\u044F, \u0430\u043F\u0440\u0443\u0432, \u043E\u043F\u043B\u0430\u0442\u0430, \u0440\u0430\u0441\u0441\u044B\u043B\u043A\u0430), \u043E\u0431\u044F\u0437\u0430\u043D\u043E \u043E\u0442\u0431\u0438\u0432\u0430\u0442\u044C \u0430\u0433\u0435\u043D\u0442\u0430 \u044F\u0432\u043D\u043E (denyAgentActor \u0438 \u0430\u043D\u0430\u043B\u043E\u0433\u0438), \u0430 \u043D\u0435 \u043F\u043E\u043B\u0430\u0433\u0430\u0442\u044C\u0441\u044F \u043D\u0430 \u0442\u043E, \u0447\u0442\u043E \u0443 \u043D\u0435\u0433\u043E \u0440\u043E\u043B\u044C \u043F\u043E\u0436\u0438\u0436\u0435 \u2014 \u0443 \u0434\u0440\u043E\u043D\u0430 \u043E\u043D\u0430 \u043E\u0431\u044B\u0447\u043D\u043E \u043A\u0430\u043A \u0443 \u0432\u043B\u0430\u0434\u0435\u043B\u044C\u0446\u0430. \u041B\u044E\u0431\u043E\u0439 id, \u043F\u0440\u0438\u0435\u0445\u0430\u0432\u0448\u0438\u0439 \u0432 \u0442\u0435\u043B\u0435 \u0437\u0430\u043F\u0440\u043E\u0441\u0430, \u043E\u0431\u044F\u0437\u0430\u043D \u0441\u0432\u0435\u0440\u044F\u0442\u044C\u0441\u044F \u0441 \u0432\u043E\u0440\u043A\u0441\u043F\u0435\u0439\u0441\u043E\u043C \u0437\u0432\u043E\u043D\u044F\u0449\u0435\u0433\u043E: \u0440\u043E\u0443\u0442 \u0431\u0435\u0437 \u0442\u0430\u043A\u043E\u0439 \u0441\u0432\u0435\u0440\u043A\u0438 \u2014 \u043A\u0440\u043E\u0441\u0441-\u0442\u0435\u043D\u0430\u043D\u0442\u043D\u0430\u044F \u0434\u044B\u0440\u0430, \u0438 \u0432 \u044D\u0442\u043E\u043C \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u044D\u0442\u043E \u0441\u0430\u043C\u044B\u0439 \u0447\u0430\u0441\u0442\u044B\u0439 \u043A\u043B\u0430\u0441\u0441.",
18813
18926
  "- [ ] \u041F\u043E\u043B\u043D\u043E\u0442\u0430 \u0432\u0430\u0440\u0438\u0430\u043D\u0442\u043E\u0432: PR \u0434\u043E\u0431\u0430\u0432\u0438\u043B \u0432\u0430\u0440\u0438\u0430\u043D\u0442 \u0432 enum/union/\u043D\u0430\u0431\u043E\u0440 \u0441\u0442\u0440\u043E\u043A \u2014 \u043D\u0430\u0439\u0434\u0438 \u0412\u0421\u0415 \u043C\u0435\u0441\u0442\u0430, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u044D\u0442\u0438 \u0432\u0430\u0440\u0438\u0430\u043D\u0442\u044B \u043F\u0435\u0440\u0435\u0447\u0438\u0441\u043B\u044F\u044E\u0442 (\u0431\u0435\u043B\u044B\u0435 \u0441\u043F\u0438\u0441\u043A\u0438, switch, \u043C\u0430\u043F\u043F\u0438\u043D\u0433\u0438, \u0443\u0441\u043B\u043E\u0432\u0438\u044F \u0432 UI), \u0438 \u043F\u0440\u043E\u0432\u0435\u0440\u044C, \u0447\u0442\u043E \u043D\u043E\u0432\u044B\u0439 \u0442\u0430\u043C \u0435\u0441\u0442\u044C. typecheck \u044D\u0442\u043E \u043F\u0440\u043E\u043F\u0443\u0441\u043A\u0430\u0435\u0442, \u043A\u043E\u0433\u0434\u0430 \u0441\u043F\u0438\u0441\u043E\u043A \u2014 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435, \u0430 \u043D\u0435 exhaustive-switch. \u042D\u0442\u043E \u041E\u0414\u0418\u041D grep \u043F\u043E \u0438\u043C\u0435\u043D\u0438 \u0442\u0438\u043F\u0430 \u0438\u043B\u0438 \u043F\u043E \u0441\u0430\u043C\u043E\u043C\u0443 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u044E, \u0430 \u043D\u0435 \u0447\u0442\u0435\u043D\u0438\u0435 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F \xAB\u0434\u043B\u044F \u043F\u043E\u043D\u0438\u043C\u0430\u043D\u0438\u044F\xBB \u2014 \u043A\u043E\u043D\u0442\u0443\u0440 \u044D\u043A\u043E\u043D\u043E\u043C\u0438\u0438 \u0442\u0430\u043A\u043E\u0433\u043E \u043D\u0435 \u0437\u0430\u043F\u0440\u0435\u0449\u0430\u0435\u0442.",
18814
18927
  "- [ ] \u0412\u0441\u0435 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u044F \u0438\u0441\u0442\u043E\u0447\u043D\u0438\u043A\u0430: \u0443 \u0441\u0443\u0449\u043D\u043E\u0441\u0442\u0438 \u043F\u043E\u0447\u0442\u0438 \u0432\u0441\u0435\u0433\u0434\u0430 \u0431\u043E\u043B\u044C\u0448\u0435 \u0434\u0432\u0443\u0445 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0439 (\u0432\u044B\u043A\u043B\u044E\u0447\u0435\u043D\u043E, \u043F\u0430\u0443\u0437\u0430, drain, \u043C\u043E\u043B\u0447\u0438\u0442, \u0434\u0430\u043D\u043D\u044B\u0445 \u0435\u0449\u0451 \u043D\u0435\u0442, \u0434\u043E\u0441\u0442\u0443\u043F \u0437\u0430\u043A\u0440\u044B\u0442). \u0420\u0430\u0437\u043E\u0431\u0440\u0430\u043D\u044B \u0412\u0421\u0415 \u0438\u043B\u0438 \u0442\u043E\u043B\u044C\u043A\u043E happy path \u0438 \u043E\u0434\u043D\u0430 \u043E\u0448\u0438\u0431\u043A\u0430? \u0427\u0430\u0441\u0442\u043E \u043F\u043E\u043B\u043D\u044B\u0439 \u043D\u0430\u0431\u043E\u0440 \u0443\u0436\u0435 \u043B\u0435\u0436\u0438\u0442 \u0440\u044F\u0434\u043E\u043C \u0432 \u044F\u0434\u0440\u0435 \u2014 \u043F\u043E\u0438\u0449\u0438 \u0435\u0433\u043E, \u043F\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043C \u0441\u0447\u0438\u0442\u0430\u0442\u044C \u043F\u0435\u0440\u0435\u0447\u0438\u0441\u043B\u0435\u043D\u0438\u0435 \u043F\u043E\u043B\u043D\u044B\u043C.",
18815
18928
  "- [ ] \u041E\u0448\u0438\u0431\u043A\u0430 \u0434\u043E\u0445\u043E\u0434\u0438\u0442 \u0434\u043E \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0430: \u043E\u0442\u043A\u0430\u0437 \u0437\u0430\u043F\u0440\u043E\u0441\u0430, \u043F\u0440\u043E\u0432\u0430\u043B \u0432\u0430\u043B\u0438\u0434\u0430\u0446\u0438\u0438, \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442 \u2014 \u0432\u0438\u0434\u043D\u044B \u0432 \u0438\u043D\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435, \u0430 \u043D\u0435 \u0442\u043E\u043B\u044C\u043A\u043E \u0432 console.warn/\u043B\u043E\u0433. \u041A\u043D\u043E\u043F\u043A\u0430, \u043A\u043E\u0442\u043E\u0440\u0430\u044F \u043C\u043E\u043B\u0447\u0430 \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u0441\u0434\u0435\u043B\u0430\u043B\u0430, \u2014 \u0434\u0435\u0444\u0435\u043A\u0442, \u0434\u0430\u0436\u0435 \u0435\u0441\u043B\u0438 \u0441\u0435\u0440\u0432\u0435\u0440 \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u043E.",
18816
18929
  "- [ ] \u0414\u0435\u0444\u043E\u043B\u0442 \u043F\u0440\u0438 \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0438 \u0434\u0430\u043D\u043D\u044B\u0445: \u043F\u043E\u043B\u044F \u043D\u0435\u0442, \u043E\u0442\u0432\u0435\u0442 \u043F\u0443\u0441\u0442\u043E\u0439, \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u044C \u043C\u043E\u043B\u0447\u0438\u0442 \u2014 \u043A\u0443\u0434\u0430 \u043F\u0430\u0434\u0430\u0435\u0442 \u0440\u0435\u0448\u0435\u043D\u0438\u0435? \u0414\u043B\u044F \u0443\u0434\u0430\u043B\u044F\u044E\u0449\u0438\u0445, \u043F\u0443\u0431\u043B\u0438\u043A\u0443\u044E\u0449\u0438\u0445 \u0438 \u0441\u043F\u0438\u0441\u044B\u0432\u0430\u044E\u0449\u0438\u0445 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u0434\u0435\u0444\u043E\u043B\u0442 \u043E\u0431\u044F\u0437\u0430\u043D \u0431\u044B\u0442\u044C \u0432 \u0431\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u0443\u044E \u0441\u0442\u043E\u0440\u043E\u043D\u0443 (\u043D\u0435 \u0434\u0435\u043B\u0430\u0442\u044C), \u0430 \xAB\u043D\u0435\u0442 \u0434\u0430\u043D\u043D\u044B\u0445\xBB \u043D\u0435 \u0441\u043C\u0435\u0435\u0442 \u0447\u0438\u0442\u0430\u0442\u044C\u0441\u044F \u043A\u0430\u043A \xAB\u0447\u0438\u0441\u0442\u043E, \u043C\u043E\u0436\u043D\u043E\xBB.",
18817
18930
  "- [ ] \u0422\u0435\u0441\u0442\u044B PR \u0434\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u044E\u0442 \u0437\u0430\u044F\u0432\u043B\u0435\u043D\u043D\u043E\u0435. \u0415\u0441\u043B\u0438 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435 \u0423\u0416\u0415 \u043F\u043E\u0434\u043D\u044F\u0442\u043E (\u0432 worktree \u0435\u0441\u0442\u044C node_modules \u0438 \u043F\u0430\u043A\u0435\u0442\u044B \u0441\u043E\u0431\u0440\u0430\u043D\u044B): \u0441\u043D\u0438\u043C\u0438 \u0432 \u041A\u041E\u0414\u0415 \u0442\u0443 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0443, \u043A\u043E\u0442\u043E\u0440\u0443\u044E \u043D\u043E\u0432\u044B\u0439 \u0442\u0435\u0441\u0442 \u044F\u043A\u043E\u0431\u044B \u0441\u0442\u043E\u0440\u043E\u0436\u0438\u0442 (\u043E\u0434\u043D\u0443 \u0441\u0442\u0440\u043E\u043A\u0443 \u2014 \u0433\u0435\u0439\u0442, \u0443\u0441\u043B\u043E\u0432\u0438\u0435, \u0432\u0430\u043B\u0438\u0434\u0430\u0446\u0438\u044E), \u043F\u0440\u043E\u0433\u043E\u043D\u0438 \u041E\u0414\u0418\u041D \u044D\u0442\u043E\u0442 \u0442\u0435\u0441\u0442 (\u043D\u0435 \u0432\u0435\u0441\u044C \u0441\u044C\u044E\u0442), \u0432\u0435\u0440\u043D\u0438 \u0444\u0430\u0439\u043B \u043A\u0430\u043A \u0431\u044B\u043B\u043E. \u0422\u0435\u0441\u0442 \u043E\u0431\u044F\u0437\u0430\u043D \u043F\u043E\u043A\u0440\u0430\u0441\u043D\u0435\u0442\u044C; \u043E\u0441\u0442\u0430\u043B\u0441\u044F \u0437\u0435\u043B\u0451\u043D\u044B\u043C \u2014 \u043D\u0430\u0445\u043E\u0434\u043A\u0430: \u043E\u043D \u043D\u0435 \u0441\u0442\u043E\u0440\u043E\u0436\u0438\u0442 \u043D\u0438\u0447\u0435\u0433\u043E.",
18818
- " \u041E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435 \u041D\u0415 \u043F\u043E\u0434\u043D\u044F\u0442\u043E \u2014 \u0442\u0430\u043A \u0438 \u0431\u0443\u0434\u0435\u0442: \u0442\u0435\u0431\u0435 \u043F\u043E\u0434\u043D\u044F\u043B\u0438 \u0447\u0438\u0441\u0442\u044B\u0439 \u0447\u0435\u043A\u0430\u0443\u0442, \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0438 \u043D\u0438\u043A\u0442\u043E \u043D\u0435 \u0441\u0442\u0430\u0432\u0438\u043B. \u041F\u043E\u0434\u043D\u0438\u043C\u0430\u0442\u044C \u0438\u0445 \u041D\u0415 \u041D\u0410\u0414\u041E, \u044D\u0442\u043E \u0434\u043E\u0440\u043E\u0436\u0435 \u0432\u0441\u0435\u0433\u043E \u0440\u0435\u0432\u044C\u044E: \u0441\u043A\u0430\u0436\u0438 \u0432 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0435 \xAB\u043F\u0440\u043E\u0433\u043D\u0430\u0442\u044C \u043D\u0435 \u043C\u043E\u0433, \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F \u043D\u0435\u0442\xBB \u0438 \u043F\u0440\u043E\u0432\u0435\u0440\u044C \u0442\u0435\u0441\u0442 \u0427\u0422\u0415\u041D\u0418\u0415\u041C. \u0422\u0440\u0438 \u0441\u043F\u043E\u0441\u043E\u0431\u0430 \u0441\u043E\u0432\u0440\u0430\u0442\u044C \u0437\u0435\u043B\u0451\u043D\u044B\u043C, \u0432\u0438\u0434\u043D\u044B\u0435 \u0433\u043B\u0430\u0437\u0430\u043C\u0438: (\u0430) \u0438\u0441\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u0435 \u043F\u0440\u043E\u0433\u043B\u043E\u0447\u0435\u043D\u043E (`try/except`, \u0437\u043E\u043D\u0442 \u0441\u0442\u043E\u0440\u043E\u0436\u0430, `.catch(() => {})`) \u2014 \u0442\u0435\u0441\u0442 \u043D\u0435 \u043F\u0430\u0434\u0430\u0435\u0442 \u0442\u0430\u043C, \u0433\u0434\u0435 \u043E\u0431\u044F\u0437\u0430\u043D; (\u0431) \u0430\u0441\u0441\u0435\u0440\u0442 \u043D\u0430 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435, \u0432\u0435\u0440\u043D\u043E\u0435 \u0432\u0441\u0435\u0433\u0434\u0430, \u043D\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E \u043E\u0442 \u043F\u0440\u0430\u0432\u043A\u0438; (\u0432) \u0442\u0435\u0441\u0442 \u043D\u0435 \u043F\u0440\u043E\u0445\u043E\u0434\u0438\u0442 \u0447\u0435\u0440\u0435\u0437 \u0438\u0437\u043C\u0435\u043D\u0451\u043D\u043D\u044B\u0439 \u0433\u0435\u0439\u0442 \u0432\u043E\u0432\u0441\u0435 \u2014 \u0434\u0451\u0440\u0433\u0430\u0435\u0442 \u0441\u043E\u0441\u0435\u0434\u043D\u0438\u0439 \u043F\u0443\u0442\u044C."
18931
+ " \u041E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435 \u041D\u0415 \u043F\u043E\u0434\u043D\u044F\u0442\u043E \u2014 \u0442\u0430\u043A \u0438 \u0431\u0443\u0434\u0435\u0442: \u0442\u0435\u0431\u0435 \u043F\u043E\u0434\u043D\u044F\u043B\u0438 \u0447\u0438\u0441\u0442\u044B\u0439 \u0447\u0435\u043A\u0430\u0443\u0442, \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0438 \u043D\u0438\u043A\u0442\u043E \u043D\u0435 \u0441\u0442\u0430\u0432\u0438\u043B. \u041F\u043E\u0434\u043D\u0438\u043C\u0430\u0442\u044C \u0438\u0445 \u041D\u0415 \u041D\u0410\u0414\u041E, \u044D\u0442\u043E \u0434\u043E\u0440\u043E\u0436\u0435 \u0432\u0441\u0435\u0433\u043E \u0440\u0435\u0432\u044C\u044E: \u0441\u043A\u0430\u0436\u0438 \u0432 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0435 \xAB\u043F\u0440\u043E\u0433\u043D\u0430\u0442\u044C \u043D\u0435 \u043C\u043E\u0433, \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F \u043D\u0435\u0442\xBB \u0438 \u043F\u0440\u043E\u0432\u0435\u0440\u044C \u0442\u0435\u0441\u0442 \u0427\u0422\u0415\u041D\u0418\u0415\u041C. \u0422\u0440\u0438 \u0441\u043F\u043E\u0441\u043E\u0431\u0430 \u0441\u043E\u0432\u0440\u0430\u0442\u044C \u0437\u0435\u043B\u0451\u043D\u044B\u043C, \u0432\u0438\u0434\u043D\u044B\u0435 \u0433\u043B\u0430\u0437\u0430\u043C\u0438: (\u0430) \u0438\u0441\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u0435 \u043F\u0440\u043E\u0433\u043B\u043E\u0447\u0435\u043D\u043E (`try/except`, \u0437\u043E\u043D\u0442 \u0441\u0442\u043E\u0440\u043E\u0436\u0430, `.catch(() => {})`) \u2014 \u0442\u0435\u0441\u0442 \u043D\u0435 \u043F\u0430\u0434\u0430\u0435\u0442 \u0442\u0430\u043C, \u0433\u0434\u0435 \u043E\u0431\u044F\u0437\u0430\u043D; (\u0431) \u0430\u0441\u0441\u0435\u0440\u0442 \u043D\u0430 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435, \u0432\u0435\u0440\u043D\u043E\u0435 \u0432\u0441\u0435\u0433\u0434\u0430, \u043D\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E \u043E\u0442 \u043F\u0440\u0430\u0432\u043A\u0438; (\u0432) \u0442\u0435\u0441\u0442 \u043D\u0435 \u043F\u0440\u043E\u0445\u043E\u0434\u0438\u0442 \u0447\u0435\u0440\u0435\u0437 \u0438\u0437\u043C\u0435\u043D\u0451\u043D\u043D\u044B\u0439 \u0433\u0435\u0439\u0442 \u0432\u043E\u0432\u0441\u0435 \u2014 \u0434\u0451\u0440\u0433\u0430\u0435\u0442 \u0441\u043E\u0441\u0435\u0434\u043D\u0438\u0439 \u043F\u0443\u0442\u044C.",
18932
+ "- [ ] \u041D\u0430\u043F\u0438\u0441\u0430\u043D\u043E \u0437\u0430\u043D\u043E\u0432\u043E \u0442\u043E, \u0447\u0442\u043E \u0443\u0436\u0435 \u0435\u0441\u0442\u044C \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438: \u043D\u043E\u0432\u044B\u0439 \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442, \u0443\u0442\u0438\u043B\u0438\u0442\u0430 \u0438\u043B\u0438 \u0445\u0435\u043B\u043F\u0435\u0440 \u043F\u0440\u0438 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044E\u0449\u0435\u043C \u0430\u043D\u0430\u043B\u043E\u0433\u0435 \u0432 \u0434\u0438\u0437\u0430\u0439\u043D-\u0441\u0438\u0441\u0442\u0435\u043C\u0435 \u0438\u043B\u0438 \u043E\u0431\u0449\u0435\u043C \u043F\u0430\u043A\u0435\u0442\u0435 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 (\u0433\u0434\u0435 \u043E\u043D\u0438 \u043B\u0435\u0436\u0430\u0442 \u2014 \u0432 CLAUDE.md \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F, \u0435\u0441\u043B\u0438 \u043E\u043D \u0435\u0441\u0442\u044C). \u0414\u0443\u0431\u043B\u0438\u043A\u0430\u0442 \u043F\u043E\u0434 \u0434\u0440\u0443\u0433\u0438\u043C \u0438\u043C\u0435\u043D\u0435\u043C \u2014 \u043D\u0430\u0445\u043E\u0434\u043A\u0430.",
18933
+ '- [ ] \u0418\u043C\u043F\u043E\u0440\u0442 \u0447\u0435\u0440\u0435\u0437 \u0433\u0440\u0430\u043D\u0438\u0446\u0443 \u043F\u0430\u043A\u0435\u0442\u0430: \u043E\u0442\u043D\u043E\u0441\u0438\u0442\u0435\u043B\u044C\u043D\u044B\u0439 \u043F\u0443\u0442\u044C \u0432\u0433\u043B\u0443\u0431\u044C \u0447\u0443\u0436\u043E\u0433\u043E \u043F\u0430\u043A\u0435\u0442\u0430 \u0438\u043B\u0438 subpath \u0432\u0438\u0434\u0430 `<\u043F\u0430\u043A\u0435\u0442>/src/\u2026` \u0432\u043C\u0435\u0441\u0442\u043E \u043F\u0443\u0431\u043B\u0438\u0447\u043D\u043E\u0433\u043E \u044D\u043A\u0441\u043F\u043E\u0440\u0442\u0430 \u2014 \u043D\u0430\u0445\u043E\u0434\u043A\u0430. \u0410\u0432\u0442\u043E\u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u0433\u0440\u0430\u043D\u0438\u0446 \u043F\u0430\u043A\u0435\u0442\u043E\u0432, \u0435\u0441\u043B\u0438 \u043E\u043D\u0430 \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u0435\u0441\u0442\u044C, \u043E\u0431\u044B\u0447\u043D\u043E \u043B\u043E\u0432\u0438\u0442 \u0442\u043E\u043B\u044C\u043A\u043E \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438\u0435 \u043E\u0442\u043D\u043E\u0441\u0438\u0442\u0435\u043B\u044C\u043D\u044B\u0435 `from "../\u2026"`; subpath \u0447\u0435\u0440\u0435\u0437 \u0438\u043C\u044F \u043F\u0430\u043A\u0435\u0442\u0430 \u0438 require/\u0434\u0438\u043D\u0430\u043C\u0438\u0447\u0435\u0441\u043A\u0438\u0439 import \u043C\u0438\u043C\u043E \u043D\u0435\u0451 \u043F\u0440\u043E\u0445\u043E\u0434\u044F\u0442 \u043D\u0435\u0437\u0430\u043C\u0435\u0447\u0435\u043D\u043D\u044B\u043C\u0438.',
18934
+ "- [ ] \u0410\u0431\u0441\u0442\u0440\u0430\u043A\u0446\u0438\u044F \u0431\u0435\u0437 \u0432\u0442\u043E\u0440\u043E\u0433\u043E \u043F\u043E\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043B\u044F: \u0438\u043D\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u0444\u0430\u0431\u0440\u0438\u043A\u0430 \u0438\u043B\u0438 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440 \xAB\u043D\u0430 \u0432\u044B\u0440\u043E\u0441\u0442\xBB \u043F\u0440\u0438 \u043E\u0434\u043D\u043E\u043C \u0432\u044B\u0437\u044B\u0432\u0430\u044E\u0449\u0435\u043C \u2014 \u043D\u0430\u0445\u043E\u0434\u043A\u0430. \u041F\u0435\u0440\u0435\u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u043C\u044B\u043C \u043A\u043E\u0434 \u0434\u0435\u043B\u0430\u044E\u0442 \u043F\u0440\u0438 \u0412\u0422\u041E\u0420\u041E\u041C \u0440\u0435\u0430\u043B\u044C\u043D\u043E\u043C \u043F\u043E\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043B\u0435, \u043D\u0435 \u0437\u0430\u0440\u0430\u043D\u0435\u0435.",
18935
+ "- [ ] \u0414\u0443\u0431\u043B\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u0435 \u043B\u043E\u0433\u0438\u043A\u0438: \u0442\u043E\u0442 \u0436\u0435 \u0440\u0430\u0441\u0447\u0451\u0442, \u043D\u043E\u0440\u043C\u0430\u043B\u0438\u0437\u0430\u0446\u0438\u044F \u0438\u043B\u0438 \u0444\u043E\u0440\u043C\u0430\u0442 \u0443\u0436\u0435 \u0435\u0441\u0442\u044C \u0432 \u0441\u043E\u0441\u0435\u0434\u043D\u0435\u043C \u0444\u0430\u0439\u043B\u0435 \u2014 \u043D\u0430\u0445\u043E\u0434\u043A\u0430.",
18936
+ "- [ ] \u041F\u0440\u0430\u0432\u043A\u0430 \u043E\u0431\u0449\u0435\u0433\u043E \u0445\u0440\u0435\u0431\u0442\u0430 \u0432\u043F\u0435\u0440\u0435\u043C\u0435\u0448\u043A\u0443 \u0441 \u0444\u0438\u0447\u0435\u0439: \u0447\u0442\u043E \u0432 \u044D\u0442\u043E\u043C \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u0445\u0440\u0435\u0431\u0435\u0442 (\u043E\u0431\u0449\u0438\u0435 \u043F\u0430\u043A\u0435\u0442\u044B, \u0441\u0445\u0435\u043C\u0430 \u0411\u0414, \u0440\u043E\u0443\u0442\u0438\u043D\u0433\u2026) \u2014 \u0441\u043C\u043E\u0442\u0440\u0438 CLAUDE.md \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u0438\u043B\u0438 \u043F\u0440\u043E\u0444\u0438\u043B\u044C \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F (spine). \u0422\u0430\u043A\u0430\u044F \u043F\u0440\u0430\u0432\u043A\u0430 \u0432 \u043E\u0434\u043D\u043E\u043C PR \u0441 \u0444\u0438\u0447\u0435\u0439 \u2014 \u043D\u0430\u0445\u043E\u0434\u043A\u0430: \u0445\u0440\u0435\u0431\u0442\u0443 \u043D\u0443\u0436\u0435\u043D \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u044B\u0439 PR."
18819
18937
  ].join("\n");
18820
18938
  function buildReviewChecklist(reviewRun, files, priorReview, verifyMode = SWARM_VERIFY) {
18821
18939
  const header = files.length ? `\u0424\u0430\u0439\u043B\u044B \u0434\u0438\u0444\u0444\u0430 (${files.length}):
@@ -18839,7 +18957,8 @@ ${priorReview}` : "",
18839
18957
  "- [ ] \u041D\u043E\u0432\u044B\u0435 \u043A\u043E\u043C\u043C\u0438\u0442\u044B \u043D\u0435 \u043F\u0440\u0438\u0432\u043D\u0435\u0441\u043B\u0438 \u043D\u043E\u0432\u044B\u0445 \u043F\u0440\u043E\u0431\u043B\u0435\u043C: \u0433\u043E\u043D\u043A\u0438/\u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0438/UI \u043F\u043E \u043F\u0440\u0438\u043C\u0435\u043D\u0438\u043C\u043E\u0441\u0442\u0438 \u2014 \u0438 \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u043E \u043F\u0440\u0430\u0432\u0430 \u0430\u043A\u0442\u043E\u0440\u0430, \u0434\u0435\u0444\u043E\u043B\u0442 \u043F\u0440\u0438 \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0438 \u0434\u0430\u043D\u043D\u044B\u0445, \u043F\u043E\u043B\u043D\u043E\u0442\u0430 \u0432\u0430\u0440\u0438\u0430\u043D\u0442\u043E\u0432. \u041F\u0440\u0430\u0432\u043A\u0430 \u043F\u043E \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0443 \u0442\u0440\u043E\u0433\u0430\u0435\u0442 \u0442\u0435 \u0436\u0435 \u043C\u0435\u0441\u0442\u0430, \u0433\u0434\u0435 \u0434\u0435\u0444\u0435\u043A\u0442 \u0438 \u0431\u044B\u043B, \u043F\u043E\u044D\u0442\u043E\u043C\u0443 \u0438\u043C\u0435\u043D\u043D\u043E \u043E\u043D\u0430 \u0447\u0430\u0449\u0435 \u0432\u0441\u0435\u0433\u043E \u043F\u0440\u0438\u043D\u043E\u0441\u0438\u0442 \u043D\u043E\u0432\u044B\u0439.",
18840
18958
  // Единственный класс из REVIEW_DEFECT_CLASSES, который на втором круге обязателен:
18841
18959
  // правки по вердикту почти всегда приносят НОВЫЕ тесты, и именно тут прошёл #819.
18842
- // Остальные пять классов заново не гоняем — ре-чек намеренно короткий (TASWM-437).
18960
+ // Остальные классы (включая пять про качество кода, TASWM-559) заново не гоняем —
18961
+ // ре-чек намеренно короткий (TASWM-437).
18843
18962
  "- [ ] \u041D\u043E\u0432\u044B\u0435 \u0442\u0435\u0441\u0442\u044B \u043F\u0440\u0430\u0432\u043A\u0438: \u0441\u043D\u0438\u043C\u0438 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0443, \u043A\u043E\u0442\u043E\u0440\u0443\u044E \u0442\u0435\u0441\u0442 \u0441\u0442\u043E\u0440\u043E\u0436\u0438\u0442, \u043F\u0440\u043E\u0433\u043E\u043D\u0438 \u041E\u0414\u0418\u041D \u044D\u0442\u043E\u0442 \u0442\u0435\u0441\u0442, \u0432\u0435\u0440\u043D\u0438 \u043A\u0430\u043A \u0431\u044B\u043B\u043E \u2014 \u043E\u043D \u043E\u0431\u044F\u0437\u0430\u043D \u043F\u043E\u043A\u0440\u0430\u0441\u043D\u0435\u0442\u044C."
18844
18963
  ].filter(Boolean).join("\n");
18845
18964
  }
@@ -18886,6 +19005,19 @@ var REVIEW_MISSION_BLOCK = [
18886
19005
  "\u2022 \u041D\u0430\u0445\u043E\u0434\u043A\u0430 \u0434\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0442\u0441\u044F \u0421\u0426\u0415\u041D\u0410\u0420\u0418\u0415\u041C: \xAB\u0442\u0430\u043A\u043E\u0439 \u0432\u0445\u043E\u0434 \u0438\u043B\u0438 \u0442\u0430\u043A\u043E\u0435 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435 \u2192 \u0432\u043E\u0442 \u0442\u0430\u043A\u043E\u0439 \u043D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\xBB. \xAB\u0412\u044B\u0433\u043B\u044F\u0434\u0438\u0442 \u043D\u0435\u0431\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u043E\xBB, \xAB\u0441\u0442\u043E\u0438\u0442 \u043F\u0435\u0440\u0435\u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C\xBB, \xAB\u0436\u0435\u043B\u0430\u0442\u0435\u043B\u044C\u043D\u043E \u043F\u043E\u043A\u0440\u044B\u0442\u044C \u0442\u0435\u0441\u0442\u0430\u043C\u0438\xBB \u2014 \u043D\u0435 \u043D\u0430\u0445\u043E\u0434\u043A\u0438; \u0442\u0430\u043A\u043E\u0435 \u043D\u0435 \u043F\u0438\u0448\u0438.",
18887
19006
  "\u2022 \u041D\u043E\u043B\u044C \u043D\u0430\u0445\u043E\u0434\u043E\u043A \u2014 \u0437\u0430\u043A\u043E\u043D\u043D\u044B\u0439 \u0432\u0435\u0440\u0434\u0438\u043A\u0442, \u043D\u043E \u0442\u043E\u0433\u0434\u0430 \u043D\u0430\u0437\u043E\u0432\u0438, \u0427\u0422\u041E \u0438\u043C\u0435\u043D\u043D\u043E \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u043B \u0438 \u043A\u0430\u043A\u0438\u043C \u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0435\u043C. APPROVE \u0431\u0435\u0437 \u044D\u0442\u043E\u0433\u043E \u0440\u043E\u0439 \u0447\u0438\u0442\u0430\u0435\u0442 \u043A\u0430\u043A \u043F\u0435\u0440\u0435\u0441\u043A\u0430\u0437, \u0438 \u0447\u0435\u043B\u043E\u0432\u0435\u043A \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0435\u0442 PR \u0437\u0430\u043D\u043E\u0432\u043E \u0432\u0440\u0443\u0447\u043D\u0443\u044E."
18888
19007
  ].join("\n");
19008
+ var REVIEW_FINDINGS_BLOCK = [
19009
+ "\u{1F4E6} \u041F\u041E\u0421\u041B\u0415\u0414\u041D\u0418\u0419 \u0411\u041B\u041E\u041A \u041A\u041E\u041C\u041C\u0415\u041D\u0422\u0410 \u2014 \u041D\u0410\u0425\u041E\u0414\u041A\u0418 \u041C\u0410\u0428\u0418\u041D\u041E\u0427\u0418\u0422\u0410\u0415\u041C\u041E (\u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u043E). \u0421\u0432\u043E\u0431\u043E\u0434\u043D\u044B\u0439 \u0442\u0435\u043A\u0441\u0442 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u0440\u043E\u0439 \u0441\u0447\u0438\u0442\u0430\u0442\u044C \u043D\u0435 \u0443\u043C\u0435\u0435\u0442; \u044D\u0442\u043E\u0442 \u0431\u043B\u043E\u043A \u043E\u043D \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0435\u0442 \u0438 \u0441\u043A\u043B\u0430\u0434\u044B\u0432\u0430\u0435\u0442 \u0432 \u043B\u0435\u043D\u0442\u0443 \u043F\u0440\u043E\u0433\u043E\u043D\u0430. \u041F\u0435\u0447\u0430\u0442\u0430\u0439 \u0435\u0433\u043E \u0412\u0421\u0415\u0413\u0414\u0410 \u0438 \u0432\u0441\u0435\u0433\u0434\u0430 \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u043C:",
19010
+ "```findings",
19011
+ '{"mode": "read", "findings": [{"category": "correctness", "severity": "blocker", "path": "packages/api/src/foo.ts", "line": 42, "what": "\u043E\u0434\u043D\u043E \u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0435\u043D\u0438\u0435: \u0442\u0430\u043A\u043E\u0439 \u0432\u0445\u043E\u0434 \u2192 \u0442\u0430\u043A\u043E\u0439 \u043D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442"}]}',
19012
+ "```",
19013
+ // Словари — ИЗ ОДНОГО МЕСТА с разбором (gh.FINDING_CATEGORIES/FINDING_SEVERITIES): значение,
19014
+ // названное промптом, но не знакомое парсеру, молча превращается в `null` — находка приедет
19015
+ // без класса, и никто этого не заметит.
19016
+ `\u2022 \`category\` \u2014 \u043E\u0434\u0438\u043D \u0438\u0437: ${FINDING_CATEGORIES.join(" | ")}. \`severity\` \u2014 ${FINDING_SEVERITIES.join(" | ")}. \`path\`/\`line\` \u2014 \u0444\u0430\u0439\u043B \u0438 \u0441\u0442\u0440\u043E\u043A\u0430, \u0433\u0434\u0435 \u0434\u0435\u0444\u0435\u043A\u0442 (\u0441\u0442\u0440\u043E\u043A\u0438 \u043D\u0435 \u0437\u043D\u0430\u0435\u0448\u044C \u2014 \`null\`). \`what\` \u2014 \u041E\u0414\u041D\u041E \u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0435\u043D\u0438\u0435.`,
19017
+ "\u2022 `mode` \u2014 \u043A\u0430\u043A \u0442\u044B \u0441\u0443\u0434\u0438\u043B: `ran` (\u0441\u0430\u043C \u043F\u0440\u043E\u0433\u043D\u0430\u043B \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438) \u0438\u043B\u0438 `read` (\u0441\u0443\u0434\u0438\u043B \u0447\u0442\u0435\u043D\u0438\u0435\u043C, \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435 \u043D\u0435 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u043B \u2014 \u044D\u0442\u043E \u0437\u0430\u043A\u043E\u043D\u043D\u043E \u0438 \u0438\u043C\u0435\u043D\u043D\u043E \u044D\u0442\u043E\u0433\u043E \u043E\u0442 \u0442\u0435\u0431\u044F \u0436\u0434\u0443\u0442). \u041D\u0430\u0437\u043E\u0432\u0438 \u0447\u0435\u0441\u0442\u043D\u043E: \u044D\u0442\u043E \u043D\u0435 \u043E\u0446\u0435\u043D\u043A\u0430 \u0442\u0435\u0431\u0435, \u044D\u0442\u043E \u0432\u0435\u0441 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u0432 \u0430\u043D\u0430\u043B\u0438\u0442\u0438\u043A\u0435.",
19018
+ '\u2022 \u041D\u0430\u0445\u043E\u0434\u043E\u043A \u043D\u0435\u0442 \u2014 \u0431\u043B\u043E\u043A \u0432\u0441\u0451 \u0440\u0430\u0432\u043D\u043E \u043F\u0435\u0447\u0430\u0442\u0430\u0439, \u0441 \u043F\u0443\u0441\u0442\u044B\u043C \u043C\u0430\u0441\u0441\u0438\u0432\u043E\u043C: {"mode": "read", "findings": []}.',
19019
+ "\u2022 \u041F\u0435\u0440\u0432\u0443\u044E \u0441\u0442\u0440\u043E\u043A\u0443 \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0430 \u0431\u043B\u043E\u043A \u041D\u0415 \u043E\u0442\u043C\u0435\u043D\u044F\u0435\u0442: APPROVE / REQUEST CHANGES \u0437\u0430\u0433\u043B\u0430\u0432\u043D\u044B\u043C\u0438 \u2014 \u043A\u0430\u043A \u0431\u044B\u043B\u043E. \u041D\u0430\u0445\u043E\u0434\u043A\u0438 \u0432 \u0431\u043B\u043E\u043A\u0435 \u0434\u0443\u0431\u043B\u0438\u0440\u0443\u044E\u0442 \u0442\u043E, \u0447\u0442\u043E \u0442\u044B \u0438 \u0442\u0430\u043A \u043D\u0430\u043F\u0438\u0441\u0430\u043B \u0442\u0435\u043A\u0441\u0442\u043E\u043C, \u0430 \u043D\u0435 \u0437\u0430\u043C\u0435\u043D\u044F\u044E\u0442 \u0440\u0430\u0437\u0431\u043E\u0440."
19020
+ ].join("\n");
18889
19021
  function buildReviewerPrompt(reviewRun, reviewer, checklist, branch, attachmentsBlock, verifyMode = SWARM_VERIFY, pendingRules = []) {
18890
19022
  const name = reviewer.name || `#${reviewer.id}`;
18891
19023
  const rulesBlock = renderPendingRulesBlock(pendingRules);
@@ -18919,15 +19051,17 @@ function buildReviewerPrompt(reviewRun, reviewer, checklist, branch, attachments
18919
19051
  "",
18920
19052
  checklist,
18921
19053
  "",
19054
+ REVIEW_FINDINGS_BLOCK,
19055
+ "",
18922
19056
  "\u041F\u043E\u0440\u044F\u0434\u043E\u043A:",
18923
19057
  ...verifyMode === "local" ? [
18924
19058
  `1. \u041F\u0440\u043E\u0439\u0434\u0438 \u0447\u0435\u043A-\u043B\u0438\u0441\u0442 \u0432\u044B\u0448\u0435: \u0433\u0435\u0439\u0442 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u043F\u0440\u043E\u0433\u043E\u043D\u0438 \u0421\u0410\u041C \u0432 \u0441\u0432\u043E\u0451\u043C worktree (\u044D\u0442\u043E \u0438 \u0435\u0441\u0442\u044C \u0434\u043E\u043A\u0430\u0437\u0430\u0442\u0435\u043B\u044C\u0441\u0442\u0432\u043E \u0437\u0435\u043B\u0451\u043D\u043E\u0433\u043E \u2014 \u0443 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 verifyMode=local), ${classesStep}\u043F\u0440\u043E\u0432\u0435\u0440\u044C \u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0438/\u0433\u043E\u043D\u043A\u0438/UI/per-page meta \u043F\u043E \u043F\u0440\u0438\u043C\u0435\u043D\u0438\u043C\u043E\u0441\u0442\u0438.`,
18925
19059
  " \u0427\u0435\u043A\u043E\u0432 \u043D\u0430 \u043A\u043E\u043C\u043C\u0438\u0442\u0435 \u043D\u0435\u0442 (\u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u043D\u0435\u0442 CI) \u2014 \u044D\u0442\u043E \u041D\u0415 \u0434\u0435\u0444\u0435\u043A\u0442 PR \u0438 \u041D\u0415 \u043F\u043E\u0432\u043E\u0434 \u0434\u043B\u044F REQUEST CHANGES: \u043D\u0435 \u0436\u0434\u0438 \u0438\u0445, \u043D\u0435 \u0442\u0440\u0435\u0431\u0443\u0439 \u0437\u0430\u0432\u0435\u0441\u0442\u0438 workflow, \u0441\u0443\u0434\u0438 \u043F\u043E \u0441\u0432\u043E\u0435\u043C\u0443 \u043F\u0440\u043E\u0433\u043E\u043D\u0443. \u041A\u0440\u0430\u0441\u043D\u044B\u0439 \u0417\u0410\u0412\u0415\u0420\u0428\u0418\u0412\u0428\u0418\u0419\u0421\u042F \u0447\u0435\u043A, \u0435\u0441\u043B\u0438 CI \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u0432\u0441\u0451-\u0442\u0430\u043A\u0438 \u0435\u0441\u0442\u044C, \u2014 \u043F\u043E-\u043F\u0440\u0435\u0436\u043D\u0435\u043C\u0443 \u0431\u043B\u043E\u043A.",
18926
- `2. \u041F\u0440\u043E\u0433\u043E\u043D \u0437\u0430\u043A\u043E\u043D\u0447\u0435\u043D \u2014 \u043E\u0441\u0442\u0430\u0432\u044C \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439 \u043D\u0430 \u0441\u0430\u043C PR: \`gh pr comment ${prUrl} --body "..."\` (\u041D\u0415 \`gh pr review\` \u2014 GitHub \u0435\u0433\u043E \u043E\u0442\u043A\u043B\u043E\u043D\u0438\u0442 \u043D\u0430 \u0441\u0432\u043E\u0451\u043C PR). \u041D\u0430\u0447\u043D\u0438 \u0441 **APPROVE** \u0438\u043B\u0438 **REQUEST CHANGES** \u0437\u0430\u0433\u043B\u0430\u0432\u043D\u044B\u043C\u0438, \u0434\u0430\u043B\u044C\u0448\u0435 \u2014 \u043A\u043E\u043D\u043A\u0440\u0435\u0442\u0438\u043A\u0430: \u0447\u0442\u043E \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u043B (\u043F\u0440\u0438\u0432\u0435\u0434\u0438 \u0432\u044B\u0432\u043E\u0434 \u0441\u0432\u043E\u0435\u0433\u043E \u043F\u0440\u043E\u0433\u043E\u043D\u0430), \u0447\u0442\u043E \u043D\u0430\u0448\u0451\u043B; \u0434\u043B\u044F REQUEST CHANGES \u2014 \u0442\u043E\u0447\u043D\u044B\u0435 \u0444\u0430\u0439\u043B\u044B/\u0441\u0442\u0440\u043E\u043A\u0438 \u0438 \u0447\u0442\u043E \u0447\u0438\u043D\u0438\u0442\u044C. \u041F\u0440\u0435\u0442\u0435\u043D\u0437\u0438\u044F \u043A \u0438\u043D\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0435 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F (\u043D\u0435\u0442 CI, \u043A\u043E\u043D\u0444\u0438\u0433\u0438, \u0434\u0435\u043F\u043B\u043E\u0439) \u2014 \u043D\u0435 \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u043F\u043E PR: \u0441\u0447\u0438\u0442\u0430\u0435\u0448\u044C, \u0447\u0442\u043E \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044E \u043D\u0443\u0436\u0435\u043D CI, \u2014 \u0441\u043A\u0430\u0436\u0438 \u044D\u0442\u043E \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u043E\u0439 \u0441\u0442\u0440\u043E\u043A\u043E\u0439 \u0432 \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0435, \u043D\u043E \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u043E\u0442 \u043D\u0435\u0451 \u043D\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u0442.`
19060
+ `2. \u041F\u0440\u043E\u0433\u043E\u043D \u0437\u0430\u043A\u043E\u043D\u0447\u0435\u043D \u2014 \u043E\u0441\u0442\u0430\u0432\u044C \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439 \u043D\u0430 \u0441\u0430\u043C PR: \`gh pr comment ${prUrl} --body "..."\` (\u041D\u0415 \`gh pr review\` \u2014 GitHub \u0435\u0433\u043E \u043E\u0442\u043A\u043B\u043E\u043D\u0438\u0442 \u043D\u0430 \u0441\u0432\u043E\u0451\u043C PR). \u041D\u0430\u0447\u043D\u0438 \u0441 **APPROVE** \u0438\u043B\u0438 **REQUEST CHANGES** \u0437\u0430\u0433\u043B\u0430\u0432\u043D\u044B\u043C\u0438, \u0434\u0430\u043B\u044C\u0448\u0435 \u2014 \u043A\u043E\u043D\u043A\u0440\u0435\u0442\u0438\u043A\u0430: \u0447\u0442\u043E \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u043B (\u043F\u0440\u0438\u0432\u0435\u0434\u0438 \u0432\u044B\u0432\u043E\u0434 \u0441\u0432\u043E\u0435\u0433\u043E \u043F\u0440\u043E\u0433\u043E\u043D\u0430), \u0447\u0442\u043E \u043D\u0430\u0448\u0451\u043B; \u0434\u043B\u044F REQUEST CHANGES \u2014 \u0442\u043E\u0447\u043D\u044B\u0435 \u0444\u0430\u0439\u043B\u044B/\u0441\u0442\u0440\u043E\u043A\u0438 \u0438 \u0447\u0442\u043E \u0447\u0438\u043D\u0438\u0442\u044C; \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u043C \u0431\u043B\u043E\u043A\u043E\u043C \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0430 \u2014 \`\`\`findings (\u0444\u043E\u0440\u043C\u0430\u0442 \u0432\u044B\u0448\u0435, \u043F\u0435\u0447\u0430\u0442\u0430\u0435\u0442\u0441\u044F \u0432\u0441\u0435\u0433\u0434\u0430). \u041F\u0440\u0435\u0442\u0435\u043D\u0437\u0438\u044F \u043A \u0438\u043D\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0435 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F (\u043D\u0435\u0442 CI, \u043A\u043E\u043D\u0444\u0438\u0433\u0438, \u0434\u0435\u043F\u043B\u043E\u0439) \u2014 \u043D\u0435 \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u043F\u043E PR: \u0441\u0447\u0438\u0442\u0430\u0435\u0448\u044C, \u0447\u0442\u043E \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044E \u043D\u0443\u0436\u0435\u043D CI, \u2014 \u0441\u043A\u0430\u0436\u0438 \u044D\u0442\u043E \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u043E\u0439 \u0441\u0442\u0440\u043E\u043A\u043E\u0439 \u0432 \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0435, \u043D\u043E \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u043E\u0442 \u043D\u0435\u0451 \u043D\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u0442.`
18927
19061
  ] : [
18928
19062
  `1. \u041F\u0440\u043E\u0439\u0434\u0438 \u0447\u0435\u043A-\u043B\u0438\u0441\u0442 \u0432\u044B\u0448\u0435: \u0441\u0442\u0430\u0442\u0443\u0441 CI \u043F\u043E \u044D\u0442\u043E\u043C\u0443 \u043A\u043E\u043C\u043C\u0438\u0442\u0443 \u0447\u0438\u0442\u0430\u0439 \u0441\u0430\u043C (\u043D\u0435 \u0432\u0435\u0440\u044C \u043E\u0442\u0447\u0451\u0442\u0443 \u0430\u0432\u0442\u043E\u0440\u0430 PR, \u043D\u043E \u0438 \u043D\u0435 \u043F\u0435\u0440\u0435\u0433\u043E\u043D\u044F\u0439 CI \u0432\u0440\u0443\u0447\u043D\u0443\u044E), ${classesStep}\u043F\u0440\u043E\u0432\u0435\u0440\u044C \u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0438/\u0433\u043E\u043D\u043A\u0438/UI/per-page meta \u043F\u043E \u043F\u0440\u0438\u043C\u0435\u043D\u0438\u043C\u043E\u0441\u0442\u0438.`,
18929
19063
  " \u0415\u0441\u043B\u0438 \u0445\u043E\u0442\u044C \u043E\u0434\u0438\u043D \u0447\u0435\u043A \u0435\u0449\u0451 PENDING \u2014 \u043D\u0430 \u044D\u0442\u043E\u043C \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0441\u044C: \u0448\u0430\u0433\u0438 2 \u0438 3 \u043D\u0438\u0436\u0435 \u043F\u0440\u043E\u043F\u0443\u0441\u0442\u0438 \u0446\u0435\u043B\u0438\u043A\u043E\u043C, \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u043D\u0435 \u0432\u044B\u043D\u043E\u0441\u0438, \u043F\u0440\u043E\u0441\u0442\u043E \u0437\u0430\u0432\u0435\u0440\u0448\u0438 \u0441\u0435\u0441\u0441\u0438\u044E (\u0441\u043C. \u0447\u0435\u043A-\u043B\u0438\u0441\u0442 \u0432\u044B\u0448\u0435, \u043F\u043E\u0447\u0435\u043C\u0443 \u044D\u0442\u043E \u0431\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u043E \u0438 \u0447\u0442\u043E \u043F\u0440\u043E\u0438\u0437\u043E\u0439\u0434\u0451\u0442 \u0434\u0430\u043B\u044C\u0448\u0435).",
18930
- `2. \u0427\u0435\u043A\u0438 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0438\u0441\u044C (\u0437\u0435\u043B\u0451\u043D\u044B\u0435 \u0438\u043B\u0438 \u0441 \u043A\u0440\u0430\u0441\u043D\u044B\u043C) \u0438\u043B\u0438 \u043D\u0430 \u044D\u0442\u043E\u043C \u043A\u043E\u043C\u043C\u0438\u0442\u0435 \u0438\u0445 \u0432\u043E\u0432\u0441\u0435 \u043D\u0435\u0442 \u2014 \u043E\u0441\u0442\u0430\u0432\u044C \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439 \u043D\u0430 \u0441\u0430\u043C PR: \`gh pr comment ${prUrl} --body "..."\` (\u041D\u0415 \`gh pr review\` \u2014 GitHub \u0435\u0433\u043E \u043E\u0442\u043A\u043B\u043E\u043D\u0438\u0442 \u043D\u0430 \u0441\u0432\u043E\u0451\u043C PR). \u041D\u0430\u0447\u043D\u0438 \u0441 **APPROVE** \u0438\u043B\u0438 **REQUEST CHANGES** \u0437\u0430\u0433\u043B\u0430\u0432\u043D\u044B\u043C\u0438, \u0434\u0430\u043B\u044C\u0448\u0435 \u2014 \u043A\u043E\u043D\u043A\u0440\u0435\u0442\u0438\u043A\u0430: \u0447\u0442\u043E \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u043B, \u0447\u0442\u043E \u043D\u0430\u0448\u0451\u043B; \u0434\u043B\u044F REQUEST CHANGES \u2014 \u0442\u043E\u0447\u043D\u044B\u0435 \u0444\u0430\u0439\u043B\u044B/\u0441\u0442\u0440\u043E\u043A\u0438 \u0438 \u0447\u0442\u043E \u0447\u0438\u043D\u0438\u0442\u044C.`
19064
+ `2. \u0427\u0435\u043A\u0438 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0438\u0441\u044C (\u0437\u0435\u043B\u0451\u043D\u044B\u0435 \u0438\u043B\u0438 \u0441 \u043A\u0440\u0430\u0441\u043D\u044B\u043C) \u0438\u043B\u0438 \u043D\u0430 \u044D\u0442\u043E\u043C \u043A\u043E\u043C\u043C\u0438\u0442\u0435 \u0438\u0445 \u0432\u043E\u0432\u0441\u0435 \u043D\u0435\u0442 \u2014 \u043E\u0441\u0442\u0430\u0432\u044C \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0439 \u043D\u0430 \u0441\u0430\u043C PR: \`gh pr comment ${prUrl} --body "..."\` (\u041D\u0415 \`gh pr review\` \u2014 GitHub \u0435\u0433\u043E \u043E\u0442\u043A\u043B\u043E\u043D\u0438\u0442 \u043D\u0430 \u0441\u0432\u043E\u0451\u043C PR). \u041D\u0430\u0447\u043D\u0438 \u0441 **APPROVE** \u0438\u043B\u0438 **REQUEST CHANGES** \u0437\u0430\u0433\u043B\u0430\u0432\u043D\u044B\u043C\u0438, \u0434\u0430\u043B\u044C\u0448\u0435 \u2014 \u043A\u043E\u043D\u043A\u0440\u0435\u0442\u0438\u043A\u0430: \u0447\u0442\u043E \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u043B, \u0447\u0442\u043E \u043D\u0430\u0448\u0451\u043B; \u0434\u043B\u044F REQUEST CHANGES \u2014 \u0442\u043E\u0447\u043D\u044B\u0435 \u0444\u0430\u0439\u043B\u044B/\u0441\u0442\u0440\u043E\u043A\u0438 \u0438 \u0447\u0442\u043E \u0447\u0438\u043D\u0438\u0442\u044C; \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u043C \u0431\u043B\u043E\u043A\u043E\u043C \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0430 \u2014 \`\`\`findings (\u0444\u043E\u0440\u043C\u0430\u0442 \u0432\u044B\u0448\u0435, \u043F\u0435\u0447\u0430\u0442\u0430\u0435\u0442\u0441\u044F \u0432\u0441\u0435\u0433\u0434\u0430).`
18931
19065
  ],
18932
19066
  `3. \u0412\u044B\u0437\u043E\u0432\u0438 done(${prUrl}, verdict: "approve"|"changes", summary: "\u043A\u043E\u0440\u043E\u0442\u043A\u043E \u0447\u0442\u043E \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u043B \u0438 \u0447\u0442\u043E \u0440\u0435\u0448\u0438\u043B") \u2014 \u0440\u043E\u0439 \u0441\u0430\u043C \u0440\u0430\u0437\u043D\u0435\u0441\u0451\u0442 summary \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u043E\u043C \u0432 \u0437\u0430\u0434\u0430\u0447\u0443-\u0438\u0441\u0442\u043E\u0447\u043D\u0438\u043A, \u0430 verdict \u0437\u0430\u043F\u0438\u0448\u0435\u0442 \u043D\u0430 \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0435\u043C\u044B\u0439 \u043F\u0440\u043E\u0433\u043E\u043D (\u0431\u0435\u0439\u0434\u0436 \u0432 \u0432\u0435\u0431\u0435). \u0422\u044B \u0441\u0430\u043C \u0440\u0435\u0432\u044C\u044E \u041D\u0415 \u043F\u043E\u0434\u043B\u0435\u0436\u0438\u0448\u044C \u2014 \u0440\u0435\u043A\u0443\u0440\u0441\u0438\u0438 \u043D\u0435\u0442.`,
18933
19067
  ...rulesBlock ? ["", rulesBlock] : [],
@@ -20395,12 +20529,28 @@ async function postScrapedVerdictReport(deps, runId, taskSlug, verdict, droneNam
20395
20529
  deps.db.addEvent(runId, "step", `\u0434\u0435\u043C\u043E\u043D-\u043E\u0442\u0447\u0451\u0442 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u0432 \u0437\u0430\u0434\u0430\u0447\u0443 ${taskSlug} \u043D\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D: ${e instanceof Error ? e.message : String(e)}`);
20396
20530
  });
20397
20531
  }
20532
+ function recordReviewFindings(deps, rv, comment) {
20533
+ const findings = comment.findings ?? null;
20534
+ const mode = comment.reviewMode ?? null;
20535
+ if (findings) for (const f of findings) deps.db.addEvent(rv.id, "finding", JSON.stringify(f));
20536
+ if (mode) deps.db.addEvent(rv.id, "review_mode", mode);
20537
+ if (!findings) deps.db.addEvent(rv.id, "step", "\u043C\u0430\u0448\u0438\u043D\u043E\u0447\u0438\u0442\u0430\u0435\u043C\u043E\u0433\u043E \u0431\u043B\u043E\u043A\u0430 \u043D\u0430\u0445\u043E\u0434\u043E\u043A \u0432 \u0432\u0435\u0440\u0434\u0438\u043A\u0442-\u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0435 \u043D\u0435\u0442 (\u0438\u043B\u0438 \u043E\u043D \u0431\u0438\u0442\u044B\u0439) \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0437\u0430\u0441\u0447\u0438\u0442\u0430\u043D \u043A\u0430\u043A \u0435\u0441\u0442\u044C, \u043D\u0430\u0445\u043E\u0434\u043A\u0438 \u043D\u0435 \u0440\u0430\u0437\u043E\u0431\u0440\u0430\u043D\u044B");
20538
+ }
20539
+ async function recordReviewFindingsFromPr(deps, rv) {
20540
+ if (!deps.verdictComment || !rv.pr_url) return;
20541
+ try {
20542
+ const found = await deps.verdictComment(rv.pr_url, rv.created_at);
20543
+ if (found) recordReviewFindings(deps, rv, found);
20544
+ } catch {
20545
+ }
20546
+ }
20398
20547
  async function salvageVerdictFromPrComment(deps, rv, reviewsRunId, since, ctx) {
20399
20548
  if (!deps.verdictComment || !rv.pr_url) return false;
20400
20549
  const found = await deps.verdictComment(rv.pr_url, since).catch(() => null);
20401
20550
  if (!found) return false;
20402
20551
  const verdictText = verdictLabel(found.verdict);
20403
20552
  deps.db.updateRun(reviewsRunId, { review_verdict: found.verdict });
20553
+ recordReviewFindings(deps, rv, found);
20404
20554
  deps.db.addEvent(reviewsRunId, "step", `\u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0440\u0435\u0432\u044C\u044E \u043F\u043E\u0434\u0445\u0432\u0430\u0447\u0435\u043D \u0438\u0437 PR-\u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0430 (${ctx.how}): ${verdictText}`);
20405
20555
  await closeReviewerRun(
20406
20556
  deps,
@@ -20683,6 +20833,7 @@ async function tickOnce(deps) {
20683
20833
  }
20684
20834
  if (run.worktree_path) await deps.removeWorktree(run.repo, run.worktree_path).catch(() => {
20685
20835
  });
20836
+ await recordReviewFindingsFromPr(deps, run);
20686
20837
  deps.db.updateRun(run.id, { status: "done" });
20687
20838
  deps.db.addEvent(run.id, "done", "\u0440\u0435\u0432\u044C\u044E \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u043E, \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0432\u044B\u043D\u0435\u0441\u0435\u043D");
20688
20839
  narrate(deps, `${droneLabel(run)} \u0432\u044B\u043D\u0435\u0441 \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u043F\u043E \u0440\u0435\u0432\u044C\u044E \u2014 \u043F\u0440\u043E\u0433\u043E\u043D \u0437\u0430\u043A\u0440\u044B\u0442.`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taskless-app/swarm",
3
- "version": "0.6.45",
3
+ "version": "0.6.47",
4
4
  "type": "module",
5
5
  "description": "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
6
6
  "keywords": [
@@ -39,7 +39,7 @@
39
39
  "execa": "^10.0.0",
40
40
  "zod": "3.25.76"
41
41
  },
42
- "gitHead": "a30a18d26e9b3a6895d350931b294c3df986ca01",
42
+ "gitHead": "561ecd5dfe7de7c9b1a106552bbef19921c65cbf",
43
43
  "scripts": {
44
44
  "overmind": "NODE_OPTIONS=\"${NODE_OPTIONS:-} --conditions=development\" tsx src/overmind.ts",
45
45
  "swarm": "NODE_OPTIONS=\"${NODE_OPTIONS:-} --conditions=development\" tsx src/cli.ts",