Навигация
Формат записей знания
Формат записей знания: из чего состоит запись, как она хранится рядом с кодом и как её читает помощник
Записи знания живут файлами в каталоге .gitriver/knowledge/ основного
репозитория — не в отдельной базе и не в вики. Поэтому у записи есть ветка,
рецензирование, CODEOWNERS, защита ветки и откат, и она едет в той же ветке,
что и породившее её изменение кода: одна рецензия, одно одобрение, один откат.
Один файл — одна запись. Два человека, правящие разные записи, правят разные файлы, и конфликтов слияния между ними нет.
Устройство файла
Markdown-тело под YAML-заголовком (frontmatter) между строками ---:
---
id: 0192ec1a-7b1a-7def-abcd-0987654321fe
origin: agent
contract:
- mechanism: on_query
params:
words: ["развёртывание", "откат"]
- mechanism: on_tool
params:
name: run_migration
links:
- 0192ec1b-0000-7000-8000-000000000001
---
Первая законченная мысль записи — она же фрагмент показа в списках.
Дальше — обоснование: почему так, что попробовать, когда не работает.
Поля заголовка
| Поле | Обязательное | Смысл |
|---|---|---|
id |
да | Устойчивый идентификатор записи, UUIDv7. Живёт внутри файла: переживает переименование файла и любую правку текста. Счётчики показов и чтений ведутся по нему |
contract |
нет | Контракт извлечения: массив объявлений {"mechanism": ..., "params": {...}}. Пустой означает «не всплывать никогда» — такая запись считается недостижимой, но хранится |
origin |
нет | Происхождение: human или agent. По умолчанию agent. Поле в файле — заявление клиента, а не удостоверение: гарантией происхождения является одобрение запроса на слияние, а не заголовок |
links |
нет | Связи с другими записями — список их устойчивых идентификаторов |
summary |
нет | Явный фрагмент показа. См. правило переопределения ниже |
Имена файлов человекочитаемы и меняются свободно — идентификатор в пути не
нужен и потому не теряется. Записи, которые собирает сам сервер (см. «Как
знание выходит наружу»), кладутся под именем <id>.md; переименовать такой
файл потом можно свободно.
Тело и фрагмент показа
Фрагмент для показа в списках выводится из тела: первый абзац до пустой строки, с потолком 512 байт по границе символа.
summary переопределяет выведенный фрагмент, но обязано говорить иначе, чем
тело. Переопределение, повторяющее выведенный фрагмент (после нормализации
пробелов), отмечается в диагностике разбора, и используется выведенный
фрагмент. Пустой по смыслу или упёршийся в потолок выведенный фрагмент тоже
виден в диагностике разбора — исправлять его следует в самой записи.
Строго на запись, терпимо на чтение
Правило решается по происхождению данных:
- создание записи через API — строгий разбор: неизвестный ключ отклоняется на месте как опечатка;
- разбор файла из репозитория — терпимый: файл мог записать инструмент более новой версии, чем сервер.
Терпимость различает непонятое и ошибочное:
- непонятое — незнакомый этой версии ключ или механизм. Запись принимается целиком, объявленные механизмы сохраняются как написаны, а запись считается недостижимой, пока сервер не научится этим механизмам; после обновления сервера она оживает сама, без пересборки указателя. Неизвестный ключ называется вместе с ближайшим известным: «ключ „on_trigers“ не известен, ближайший известный — „contract“» — это опечатка правившего руками; «похожих нет» — запись новее версии сервера;
- ошибочное — негодное значение известного поля (
idне UUIDv7), файл крупнее предела, дубликат идентификатора в пачке, пустое тело. Такая запись пропускается, остальные разбираются дальше: один сломанный файл не мешает остальным.
Непонятые механизмы сводятся в отчёте разбора: «3 записи требуют механизм „on_path“, которого эта версия не умеет». По такой строке администратор видит, что серверу пора обновиться.
Отчёт разбора приходит автору в ответ на отправку (push) — строками remote:
рядом с выводом git push (по HTTP и по встроенному SSH-серверу): по строке на
замечание к файлу, «путь: причина», и строкой-сводкой на каждый непонятый
механизм. Отправка при этом проходит.
Пределы
| Предел | Значение |
|---|---|
| Размер одного файла | 64 КБ |
| Суммарный размер пачки разбора | 10 МБ |
Превышение — диагностика и пропуск файла, а не отказ в отправке.
Оба предела действуют и на входе. Черновик проверяется по размеру будущего файла вместе со служебным заголовком, а не только текста: принятый черновик всегда можно доставить. Пачка при сборке режется по пределу разбора: остаток уезжает следующей пачкой, а не отказом.
Разбор при отправке
Указатель знания наполняется двумя источниками: черновики пишутся прямо в него,
файлы .gitriver/knowledge/ разбираются при приёме изменений — только
изменённые в этой отправке файлы, содержимое читается из нового коммита.
- Разбор знания никогда не валит отправку: любая ошибка — запись в журнал.
- Файловые записи попадают в указатель только с ветки по умолчанию. Знание ветки, которая ещё не слита, видно команде черновиком.
- Разбор при этом идёт при любой отправке: об опечатке в записи автор узнаёт сразу, а не после слияния.
- Слияние запроса через интерфейс или очередь слияния тоже разбирается: знание становится доступным сразу по слиянию запроса, не дожидаясь следующей отправки в ветку по умолчанию.
- Удаление файла снимает запись указателя только на ветке по умолчанию. Если указатель на миг разошёлся с деревом, расхождение снимается само при следующем чтении, без действий администратора.
- Чтение знания всегда идёт из указателя, а не из файлов ветки: черновики одной ветки видны и из других ещё до слияния. Выдача фильтруется по правам вызывающего на каждый репозиторий.
Как знание выходит наружу
Агент не коммитит каждую мысль. Он пишет черновик — черновик сразу виден всей команде с доступом к репозиторию и помечен непроверенным, — а наружу знание выходит пачкой: все накопленные в одной ветке черновики становятся файлами одним коммитом и проходят одну рецензию.
Путей два, и первый основной.
- В ту же ветку, что и код. Файлы кладутся в ветку, где агент работал: знание приезжает в запросе на слияние разработчика — одна рецензия, одно одобрение, отдельного шага нет вовсе.
- Отдельной веткой и своим запросом на слияние. Ветка называется
knowledge/<исходная ветка>-<метка>. Запасной путь: нужен, когда исходной ветки уже нет (слита и удалена) или знание не привязано к изменению кода.
Своего порядка одобрения у знания нет ни на одном пути: наружу оно выходит обычным запросом на слияние, и защита ветки, CODEOWNERS, требуемые одобрения и проверки слияния действуют на него так же, как на любой другой. Запись файлов сервером подчиняется защите ветки наравне с отправкой: в ветку, куда отправка запрещена, пачка не ляжет.
Происхождение запроса ставит сервер
Запрос на слияние, открытый сборкой знания, помечен машинным происхождением. Пометку ставит сервер: в теле запроса на создание запроса на слияние такого поля нет, и объявить свой запрос машинным клиент не может. То же правило, что у машинных комментариев к запросам на слияние.
Что происходит с черновиком
Ничего — до одобрения. Черновик помечается доставленным (куда, когда, в каком запросе ждёт) и второй раз в пачку не попадает, но продолжает существовать как черновик: если запрос отклонят, а ветку удалят, знание не потеряется.
Снимает черновик попадание его файла в ветку по умолчанию. Если пачка ушла прямо в ветку по умолчанию (небольшая команда работает без веток), это происходит сразу, и запроса на слияние не появляется вовсе. Запись указателя при этом остаётся той же — идентификатор общий, поэтому счётчики показов и чтений переживают превращение черновика в файл.
Интерфейс
Знание читается и пишется через /api/v1; чтение всегда идёт из указателя, а
не из файлов дерева.
| Вызов | Что делает |
|---|---|
GET /repos/{owner}/{name}/knowledge |
Указатель репозитория: {items, partial?} — записи с контрактом и признаком достижимости |
GET /repos/{owner}/{name}/knowledge/drafts |
Черновики репозитория — все, а не только свои; страница {items, next_cursor} (per_page, after) |
POST /repos/{owner}/{name}/knowledge/drafts |
Записать черновик |
DELETE /repos/{owner}/{name}/knowledge/drafts/{id} |
Снять черновик |
POST /repos/{owner}/{name}/knowledge/drafts/deliver |
Собрать накопленные в ветке черновики в файлы |
POST /repos/{owner}/{name}/knowledge/shown |
Отметить показ пачки записей |
POST /repos/{owner}/{name}/knowledge/entries/{id}/read |
Отметить, что запись открыли |
GET /repos/{owner}/{name}/knowledge/stats/me |
Мои счётчики показов и чтений: {items, partial?} |
GET /repos/{owner}/{name}/knowledge/stats/team |
Сведение по команде, та же форма (Pro). Строка есть у КАЖДОЙ записи указателя, в том числе у непоказанной — с нулями |
GET /repos/{owner}/{name}/knowledge/stats/dead |
Разбор мёртвой памяти (Pro): {items, partial?, min_shown, min_age_days}. У каждой строки ailment — shown_not_read, unreachable (с unmet_mechanisms) или never_shown. Пороги задаются min_shown и min_age_days, применённые возвращаются в ответе |
GET·PUT·DELETE /admin/knowledge/limits/{users|groups}/{id} |
Предел черновиков на владельца: просмотр и снятие — в любой редакции, заведение и изменение (PUT) — редакция Max |
Показ отмечается пачкой — до 256 записей за вызов.
Отмечать показ можно так часто, как он случается на самом деле: предела частоты этого вызова (600 обращений в минуту на токен) хватает с запасом. Чтение счётчиков всегда точное, даже сразу после отметки показа.
Указатель отдаётся целиком, без разбиения на страницы, — отбор по контракту
делает потребитель. Действует защитный потолок в тысячу записей; если выдачу
обрезали, ответ несёт оговорку partial: "truncated". Тем же потолком и той же
оговоркой ограничены сведения счётчиков. Отсутствие поля partial означает
полный ответ, поэтому клиент, не знающий о нём, продолжает работать без правок.
Тела записи в указателе нет — у файловой записи оно читается обычным чтением
файла по file_path, у черновика приходит в списке черновиков.
Права — knowledge:read и knowledge:write; они же выводятся из уровня
доступа к репозиторию (чтение знания = чтение репозитория, запись = запись).
Токен нужен всегда, в том числе на публичном репозитории.
Разделение редакций выражено АДРЕСАМИ, а не полем запроса: знание одного репозитория и личные счётчики работают в Community целиком; платны только сведение по команде и разбор мёртвой памяти (Pro) и заведение пределов черновиков (Max). Платный вызов либо доступен целиком, либо отклоняется целиком — получить платную часть ответа через бесплатный вызов нельзя. Подробнее — licensing.md.
Отказ по лицензии несёт машинный код license_required, отличный от отказа по
правам: разбирать переводимый текст клиенту не нужно.
Пример полного файла
---
id: 0192ec1a-7b1a-7def-abcd-0987654321fe
origin: human
contract:
- mechanism: on_query
params:
words: ["миграция", "sqlx"]
summary: Подготовка базы перед sqlx migrate — иначе миграции падают на правах
---
Миграции sqlx требуют владельца схемы, а не суперпользователя.
Миграция, применённая под суперпользователем, создаёт объекты от его имени, и
следующая миграция под обычной учётной записью падает на правах. Заводите
отдельную роль-владельца схемы и применяйте миграции под ней.