ПРАКТИЧЕСКАЯ ИНСТРУКЦИЯ
Интерактивная сортировка GitHub Issues с помощью Copilot Canvas
Большой список задач трудно разбирать в чате: решение по одной карточке быстро теряется среди объяснений, а таблица не показывает достаточно контекста для уверенного выбора. В этой инструкции мы создадим Copilot Canvas, который загружает GitHub Issues, показывает их как карточки, позволяет принять, отложить или отклонить каждую задачу и сохраняет решения до синхронизации с репозиторием.
Что получится
Готовый интерфейс разделяет работу на два безопасных этапа. Сначала человек принимает решения на canvas и видит их в отдельной очереди. Затем подтверждает применение изменений к GitHub. Ошибочный клик поэтому не закрывает Issue и не меняет метки немедленно.
GitHub Issues
↓ загрузка
карточки на canvas
↓ принять / отложить / отклонить
локальный журнал решений
↓ проверка человеком
подтверждение применения
↓
метки, комментарии или закрытие в GitHub
Минимальная версия canvas должна уметь:
- загрузить открытые Issues из выбранного репозитория;
- показать номер, заголовок, автора, метки, возраст и короткое резюме;
- фильтровать карточки по меткам, автору и строке поиска;
- зафиксировать решение
accept,deferилиreject; - потребовать причину для отклонения;
- показать все ожидающие изменения перед применением;
- применить подтверждённые решения и сохранить результат каждой операции;
- не повторить уже успешно применённое действие после перезапуска.
Почему чат и таблица перестают помогать
Триаж Issues — это не просто сортировка по заголовку. Для каждого обращения нужно понять: воспроизводится ли проблема, хватает ли данных, относится ли она к продукту, не является ли дублем и стоит ли тратить на неё время сейчас.
В чате хорошо обсуждать неоднозначную задачу, но плохо поддерживать десятки параллельных состояний. После нескольких ответов становится трудно увидеть, какие Issues уже разобраны, какие ожидают решения и почему одна карточка была отклонена.
Таблица лучше показывает список, но обычно требует переключаться между строкой и страницей GitHub. Длинное описание, метки, фрагменты обсуждения и связанные задачи не помещаются в одну строку. К тому же таблица редко различает черновое решение и уже применённое изменение.
Canvas решает эту проблему общей рабочей поверхностью. Человек взаимодействует с карточками, а AI-агент может загружать данные, готовить резюме, искать возможные дубли и выполнять только подтверждённые действия.
Конкретный кейс: очередь входящих ошибок
Возьмём воспроизводимый сценарий без привязки к конкретной компании. В репозитории OWNER/REPOSITORY накопились открытые обращения с метками bug, needs-info и enhancement. Часть сообщений описывает реальные ошибки, часть не содержит шагов воспроизведения, а несколько карточек могут относиться к одной причине.
Команда хочет проводить короткую сессию разбора и принимать по каждой карточке одно решение:
| Решение | Что означает | Изменение в GitHub |
|---|---|---|
accept |
Задача достаточно ясна и входит в рабочую очередь | Добавить triage:accepted, убрать triage:pending |
defer |
Решение отложено до даты или получения данных | Добавить triage:deferred, сохранить причину |
reject |
Задача не планируется, является дублем или не относится к проекту | Добавить triage:rejected; закрытие выполняется только отдельным подтверждением |
Такое соответствие нужно определить до создания интерфейса. Кнопка «Отклонить» не должна иметь скрытого смысла. Если в одной команде она означает «поставить метку», а в другой — «закрыть без комментария», автоматизация быстро создаст конфликт.
Сначала отделите решение от действия
Главный принцип этого workflow: нажатие на карточке фиксирует намерение, но не выполняет необратимое действие. Изменения отправляются в GitHub только через отдельный контур подтверждения.
У каждой записи поэтому есть два независимых состояния:
decision.status:
undecided | accept | defer | reject
sync.status:
draft | pending | applying | applied | failed
Разделение помогает ответить на два разных вопроса:
- какое решение принял человек;
- успела ли система применить это решение в GitHub.
Не смешивайте эти поля в один статус. Если запрос к GitHub завершится ошибкой, решение не должно исчезнуть или снова стать «неразобранным».
Шаг 1. Подготовьте репозиторий и доступ
Откройте GitHub Copilot app и добавьте нужный репозиторий в рабочее пространство. У пользователя, под которым работает приложение, должен быть доступ к чтению Issues. Для применения меток, комментариев или закрытия потребуется право изменять Issues.
Перед автоматизацией проверьте репозиторий через GitHub CLI. Команды ниже ничего не изменяют:
gh auth status
gh repo view OWNER/REPOSITORY \
--json nameWithOwner,url,hasIssuesEnabled
gh issue list \
--repo OWNER/REPOSITORY \
--state open \
--limit 20 \
--json number,title,author,labels,createdAt,updatedAt,url
Замените OWNER/REPOSITORY на настоящий путь. Не вставляйте токены в prompt, исходный код canvas или журнал решений. Copilot app и CLI должны использовать уже настроенную авторизацию.
Если hasIssuesEnabled имеет значение false, сначала включите Issues в настройках репозитория. Если gh issue list возвращает отказ в доступе, canvas столкнётся с той же границей прав.
Шаг 2. Зафиксируйте словарь решений
Создайте или согласуйте метки, которые canvas будет использовать. Названия ниже — конфигурация примера, а не обязательный стандарт GitHub:
triage:pending
triage:accepted
triage:deferred
triage:rejected
needs-info
duplicate
Проверить существующие метки можно так:
gh label list \
--repo OWNER/REPOSITORY \
--limit 200
Если нужных меток нет, создайте их вручную в GitHub или следующими командами после проверки названий и цветов:
gh label create "triage:pending" \
--repo OWNER/REPOSITORY \
--color "D4C5F9" \
--description "Issue ожидает решения"
gh label create "triage:accepted" \
--repo OWNER/REPOSITORY \
--color "0E8A16" \
--description "Issue принят в рабочую очередь"
gh label create "triage:deferred" \
--repo OWNER/REPOSITORY \
--color "FBCA04" \
--description "Решение отложено"
gh label create "triage:rejected" \
--repo OWNER/REPOSITORY \
--color "B60205" \
--description "Issue отклонён после триажа"
Эти команды изменяют репозиторий. Выполняйте их только после согласования названий с командой. Если организация уже использует собственную систему меток, настройте canvas под неё вместо создания параллельного словаря.
Шаг 3. Создайте canvas
Откройте новую сессию в GitHub Copilot app и вызовите навык:
/create-canvas
Затем передайте подробный prompt. Чем точнее описаны состояния и границы действий, тем меньше придётся исправлять после генерации.
Создай canvas для триажа GitHub Issues в репозитории
OWNER/REPOSITORY.
Интерфейс:
- показывай открытые Issues как карточки;
- на карточке показывай number, title, author, labels,
createdAt, updatedAt, URL и краткое резюме;
- добавь поиск и фильтры по label и decision;
- добавь кнопки «Принять», «Отложить», «Отклонить»;
- для «Отложить» требуй причину и необязательную дату;
- для «Отклонить» требуй причину;
- разреши отменить локальное решение;
- показывай счётчики undecided, accepted, deferred, rejected;
- добавь отдельную панель «Ожидающие изменения».
Безопасность:
- клики по карточкам изменяют только локальный draft;
- не закрывай Issues автоматически;
- перед изменениями GitHub показывай точный список операций;
- применяй изменения только после явного подтверждения;
- не записывай токены и секреты в состояние;
- после каждой операции сохраняй applied или failed;
- повторное применение applied-операции должно быть безопасным.
Возможности для агента:
- load_issues;
- get_board_state;
- set_decision;
- clear_decision;
- preview_changes;
- apply_approved_changes;
- retry_failed_changes.
Храни решения в JSON-артефакте внутри расширения.
Для каждой записи сохраняй issue number, repository,
decision, reason, deferredUntil, decidedAt, decidedBy,
sourceUpdatedAt, syncStatus, appliedAt и error.
При загрузке не затирай решения, которые ещё не применены.
Если Issue изменился после sourceUpdatedAt, пометь карточку
как stale и не применяй решение без повторного подтверждения.
Canvas откроется в правой панели приложения. Сначала проверьте интерфейс на нескольких карточках, не включая применение изменений.
Шаг 4. Выберите область хранения
Canvas может быть личным или общим для репозитория:
~/.copilot/extensionsподходит для личного прототипа на одной машине;.github/extensionsподходит для расширения, которое команда хранит и проверяет вместе с репозиторием.
Для первой итерации разумно использовать личную область. Переносите расширение в репозиторий после того, как проверили модель решений, названия действий и формат состояния. Общий canvas становится частью проекта: его изменения следует просматривать так же внимательно, как изменения автоматизации.
Сгенерированная структура может различаться, но обычно в каталоге расширения есть метаданные пакета, входной файл и каталог артефактов:
issue-triage-canvas/
├── package.json
├── extension.mjs
└── artifacts/
└── decisions.json
Не привязывайте проверку к точному имени входного файла. Сначала откройте созданный каталог и посмотрите, какие файлы действительно сгенерировал Copilot.
Шаг 5. Проверьте формат состояния
Состояние лучше хранить в явном JSON-артефакте. Это позволяет просмотреть решения без запуска интерфейса и восстановить сессию после закрытия приложения.
Минимальная запись может выглядеть так:
{
"repository": "OWNER/REPOSITORY",
"issueNumber": 123,
"issueUrl": "https://github.com/OWNER/REPOSITORY/issues/123",
"sourceUpdatedAt": "2026-07-27T09:30:00Z",
"decision": "reject",
"reason": "Недостаточно данных для воспроизведения",
"deferredUntil": null,
"decidedAt": "2026-07-27T10:05:00Z",
"decidedBy": "current-user",
"syncStatus": "draft",
"appliedAt": null,
"error": null
}
Значения в примере демонстрационные. Canvas должен подставлять данные текущего Issue и фактическое время решения.
Особенно важны четыре поля:
sourceUpdatedAtпозволяет заметить, что Issue изменился после загрузки;reasonобъясняет решение и не даёт превратить отклонение в безымянный клик;syncStatusотличает черновик от применённого изменения;errorсохраняет причину сбоя, чтобы решение не пришлось принимать заново.
Файл решений не должен содержать токены, приватные ключи, полные копии конфиденциальных обсуждений или данные, которые не нужны для восстановления состояния.
Шаг 6. Разделите возможности canvas
Одна универсальная функция вроде update_issue слишком широка: трудно понять, что именно она меняет и можно ли безопасно повторить вызов. Полезнее иметь небольшие возможности с чёткими входами и результатами.
load_issues
Загружает только нужную выборку. На старте не просите все Issues за всю историю. Ограничьте запрос открытым состоянием, меткой и размером страницы.
{
"repository": "OWNER/REPOSITORY",
"state": "open",
"labels": ["triage:pending"],
"limit": 50
}
set_decision
Изменяет локальный draft и ничего не отправляет в GitHub.
{
"issueNumber": 123,
"decision": "defer",
"reason": "Нужны логи новой версии",
"deferredUntil": "2026-08-03"
}
preview_changes
Преобразует решения в конкретный план операций. Предпросмотр должен показывать не абстрактное «обновить три задачи», а номер Issue, добавляемые и удаляемые метки, текст комментария и факт закрытия.
Issue #123
add labels: triage:deferred
remove labels: triage:pending
comment: "Триаж: отложено. Причина: нужны логи новой версии."
close: false
apply_approved_changes
Принимает только подтверждённый список операций. Перед выполнением функция повторно получает текущий updatedAt Issue и сравнивает его с sourceUpdatedAt. Это простая форма оптимистической блокировки: если карточка изменилась, система останавливается и просит проверить её заново.
retry_failed_changes
Повторяет только операции со статусом failed. Уже применённые записи пропускаются. Такая идемпотентность защищает от двойных комментариев и повторного изменения меток после сетевого сбоя.
Шаг 7. Доведите карточку до рабочего состояния
Попросите Copilot изменить canvas, если карточка показывает только заголовок. Для уверенного решения обычно нужны:
- номер и кликабельная ссылка на Issue;
- заголовок и первые строки описания;
- автор, метки и дата последнего изменения;
- число комментариев, если оно доступно в загруженных данных;
- краткое резюме с явной пометкой, что оно создано моделью;
- предупреждение о возможном дубле без автоматического слияния;
- текущее локальное решение и состояние синхронизации;
- кнопки решения и отмены;
- заметный индикатор
staleдля изменившихся карточек.
Удобный порядок работы — одна активная карточка в центре и следующая карточка сразу после решения. При этом боковая панель должна сохранять обзор всей очереди: пользователь не должен терять возможность вернуться к предыдущему выбору.
Добавьте клавиатурные действия только после визуальных кнопок. Например:
A — принять
D — отложить
R — отклонить
U — отменить локальное решение
Enter — открыть подробности
Горячая клавиша не должна применять изменения к GitHub. Для пакетной синхронизации оставьте отдельную кнопку с подтверждением.
Шаг 8. Ограничьте роль AI-резюме
Короткое резюме ускоряет чтение длинных Issues, но не является источником истины. Модель может пропустить условие воспроизведения, неверно связать два комментария или сделать слишком уверенный вывод. Это частный риск галлюцинации модели.
Попросите canvas разделять исходные данные и вывод агента:
Исходные данные:
- заголовок;
- описание;
- метки;
- автор;
- даты;
- ссылка.
AI-резюме:
- наблюдаемая проблема;
- ожидаемое поведение;
- наличие шагов воспроизведения;
- недостающие данные;
- возможные дубли;
- уровень уверенности.
Если резюме утверждает, что Issue является дублем, карточка должна показывать ссылку на предполагаемый оригинал. Без ссылки это гипотеза, а не основание для закрытия.
Не загружайте все комментарии без необходимости. Для длинного обсуждения сначала показывайте краткий обзор, а полный контекст открывайте по запросу. Это уменьшает объём контекста и облегчает ручную проверку.
Шаг 9. Проверьте изменения перед применением
После разбора нескольких карточек откройте панель ожидающих изменений. Для каждой записи проверьте:
- правильный ли указан репозиторий;
- совпадает ли номер Issue;
- какие метки будут добавлены и удалены;
- будет ли опубликован комментарий;
- не запланировано ли закрытие без отдельного согласия;
- не изменилась ли карточка после принятия решения;
- есть ли причина у
deferиreject.
Эквивалентные операции GitHub CLI полезно знать для ручной проверки. Принятие задачи может выглядеть так:
gh issue edit 123 \
--repo OWNER/REPOSITORY \
--add-label "triage:accepted" \
--remove-label "triage:pending"
Отклонение без закрытия:
gh issue edit 123 \
--repo OWNER/REPOSITORY \
--add-label "triage:rejected" \
--remove-label "triage:pending"
gh issue comment 123 \
--repo OWNER/REPOSITORY \
--body "Триаж: отклонено. Причина: недостаточно данных для воспроизведения."
Закрытие должно быть отдельной операцией:
gh issue close 123 \
--repo OWNER/REPOSITORY \
--reason "not planned"
Не выполняйте пример буквально для Issue #123. Canvas должен сформировать команды или вызовы для реально выбранных карточек после подтверждения.
Шаг 10. Сохраните историю решений
Одного текущего состояния недостаточно. Если решение изменилось с reject на accept, полезно знать, кто и когда это сделал. Добавьте append-only журнал аудита, в котором старые события не перезаписываются.
{
"eventId": "decision-123-20260727T100500Z",
"repository": "OWNER/REPOSITORY",
"issueNumber": 123,
"action": "decision_changed",
"from": "undecided",
"to": "reject",
"reason": "Недостаточно данных для воспроизведения",
"actor": "current-user",
"createdAt": "2026-07-27T10:05:00Z"
}
Отдельно фиксируйте результат синхронизации:
{
"eventId": "sync-123-20260727T101200Z",
"issueNumber": 123,
"action": "github_update",
"status": "applied",
"operations": [
"add:triage:rejected",
"remove:triage:pending"
],
"createdAt": "2026-07-27T10:12:00Z"
}
Такой журнал помогает восстановить ход сессии и проверить, что локальное решение действительно дошло до GitHub. Он не заменяет историю самого Issue, а дополняет её сведениями о черновых и неудачных операциях.
Проверка результата
Не начинайте с полного рабочего списка. Создайте отдельный тестовый Issue или выберите безопасную карточку, на которой команда разрешила проверку меток. Затем выполните последовательность ниже.
1. Проверка загрузки
- Canvas показывает тот же номер, заголовок, URL и набор меток, что GitHub.
- Фильтр по метке не скрывает карточки с локальными решениями без предупреждения.
- Повторная загрузка не создаёт дубль одной карточки.
2. Проверка чернового решения
- Нажмите «Принять».
- Убедитесь, что карточка перешла в локальную группу accepted.
- Обновите страницу Issue в GitHub.
- Проверьте, что метки ещё не изменились.
Если GitHub изменился сразу после клика, разделение между решением и применением не работает.
3. Проверка отмены
- Отмените решение.
- Убедитесь, что запись исчезла из очереди применения.
- Закройте и снова откройте canvas.
- Проверьте, что отменённое действие не восстановилось как ожидающее.
4. Проверка подтверждения
- Снова примите тестовый Issue.
- Откройте предпросмотр.
- Сравните номер и метки с карточкой.
- Подтвердите применение.
- Откройте Issue в GitHub и проверьте итоговые метки.
- Убедитесь, что
syncStatusсталapplied.
5. Проверка безопасного повтора
Повторно запустите применение той же записи. Canvas должен сообщить, что операция уже выполнена, и не создавать второй комментарий.
6. Проверка изменившейся карточки
- Примите решение на canvas, но не применяйте его.
- Измените описание или добавьте комментарий в GitHub.
- Запустите обновление данных.
- Убедитесь, что карточка получила статус
stale. - Canvas не должен применять старое решение без повторной проверки.
7. Проверка ошибки доступа
Если возможно выполнить проверку без риска для рабочего репозитория, используйте тестовую среду или пользователя только с правом чтения. Применение должно завершиться состоянием failed с понятным сообщением, а само решение должно остаться сохранённым.
Canvas считается готовым не тогда, когда карточки красиво двигаются, а когда каждое подтверждённое решение можно проследить от клика до фактического состояния GitHub.
Что обычно ломается
Карточки дублируются после обновления
Причина — использование позиции в списке или заголовка вместо стабильного ключа. Идентификатором карточки должна быть пара repository + issueNumber. Заголовок может измениться, а одинаковые заголовки разрешены.
Локальные решения исчезают после новой загрузки
Загрузчик заменяет весь state ответом GitHub. Нужно объединять удалённые данные с локальными решениями по стабильному ключу. Неприменённый draft нельзя стирать только потому, что он отсутствует в ответе сервера.
Отклонение сразу закрывает Issue
Возможность canvas объединила классификацию и внешнее действие. Разделите set_decision и apply_approved_changes. Закрытие сделайте отдельным флагом, выключенным по умолчанию.
Появляются повторные комментарии
Приложение повторяет весь пакет после частичного сбоя. Сохраняйте результат каждой операции отдельно и используйте устойчивый ключ, например:
operationKey =
repository + ":" +
issueNumber + ":" +
decisionVersion + ":" +
operationType
Повтор должен пропускать операцию, уже отмеченную как applied.
Решение применено к устаревшей карточке
Canvas не сравнивает дату изменения. Сохраняйте sourceUpdatedAt, а перед записью снова получайте актуальное значение. При несовпадении возвращайте карточку на проверку.
Фильтры скрывают незавершённую работу
Пользователь выбрал метку, и карточка с черновым решением исчезла из поля зрения. Показывайте постоянный счётчик ожидающих изменений и отдельную панель drafts независимо от текущего фильтра.
После большой загрузки появляются ошибки
Причиной может быть слишком широкий запрос или ограничение частоты запросов. Загружайте данные страницами, кэшируйте уже полученные карточки и не обновляйте каждую Issue отдельным запросом без необходимости.
AI-резюме выдаётся за факт
Отделите резюме визуально, показывайте ссылку на оригинал и не разрешайте модели закрывать Issue только на основании собственной классификации. Решение остаётся за человеком.
В журнал попадают лишние данные
Canvas сохраняет полное описание и комментарии, хотя для восстановления решения достаточно номера, дат, статуса и причины. Сократите артефакт и исключите секреты. Репозиторий с приватным кодом не становится публичным из-за canvas, но экспортированный журнал может создать отдельную утечку.
Ограничения
- Canvas ускоряет повторяющиеся решения, но не определяет продуктовые приоритеты вместо владельца проекта.
- AI-резюме может потерять важную деталь длинного обсуждения. Перед отклонением открывайте оригинальную Issue.
- Поиск дублей остаётся вероятностным. Совпадение темы или текста не доказывает, что причины ошибок одинаковы.
- Права пользователя ограничивают возможности canvas. Интерфейс не должен пытаться обходить правила репозитория.
- Структура расширений и доступные возможности GitHub Copilot app могут меняться. Проверяйте сгенерированные файлы и интерфейс текущей версии приложения.
- Локальный JSON удобен для одной сессии, но не решает одновременное редактирование несколькими людьми.
- Для командной работы потребуется механизм блокировки или слияния решений, иначе два участника могут независимо обработать одну карточку.
- Массовое закрытие Issues остаётся рискованной операцией даже при хорошем интерфейсе. Начинайте с меток и ручного подтверждения небольших пакетов.
- Canvas не заменяет резервное копирование и откат. Для ошибочного массового изменения должен существовать сохранённый список предыдущих меток и состояний.
Как переходить от прототипа к командной работе
После проверки на нескольких Issues перенесите canvas в область репозитория и добавьте его файлы в обычный процесс review. Перед общим использованием зафиксируйте:
- разрешённые репозитории;
- словарь решений и меток;
- обязательные причины для отклонения и отсрочки;
- максимальный размер одного пакета;
- разрешено ли автоматическое комментирование;
- кто может подтверждать закрытие;
- срок хранения журнала решений;
- процедуру восстановления после частичного сбоя.
Для первого рабочего запуска установите небольшой размер пакета — например, не более десяти подтверждённых карточек за одну операцию. Это ограничение является правилом безопасности, а не результатом сравнительного теста. После каждой партии сверяйте журнал с GitHub и только затем увеличивайте объём.
Если несколько участников проводят триаж одновременно, добавьте владельца решения и проверку версии. Canvas должен предупредить, если для одной Issue уже существует более новое решение другого участника.
Финальный чек-лист
- Репозиторий указан явно, а не определяется из случайного текущего каталога.
- Карточка идентифицируется по репозиторию и номеру Issue.
- Принятие, отсрочка и отклонение имеют однозначный смысл.
- Причина обязательна для
deferиreject. - Клик изменяет только локальный draft.
- Перед синхронизацией показан полный список операций.
- Закрытие не включено по умолчанию.
- Устаревшая карточка блокирует применение решения.
- Каждая операция получает отдельный статус.
- Успешное действие не повторяется после перезапуска.
- Ошибка сохраняется вместе с решением.
- Журнал не содержит токенов и лишнего содержимого Issues.
- Результат проверен и на canvas, и на странице GitHub.
Итог
Полезный Copilot Canvas для Issues — это не просто канбан с тремя колонками. Это управляемый процесс, в котором исходная карточка, решение человека и фактическое изменение GitHub остаются различимыми.
Начните с чтения и локальных решений. Затем добавьте предпросмотр, подтверждение, защиту от устаревших данных и журнал операций. Только после ручной проверки небольшого пакета разрешайте canvas менять рабочий репозиторий.
В результате длинная очередь превращается в понятную поверхность: карточка содержит нужный контекст, решение принимается одним действием, причина не теряется, а каждое изменение можно проверить и безопасно повторить.