@taskless-app/swarm 0.6.45 → 0.6.46

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",
@@ -3390,7 +3399,7 @@ var init_package = __esm({
3390
3399
  "package.json"() {
3391
3400
  package_default = {
3392
3401
  name: "@taskless-app/swarm",
3393
- version: "0.6.45",
3402
+ version: "0.6.46",
3394
3403
  type: "module",
3395
3404
  description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
3396
3405
  keywords: [
@@ -3441,7 +3450,7 @@ var init_package = __esm({
3441
3450
  execa: "^10.0.0",
3442
3451
  zod: "3.25.76"
3443
3452
  },
3444
- gitHead: "a30a18d26e9b3a6895d350931b294c3df986ca01"
3453
+ gitHead: "09254fc07f88535996f8b3da61c09ce8aeec0e92"
3445
3454
  };
3446
3455
  }
3447
3456
  });
@@ -7589,13 +7598,15 @@ var SwarmClient = class _SwarmClient {
7589
7598
  const key = this.keyByLocal.get(localId);
7590
7599
  if (key) out[prodKey] = runRef(key);
7591
7600
  }
7592
- enqueueRun(run, fields) {
7601
+ enqueueRun(run, fields, opts = {}) {
7593
7602
  if (Object.keys(fields).length === 0) return;
7594
7603
  this.enqueue({
7595
7604
  t: "run",
7596
7605
  runKey: run.runKey,
7597
7606
  taskRef: run.task_slug,
7598
7607
  ...run.workspace_id ? { workspaceId: run.workspace_id } : {},
7608
+ create: opts.create === true,
7609
+ // TASWM-562: пометка есть у КАЖДОЙ новой записи — см. resolveArgs
7599
7610
  fields
7600
7611
  });
7601
7612
  }
@@ -7710,7 +7721,7 @@ var SwarmClient = class _SwarmClient {
7710
7721
  // закрепление (TASWM-291): true только у явного --host
7711
7722
  base_branch: d.base_branch
7712
7723
  // базовая ветка PR (TASWM-285), если задана на спавне
7713
- }));
7724
+ }), { create: true });
7714
7725
  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
7726
  this.addEvent(id, "spawned", spawnNote);
7716
7727
  return this.mat(d);
@@ -7749,7 +7760,7 @@ var SwarmClient = class _SwarmClient {
7749
7760
  runLogKeys(id) {
7750
7761
  const key = this.runs.get(id)?.runKey;
7751
7762
  if (!key) return [];
7752
- const prodId = key.startsWith("p:") ? key.slice(2) : this.prodIdByKey.get(key);
7763
+ const prodId = this.prodIdFor(key);
7753
7764
  if (!prodId) return [key];
7754
7765
  const canonical = `p:${prodId}`;
7755
7766
  const aliases = [key, ...[...this.prodIdByKey.entries()].filter(([, v]) => v === prodId).map(([k]) => k)];
@@ -9487,11 +9498,31 @@ var SwarmClient = class _SwarmClient {
9487
9498
  for (const [full, path] of this.repoPathByFullName) if (path === norm) return full;
9488
9499
  return /^[^/\s]+\/[^/\s]+$/.test(repo) ? repo : void 0;
9489
9500
  }
9501
+ /**
9502
+ * Прод-id прогона по его runKey — ЕДИНСТВЕННЫЙ резолв на пути флаша (TASWM-562) и
9503
+ * в адресах run-лога (runLogKeys). Гидрированный прогон несёт cuid прямо в ключе
9504
+ * (`p:<cuid>`, см. hydrateRun). Карта prodIdByKey ему не подмога: в сайдкар она пишется
9505
+ * только после успешного create-флаша (persistMap), а прогон, РОЖДЁННЫЙ в проде
9506
+ * (claim/hydrate), туда не попадает никогда. Отсюда и брались пустые парные строки:
9507
+ * после рестарта демона recover() планирует флаш ДО первой гидрации, карта пуста —
9508
+ * и лежащий в outbox патч («прогон перешёл в done») заводил в проде второй прогон,
9509
+ * без токенов, цены, времени и PR.
9510
+ */
9511
+ prodIdFor(runKey) {
9512
+ return runKey.startsWith("p:") ? runKey.slice(2) : this.prodIdByKey.get(runKey);
9513
+ }
9490
9514
  async resolveArgs(e) {
9491
9515
  if (e.t === "run") {
9492
9516
  const fields = this.materializeRefs(e.fields);
9493
- const prodId2 = this.prodIdByKey.get(e.runKey);
9517
+ const prodId2 = this.prodIdFor(e.runKey);
9494
9518
  if (prodId2) return { tool: "swarm_run_upsert", args: { runId: prodId2, ...fields } };
9519
+ if (e.create === false) {
9520
+ console.error(
9521
+ `[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(", ")}`
9522
+ );
9523
+ this.poisonedRunKeys.add(e.runKey);
9524
+ return "skip";
9525
+ }
9495
9526
  const taskId = await this.resolveTaskCuid(e.taskRef, this.repoFullNameFor(fields.repo), e.workspaceId);
9496
9527
  if (taskId === "transient") {
9497
9528
  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 +9540,7 @@ var SwarmClient = class _SwarmClient {
9509
9540
  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
9541
  return "skip";
9511
9542
  }
9512
- const prodId = this.prodIdByKey.get(e.runKey);
9543
+ const prodId = this.prodIdFor(e.runKey);
9513
9544
  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
9545
  if (e.t === "event") return { tool: "swarm_event_append", args: { runId: prodId, kind: e.kind, text: e.text, actor: e.actor } };
9515
9546
  return { tool: "swarm_ask", args: { runId: prodId, text: e.text } };
@@ -9522,7 +9553,7 @@ var SwarmClient = class _SwarmClient {
9522
9553
  const out = {};
9523
9554
  for (const [k, v] of Object.entries(fields)) {
9524
9555
  if (isRunRef(v)) {
9525
- const cuid = this.prodIdByKey.get(v.__runRef);
9556
+ const cuid = this.prodIdFor(v.__runRef);
9526
9557
  if (cuid) out[k] = cuid;
9527
9558
  } else {
9528
9559
  out[k] = v;
@@ -9562,6 +9593,7 @@ var SwarmClient = class _SwarmClient {
9562
9593
  if (tries >= _SwarmClient.REJECT_LIMIT) {
9563
9594
  this.pending.shift();
9564
9595
  this.rejected.delete(e.seq);
9596
+ if (e.t === "run" && e.create === true) this.poisonedRunKeys.add(e.runKey);
9565
9597
  console.error(
9566
9598
  `[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
9599
  );
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",
@@ -7827,13 +7836,15 @@ var SwarmClient = class _SwarmClient {
7827
7836
  const key = this.keyByLocal.get(localId);
7828
7837
  if (key) out[prodKey] = runRef(key);
7829
7838
  }
7830
- enqueueRun(run, fields) {
7839
+ enqueueRun(run, fields, opts = {}) {
7831
7840
  if (Object.keys(fields).length === 0) return;
7832
7841
  this.enqueue({
7833
7842
  t: "run",
7834
7843
  runKey: run.runKey,
7835
7844
  taskRef: run.task_slug,
7836
7845
  ...run.workspace_id ? { workspaceId: run.workspace_id } : {},
7846
+ create: opts.create === true,
7847
+ // TASWM-562: пометка есть у КАЖДОЙ новой записи — см. resolveArgs
7837
7848
  fields
7838
7849
  });
7839
7850
  }
@@ -7948,7 +7959,7 @@ var SwarmClient = class _SwarmClient {
7948
7959
  // закрепление (TASWM-291): true только у явного --host
7949
7960
  base_branch: d.base_branch
7950
7961
  // базовая ветка PR (TASWM-285), если задана на спавне
7951
- }));
7962
+ }), { create: true });
7952
7963
  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
7964
  this.addEvent(id, "spawned", spawnNote);
7954
7965
  return this.mat(d);
@@ -7987,7 +7998,7 @@ var SwarmClient = class _SwarmClient {
7987
7998
  runLogKeys(id) {
7988
7999
  const key = this.runs.get(id)?.runKey;
7989
8000
  if (!key) return [];
7990
- const prodId = key.startsWith("p:") ? key.slice(2) : this.prodIdByKey.get(key);
8001
+ const prodId = this.prodIdFor(key);
7991
8002
  if (!prodId) return [key];
7992
8003
  const canonical = `p:${prodId}`;
7993
8004
  const aliases = [key, ...[...this.prodIdByKey.entries()].filter(([, v]) => v === prodId).map(([k]) => k)];
@@ -9725,11 +9736,31 @@ var SwarmClient = class _SwarmClient {
9725
9736
  for (const [full, path] of this.repoPathByFullName) if (path === norm) return full;
9726
9737
  return /^[^/\s]+\/[^/\s]+$/.test(repo) ? repo : void 0;
9727
9738
  }
9739
+ /**
9740
+ * Прод-id прогона по его runKey — ЕДИНСТВЕННЫЙ резолв на пути флаша (TASWM-562) и
9741
+ * в адресах run-лога (runLogKeys). Гидрированный прогон несёт cuid прямо в ключе
9742
+ * (`p:<cuid>`, см. hydrateRun). Карта prodIdByKey ему не подмога: в сайдкар она пишется
9743
+ * только после успешного create-флаша (persistMap), а прогон, РОЖДЁННЫЙ в проде
9744
+ * (claim/hydrate), туда не попадает никогда. Отсюда и брались пустые парные строки:
9745
+ * после рестарта демона recover() планирует флаш ДО первой гидрации, карта пуста —
9746
+ * и лежащий в outbox патч («прогон перешёл в done») заводил в проде второй прогон,
9747
+ * без токенов, цены, времени и PR.
9748
+ */
9749
+ prodIdFor(runKey) {
9750
+ return runKey.startsWith("p:") ? runKey.slice(2) : this.prodIdByKey.get(runKey);
9751
+ }
9728
9752
  async resolveArgs(e) {
9729
9753
  if (e.t === "run") {
9730
9754
  const fields = this.materializeRefs(e.fields);
9731
- const prodId2 = this.prodIdByKey.get(e.runKey);
9755
+ const prodId2 = this.prodIdFor(e.runKey);
9732
9756
  if (prodId2) return { tool: "swarm_run_upsert", args: { runId: prodId2, ...fields } };
9757
+ if (e.create === false) {
9758
+ console.error(
9759
+ `[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(", ")}`
9760
+ );
9761
+ this.poisonedRunKeys.add(e.runKey);
9762
+ return "skip";
9763
+ }
9733
9764
  const taskId = await this.resolveTaskCuid(e.taskRef, this.repoFullNameFor(fields.repo), e.workspaceId);
9734
9765
  if (taskId === "transient") {
9735
9766
  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 +9778,7 @@ var SwarmClient = class _SwarmClient {
9747
9778
  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
9779
  return "skip";
9749
9780
  }
9750
- const prodId = this.prodIdByKey.get(e.runKey);
9781
+ const prodId = this.prodIdFor(e.runKey);
9751
9782
  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
9783
  if (e.t === "event") return { tool: "swarm_event_append", args: { runId: prodId, kind: e.kind, text: e.text, actor: e.actor } };
9753
9784
  return { tool: "swarm_ask", args: { runId: prodId, text: e.text } };
@@ -9760,7 +9791,7 @@ var SwarmClient = class _SwarmClient {
9760
9791
  const out = {};
9761
9792
  for (const [k, v] of Object.entries(fields)) {
9762
9793
  if (isRunRef(v)) {
9763
- const cuid = this.prodIdByKey.get(v.__runRef);
9794
+ const cuid = this.prodIdFor(v.__runRef);
9764
9795
  if (cuid) out[k] = cuid;
9765
9796
  } else {
9766
9797
  out[k] = v;
@@ -9800,6 +9831,7 @@ var SwarmClient = class _SwarmClient {
9800
9831
  if (tries >= _SwarmClient.REJECT_LIMIT) {
9801
9832
  this.pending.shift();
9802
9833
  this.rejected.delete(e.seq);
9834
+ if (e.t === "run" && e.create === true) this.poisonedRunKeys.add(e.runKey);
9803
9835
  console.error(
9804
9836
  `[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
9837
  );
@@ -12314,7 +12346,7 @@ function parkReasonUnretriable(reason) {
12314
12346
  // package.json
12315
12347
  var package_default = {
12316
12348
  name: "@taskless-app/swarm",
12317
- version: "0.6.45",
12349
+ version: "0.6.46",
12318
12350
  type: "module",
12319
12351
  description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
12320
12352
  keywords: [
@@ -12365,7 +12397,7 @@ var package_default = {
12365
12397
  execa: "^10.0.0",
12366
12398
  zod: "3.25.76"
12367
12399
  },
12368
- gitHead: "a30a18d26e9b3a6895d350931b294c3df986ca01"
12400
+ gitHead: "09254fc07f88535996f8b3da61c09ce8aeec0e92"
12369
12401
  };
12370
12402
 
12371
12403
  // src/version.ts
@@ -12741,6 +12773,70 @@ async function closePR(prUrl, run = realRunner) {
12741
12773
  async function postPrComment(prUrl, body, run = realRunner) {
12742
12774
  await run(["pr", "comment", prUrl, "--body", body]);
12743
12775
  }
12776
+ var FINDING_CATEGORIES = [
12777
+ "correctness",
12778
+ "security",
12779
+ "reuse",
12780
+ "boundaries",
12781
+ "over-abstraction",
12782
+ "duplication",
12783
+ "spine",
12784
+ "tests",
12785
+ "ui-rules"
12786
+ ];
12787
+ var FINDING_SEVERITIES = ["blocker", "major", "nit"];
12788
+ var FINDINGS_BLOCK_RE = /```findings[^\n]*\n([\s\S]*?)```/gi;
12789
+ var READ_ONLY_PHRASE_RE = /прогнать не мог|окружения нет/i;
12790
+ var FINDING_WHAT_MAX = 400;
12791
+ var FINDING_PATH_MAX = 300;
12792
+ var FINDINGS_MAX = 50;
12793
+ function oneOf(dict, v) {
12794
+ return typeof v === "string" && dict.includes(v) ? v : null;
12795
+ }
12796
+ function normalizeFinding(raw) {
12797
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
12798
+ const r = raw;
12799
+ const what = typeof r.what === "string" ? r.what.trim().slice(0, FINDING_WHAT_MAX) : "";
12800
+ if (!what) return null;
12801
+ const rawLine = typeof r.line === "number" ? r.line : typeof r.line === "string" && r.line.trim() ? Number(r.line) : NaN;
12802
+ return {
12803
+ category: oneOf(FINDING_CATEGORIES, r.category),
12804
+ severity: oneOf(FINDING_SEVERITIES, r.severity),
12805
+ path: typeof r.path === "string" && r.path.trim() ? r.path.trim().slice(0, FINDING_PATH_MAX) : null,
12806
+ // Номер строки — только целый положительный: «7.5», «-1» и NaN значат, что ревьюер
12807
+ // строку не знал, а не что дефект на строке −1 (по такому числу потом ищут в файле).
12808
+ line: Number.isInteger(rawLine) && rawLine > 0 ? rawLine : null,
12809
+ what
12810
+ };
12811
+ }
12812
+ function parseFindingsBlock(body) {
12813
+ const byPhrase = READ_ONLY_PHRASE_RE.test(body) ? "read" : null;
12814
+ const blocks = [...body.matchAll(FINDINGS_BLOCK_RE)];
12815
+ const last = blocks[blocks.length - 1];
12816
+ if (!last) return { findings: null, mode: byPhrase };
12817
+ let payload;
12818
+ try {
12819
+ payload = JSON.parse(last[1]);
12820
+ } catch {
12821
+ 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");
12822
+ return { findings: null, mode: byPhrase };
12823
+ }
12824
+ const arr = Array.isArray(payload) ? payload : payload && typeof payload === "object" && Array.isArray(payload.findings) ? payload.findings : null;
12825
+ const mode = payload && typeof payload === "object" && !Array.isArray(payload) ? oneOf(["ran", "read"], payload.mode) ?? byPhrase : byPhrase;
12826
+ if (!arr) {
12827
+ 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");
12828
+ return { findings: null, mode };
12829
+ }
12830
+ const capped = arr.slice(0, FINDINGS_MAX);
12831
+ if (arr.length > capped.length) {
12832
+ 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`);
12833
+ }
12834
+ const findings = capped.map((raw) => normalizeFinding(raw)).filter((f) => f != null);
12835
+ if (findings.length < capped.length) {
12836
+ 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));
12837
+ }
12838
+ return { findings, mode };
12839
+ }
12744
12840
  var realCommentsRunner = (prUrl) => runGh(["pr", "view", prUrl, "--json", "comments"], GH_CALL_TIMEOUT_MS);
12745
12841
  var VERDICT_RE = /^[^A-Za-z]*(APPROVED?|LGTM|(?:REQUEST(?:ED)?\s+)?CHANGES(?:\s+REQUESTED)?)\b/i;
12746
12842
  var APPROVE_RE = /^(APPROVE|LGTM)/i;
@@ -12748,11 +12844,15 @@ function verdictLabel(verdict) {
12748
12844
  return verdict === "approve" ? "\u2705 APPROVE" : "\u274C REQUEST CHANGES";
12749
12845
  }
12750
12846
  async function latestVerdictComment(prUrl, sinceIso, run = realCommentsRunner) {
12751
- return scanPrComments(prUrl, sinceIso, run, (body) => {
12847
+ const found = await scanPrComments(prUrl, sinceIso, run, (body) => {
12752
12848
  const m = body.match(VERDICT_RE);
12753
12849
  if (!m) return null;
12754
- return APPROVE_RE.test(m[1]) ? "approve" : "changes";
12850
+ const verdict = APPROVE_RE.test(m[1]) ? "approve" : "changes";
12851
+ const { findings, mode } = parseFindingsBlock(body);
12852
+ return { verdict, findings, reviewMode: mode };
12755
12853
  });
12854
+ if (!found) return null;
12855
+ return { verdict: found.verdict.verdict, createdAt: found.createdAt, findings: found.verdict.findings, reviewMode: found.verdict.reviewMode };
12756
12856
  }
12757
12857
  async function scanPrComments(prUrl, sinceIso, run, pick4) {
12758
12858
  if (!isGithubPrUrl(prUrl)) return null;
@@ -18808,14 +18908,19 @@ function ciTrustLine(prUrl, verifyMode) {
18808
18908
  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
18909
  }
18810
18910
  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:",
18911
+ "\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
18912
  "- [ ] \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
18913
  "- [ ] \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
18914
  "- [ ] \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
18915
  "- [ ] \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
18916
  "- [ ] \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
18917
  "- [ ] \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."
18918
+ " \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.",
18919
+ "- [ ] \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.",
18920
+ '- [ ] \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.',
18921
+ "- [ ] \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.",
18922
+ "- [ ] \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.",
18923
+ "- [ ] \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
18924
  ].join("\n");
18820
18925
  function buildReviewChecklist(reviewRun, files, priorReview, verifyMode = SWARM_VERIFY) {
18821
18926
  const header = files.length ? `\u0424\u0430\u0439\u043B\u044B \u0434\u0438\u0444\u0444\u0430 (${files.length}):
@@ -18839,7 +18944,8 @@ ${priorReview}` : "",
18839
18944
  "- [ ] \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
18945
  // Единственный класс из REVIEW_DEFECT_CLASSES, который на втором круге обязателен:
18841
18946
  // правки по вердикту почти всегда приносят НОВЫЕ тесты, и именно тут прошёл #819.
18842
- // Остальные пять классов заново не гоняем — ре-чек намеренно короткий (TASWM-437).
18947
+ // Остальные классы (включая пять про качество кода, TASWM-559) заново не гоняем —
18948
+ // ре-чек намеренно короткий (TASWM-437).
18843
18949
  "- [ ] \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
18950
  ].filter(Boolean).join("\n");
18845
18951
  }
@@ -18886,6 +18992,19 @@ var REVIEW_MISSION_BLOCK = [
18886
18992
  "\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
18993
  "\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
18994
  ].join("\n");
18995
+ var REVIEW_FINDINGS_BLOCK = [
18996
+ "\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:",
18997
+ "```findings",
18998
+ '{"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"}]}',
18999
+ "```",
19000
+ // Словари — ИЗ ОДНОГО МЕСТА с разбором (gh.FINDING_CATEGORIES/FINDING_SEVERITIES): значение,
19001
+ // названное промптом, но не знакомое парсеру, молча превращается в `null` — находка приедет
19002
+ // без класса, и никто этого не заметит.
19003
+ `\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.`,
19004
+ "\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.",
19005
+ '\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": []}.',
19006
+ "\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."
19007
+ ].join("\n");
18889
19008
  function buildReviewerPrompt(reviewRun, reviewer, checklist, branch, attachmentsBlock, verifyMode = SWARM_VERIFY, pendingRules = []) {
18890
19009
  const name = reviewer.name || `#${reviewer.id}`;
18891
19010
  const rulesBlock = renderPendingRulesBlock(pendingRules);
@@ -18919,15 +19038,17 @@ function buildReviewerPrompt(reviewRun, reviewer, checklist, branch, attachments
18919
19038
  "",
18920
19039
  checklist,
18921
19040
  "",
19041
+ REVIEW_FINDINGS_BLOCK,
19042
+ "",
18922
19043
  "\u041F\u043E\u0440\u044F\u0434\u043E\u043A:",
18923
19044
  ...verifyMode === "local" ? [
18924
19045
  `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
19046
  " \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.`
19047
+ `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
19048
  ] : [
18928
19049
  `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
19050
  " \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.`
19051
+ `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
19052
  ],
18932
19053
  `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
19054
  ...rulesBlock ? ["", rulesBlock] : [],
@@ -20395,12 +20516,28 @@ async function postScrapedVerdictReport(deps, runId, taskSlug, verdict, droneNam
20395
20516
  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
20517
  });
20397
20518
  }
20519
+ function recordReviewFindings(deps, rv, comment) {
20520
+ const findings = comment.findings ?? null;
20521
+ const mode = comment.reviewMode ?? null;
20522
+ if (findings) for (const f of findings) deps.db.addEvent(rv.id, "finding", JSON.stringify(f));
20523
+ if (mode) deps.db.addEvent(rv.id, "review_mode", mode);
20524
+ 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");
20525
+ }
20526
+ async function recordReviewFindingsFromPr(deps, rv) {
20527
+ if (!deps.verdictComment || !rv.pr_url) return;
20528
+ try {
20529
+ const found = await deps.verdictComment(rv.pr_url, rv.created_at);
20530
+ if (found) recordReviewFindings(deps, rv, found);
20531
+ } catch {
20532
+ }
20533
+ }
20398
20534
  async function salvageVerdictFromPrComment(deps, rv, reviewsRunId, since, ctx) {
20399
20535
  if (!deps.verdictComment || !rv.pr_url) return false;
20400
20536
  const found = await deps.verdictComment(rv.pr_url, since).catch(() => null);
20401
20537
  if (!found) return false;
20402
20538
  const verdictText = verdictLabel(found.verdict);
20403
20539
  deps.db.updateRun(reviewsRunId, { review_verdict: found.verdict });
20540
+ recordReviewFindings(deps, rv, found);
20404
20541
  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
20542
  await closeReviewerRun(
20406
20543
  deps,
@@ -20683,6 +20820,7 @@ async function tickOnce(deps) {
20683
20820
  }
20684
20821
  if (run.worktree_path) await deps.removeWorktree(run.repo, run.worktree_path).catch(() => {
20685
20822
  });
20823
+ await recordReviewFindingsFromPr(deps, run);
20686
20824
  deps.db.updateRun(run.id, { status: "done" });
20687
20825
  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
20826
  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.46",
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": "09254fc07f88535996f8b3da61c09ce8aeec0e92",
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",