mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
process: отвязать роли от конкретных агентов (#562)
Issue: #562 User-Visible: no
This commit is contained in:
committed by
claude[bot]
parent
f6e5651ccf
commit
3934f8d8e5
@@ -38,8 +38,11 @@ task records: problem, scope, acceptance criteria and discussion.
|
||||
|
||||
**Status lives in labels:** `S1-new`, `S2-analysis`, `S3-spec`, `S4-spec-review`,
|
||||
`S5-ready`, `S6-in-progress`, `S7-code-review`, `S8-merged`, plus `blocked` on top
|
||||
of a status and `rejected` on a closed issue. Exactly one `S*` label per open
|
||||
issue. Labels are the whole of it: GitHub Projects is no longer used.
|
||||
of a status and `rejected` on a closed issue. A product issue in flight carries
|
||||
exactly one `S*` label. An infrastructure-only issue is the deliberate exception:
|
||||
it may carry no `S*` label while being implemented, enters the common flow at
|
||||
`S7-code-review`, and from then on carries exactly one. Labels are the whole of
|
||||
it: GitHub Projects is no longer used.
|
||||
|
||||
**The light track is the default, not a shortcut** (owner's decision 2026-08-27,
|
||||
issue #338). `small` — the spec lives in the issue body and its review is a
|
||||
@@ -57,10 +60,11 @@ review is never skipped on either track; it is what stands in for testing.
|
||||
`PROCESS.md` §5 and §5.1 hold the criteria.
|
||||
|
||||
An issue filed by an outsider is worked exactly like one of the owner's own, once
|
||||
the owner has decided to take it. The check sits **at the entrance**, not on every
|
||||
step: while an issue carries no status label it is outside the process and the
|
||||
invariants do not apply to it; once a label is on, the task is in flight and **who
|
||||
filed it stops mattering**.
|
||||
the owner has decided to take it. For product work the check sits **at the
|
||||
entrance**, not on every step: the first status label admits it to the flow. For
|
||||
infrastructure work an explicit assignment by the owner is the entrance, and the
|
||||
issue may remain without an `S*` label until its first `S7-code-review`. In either
|
||||
case, **who filed it stops mattering** once the owner has admitted it.
|
||||
|
||||
Applying that first label *is* the owner's explicit decision, and the platform
|
||||
already guarantees it — only someone with write access can label. The earlier rule
|
||||
@@ -185,32 +189,36 @@ way. The layout is therefore fixed:
|
||||
or the owner — never reset or clean them away.
|
||||
- **`houseplan-card-src/hp-dev`** — the owner's worktree, permanently on `dev`. For owner-side operations that must not disturb the
|
||||
author's tree: pushing `dev`, restoring a hook's executable bit, emergencies.
|
||||
- **The reviewer and the infrastructure agent own no local tree.** The reviewer
|
||||
runs in CI on a fresh checkout. The infrastructure agent reads via `git show`
|
||||
and publishes through the GitHub API; it makes no local commits at all, so it
|
||||
needs no `HEAD` of its own. Its scratch worktrees live outside the repo and are
|
||||
pruned after use.
|
||||
- **The reviewer owns no author tree.** It runs in CI on a fresh checkout. The
|
||||
agent implementing an infrastructure task is an ordinary task author and uses
|
||||
the same author-tree rules as product work; task branches must not share a
|
||||
mutable checkout concurrently.
|
||||
|
||||
A worktree is only usable on the machine that created it: the `.git` file records
|
||||
an absolute path in that machine's format. One created from a Linux sandbox is
|
||||
dead on Windows and vice versa — create worktrees on the machine that will use
|
||||
them, which for `hp-dev` means the owner's.
|
||||
|
||||
## Two-agent workflow
|
||||
## Agent-neutral workflow
|
||||
|
||||
**Codex** writes analysis, specs and all product code. **Claude** reviews specs and
|
||||
code and owns infrastructure and distribution. The owner rules on disputes, closes
|
||||
issues and commands releases.
|
||||
No task type is reserved for Codex, Claude or any other named model. **Any agent
|
||||
may take any task**: analysis, spec, product implementation, infrastructure or a
|
||||
release explicitly commanded by the owner. Roles describe the current artifact,
|
||||
not the agent brand. The owner rules on product disputes, closes issues and
|
||||
commands releases.
|
||||
|
||||
Author and reviewer are different models, which is what "a fresh session without
|
||||
implementation context" means in practice. The reviewer never edits product code;
|
||||
the author never grades their own work.
|
||||
Author and reviewer are independent agents/sessions. They need not use different
|
||||
model families, but the reviewer must start without implementation context and
|
||||
must not be the author grading their own work. The reviewer does not edit the
|
||||
material under review.
|
||||
|
||||
**Infrastructure-only work runs outside this flow.** CI, scripts, labels, demo
|
||||
stands, the landing page and distribution are Claude's alone, and running them
|
||||
through spec-writing and review buys nothing: the spec would restate what is
|
||||
already unambiguous, and author and reviewer would be the same role. So no spec
|
||||
file, no spec review, no code review, no walk through `S1`…`S8`.
|
||||
**Infrastructure-only work uses an accelerated entry into the common flow.** It
|
||||
is implemented immediately by any agent, without analysis, spec, spec review or
|
||||
the statuses `S1`…`S6`. Once the branch is ready and pushed, apply
|
||||
`S7-code-review`. From there the ordinary controller applies: green review rebases
|
||||
and merges the checked material into `dev` and then sets `S8-merged`; findings or
|
||||
a failed merge return the issue to `S6-in-progress`, and after correction it is
|
||||
submitted to `S7-code-review` again.
|
||||
|
||||
The test for "infrastructure only" is mechanical: **not a single class A file** —
|
||||
nothing under `src/**`, no `custom_components/**/*.py`, no manifests, no i18n. A
|
||||
@@ -220,8 +228,10 @@ a loose reading would turn this into the route by which product changes skip
|
||||
review.
|
||||
|
||||
What stays mandatory either way: an issue exists, both trailers are on every
|
||||
commit, `typecheck`, `test` and `build` are green, and any non-obvious decision is
|
||||
written down in the code or the issue rather than kept in someone's head.
|
||||
commit, proportionate local gates are green (normally `typecheck`, `test` and
|
||||
`build`), and any non-obvious decision is written down in the code or the issue
|
||||
rather than kept in someone's head. Infrastructure work skips specification, not
|
||||
code review.
|
||||
|
||||
**Review starts by itself.** Applying `S4-spec-review` or `S7-code-review` fires the
|
||||
pipeline, which reviews without anyone asking and takes ten to forty-five minutes.
|
||||
|
||||
+53
-33
@@ -1,9 +1,11 @@
|
||||
# Процесс работы над House Plan
|
||||
|
||||
> **Статус документа: канон** (редакция 2026-08-13). Решения владельца, на
|
||||
> **Статус документа: канон** (редакция 2026-08-13, ролевое уточнение
|
||||
> 2026-09-13). Решения владельца, на
|
||||
> которых он стоит: прямые коммиты в `dev` **без PR** · канон статуса — **метки**,
|
||||
> имена английские · лёгкий трек **включён** · автор и ревьюер — разные модели ·
|
||||
> инфраструктурные задачи идут **вне** флоу.
|
||||
> имена английские · лёгкий трек **включён** · автор и ревьюер — независимые
|
||||
> агенты/сессии · любой агент может взять любую роль · инфраструктурные задачи
|
||||
> входят в общий флоу сразу на `S7-code-review`.
|
||||
>
|
||||
> **Область действия:** обязателен для владельца и для любого агента. Читается
|
||||
> сразу после `docs/SCOPE.md` и `AGENTS.md`, до `docs/STATUS.md`. Живёт в
|
||||
@@ -44,13 +46,20 @@
|
||||
лежит внутри `custom_components/houseplan/frontend/`, и без этого правила он
|
||||
считался бы продуктовым исходником.
|
||||
|
||||
**Инфраструктурная задача идёт вне флоу** (решение владельца 2026-08-13,
|
||||
issue #118). Признак механический: **ни одного файла класса A**. Такая задача
|
||||
делается без ТЗ, ревью ТЗ, код-ревью и без прохода по статусам — флоу построен
|
||||
для изменений, у которых есть персона и видимое поведение, а в инфраструктуре ТЗ
|
||||
пересказывало бы очевидное, и автор с ревьюером оказались бы одной ролью.
|
||||
Проверкой служат гейты и CI. Обязательным остаётся issue, трейлеры и зелёные
|
||||
`typecheck`, `test`, `build`.
|
||||
**Инфраструктурная задача использует ускоренный вход в общий флоу** (решение
|
||||
владельца 2026-09-13, issue #562). Признак механический: **ни одного файла класса
|
||||
A**. Любой агент может сразу реализовать её по issue и в ветке
|
||||
`issue/<NN>-<slug>`, без аналитики, ТЗ, ревью ТЗ и статусов `S1`…`S6`. Когда
|
||||
материал готов, локальные гейты зелёные и ветка запушена, исполнитель ставит
|
||||
`S7-code-review`. Дальше действует тот же контроллер, что для продуктового кода:
|
||||
зелёное ревью сливает проверенный материал в `dev` и ставит `S8-merged`, а
|
||||
замечания или неудавшееся слияние возвращают задачу в `S6-in-progress`; после
|
||||
исправлений она снова идёт в `S7-code-review`.
|
||||
|
||||
Отсутствие ТЗ не означает отсутствие проверки. Для инфраструктуры обязательны
|
||||
issue, терминальные трейлеры, соразмерные изменению зелёные гейты, хендофф с
|
||||
доказательствами и независимое код-ревью. Модель или имя агента процессом не
|
||||
предписываются.
|
||||
|
||||
Задача, задевающая класс A хотя бы одним файлом, инфраструктурной **не
|
||||
является** и идёт полным флоу. «В основном инфраструктурная» не бывает: иначе
|
||||
@@ -61,7 +70,8 @@ issue #118). Признак механический: **ни одного фай
|
||||
|
||||
## 2. Жизненный цикл
|
||||
|
||||
Восемь рабочих статусов и два служебных. Фазы тестирования в цикле сознательно
|
||||
Восемь рабочих статусов и два служебных. Полный маршрут ниже относится к
|
||||
продуктовым задачам. Фазы тестирования в цикле сознательно
|
||||
**нет**: найденные позже дефекты заводятся отдельными issue и проходят цикл
|
||||
заново. Issue закрывается после выпуска беты.
|
||||
|
||||
@@ -72,6 +82,7 @@ S1-new → S2-analysis → S3-spec → S4-spec-review ⟲ → S5-ready →
|
||||
служебные: blocked (поверх статуса) rejected (закрыт)
|
||||
⟲ — возврат на правки, не более 4 циклов (§4), на лёгком и коротком треке 2
|
||||
короткий трек (`trivial`, §5.1) идёт S2-analysis → S5-ready, минуя S3 и S4
|
||||
инфраструктурный трек (§1): без S → S7-code-review ⟲ S6-in-progress → S8-merged
|
||||
```
|
||||
|
||||
Переходы `S4-spec-review` и `S7-code-review` выполняются **автоматически**: метка
|
||||
@@ -501,19 +512,18 @@ S1-new → S2-analysis → S3-spec → S4-spec-review ⟲ → S5-ready →
|
||||
**Правило разделения:** ревьюер работает состязательно. Ему передаётся тег или
|
||||
диапазон коммитов и ТЗ — не рассказ автора о том, как всё хорошо.
|
||||
|
||||
**Роли закреплены за исполнителями** (решение владельца 2026-08-12):
|
||||
**Роли не закреплены за моделями или именами агентов** (решение владельца
|
||||
2026-09-13, issue #562). Codex, Claude или любой другой доступный агент может быть
|
||||
аналитиком, автором ТЗ, разработчиком, автором инфраструктурной задачи или
|
||||
релиз-инженером по прямой команде владельца.
|
||||
|
||||
| Исполнитель | Роли |
|
||||
|---|---|
|
||||
| **Codex** | аналитик, автор ТЗ, разработчик, релиз-инженер по команде владельца |
|
||||
| **Claude** | ревьюер ТЗ, ревьюер кода, вся инфраструктура и дистрибуция |
|
||||
| **Владелец** | приоритет, скоуп, арбитраж, закрытие issue, команда на выпуск |
|
||||
Разделение относится к артефакту: **автор и ревьюер — разные агенты/сессии**.
|
||||
Модель может совпадать, но ревьюер начинает без контекста реализации и не ставит
|
||||
вердикт собственной работе. Ревью ТЗ и код-ревью также идут в независимых
|
||||
сессиях: ревьюер кода не должен приходить с контекстом обсуждения ТЗ.
|
||||
|
||||
Автор и ревьюер — **разные модели**, и это сильнее требования «другая сессия»:
|
||||
одна модель, читая свой же артефакт заново, повторяет свои же слепые пятна.
|
||||
|
||||
Ревью ТЗ и код-ревью держатся в **разных сессиях** Claude: ревьюер кода не должен
|
||||
приходить с контекстом того, как обсуждали ТЗ.
|
||||
Владелец сохраняет исключительные решения о приоритете, ценности, продуктовом
|
||||
скоупе, отклонении, арбитраже, закрытии issue и команде на выпуск.
|
||||
|
||||
---
|
||||
|
||||
@@ -741,18 +751,22 @@ Performance зелёные на точном SHA, плюс зелёный E2E н
|
||||
Тематические метки (`polish`, `infra`, `tests`, `docs`, `security`, `vacuum`)
|
||||
ортогональны процессу.
|
||||
|
||||
Инварианты: **ровно одна `S*`-метка** на открытом issue; закрытый issue статусных
|
||||
меток не несёт; `blocked` не заменяет статус, а дополняет его.
|
||||
Инварианты: продуктовая задача в процессе несёт **ровно одну `S*`-метку**.
|
||||
Инфраструктурная задача может не иметь `S*` во время первоначальной реализации;
|
||||
с первого `S7-code-review` на неё действует тот же инвариант ровно одной метки.
|
||||
Закрытый issue статусных меток не несёт; `blocked` не заменяет статус, а дополняет
|
||||
его.
|
||||
|
||||
**Чужой issue берётся в работу так же, как свой — после явного решения
|
||||
владельца** (решение владельца 2026-08-13, уточнено в тот же день). Репозиторий
|
||||
публичный, отчёты заводят и посторонние; проверка стоит **на входе**, а не на
|
||||
каждом шаге.
|
||||
|
||||
Входом служит присвоение первой статусной метки: пока меток нет, issue вне
|
||||
процесса и инварианты на него не распространяются. Как только метка стоит, задача
|
||||
в работе, и **кто её завёл, дальше не имеет значения** — статусы, ревью и лимиты
|
||||
работают одинаково.
|
||||
Для продуктовой задачи входом служит присвоение первой статусной метки. Для
|
||||
инфраструктурной — явное назначение владельцем; до готовности к первому
|
||||
код-ревью она может оставаться без `S*`. Как только продуктовая задача вошла в
|
||||
полный маршрут либо инфраструктурная получила `S7-code-review`, **кто её завёл,
|
||||
дальше не имеет значения** — статусы, ревью и лимиты работают одинаково.
|
||||
|
||||
Присвоение метки и есть то самое явное решение, причём проверенное платформой:
|
||||
метки может ставить только тот, у кого есть право записи в репозиторий. Прежняя
|
||||
@@ -894,11 +908,13 @@ S4-spec-review → ревью ТЗ → S5-ready либо возврат в S3-
|
||||
S7-code-review → код-ревью → слияние в dev → S8-merged либо возврат в S6-in-progress
|
||||
```
|
||||
|
||||
Ревьюер — `anthropics/claude-code-action`. Он читает `docs/SCOPE.md`, `AGENTS.md`,
|
||||
этот документ и тело issue, публикует разбор комментарием, заводит issue на Medium-находки
|
||||
вне скоупа задачи (#202), кладёт документ в `docs/reviews/` ветки задачи и возвращает вердикт
|
||||
структурированным JSON. **Метку переставляет отдельный детерминированный шаг по
|
||||
вердикту, а не модель.**
|
||||
Текущая техническая реализация независимого ревьюера —
|
||||
`anthropics/claude-code-action`; это деталь автоматизации, а не закрепление роли
|
||||
или вида задач за Claude. Ревьюер читает `docs/SCOPE.md`, `AGENTS.md`, этот
|
||||
документ и тело issue, публикует разбор комментарием, заводит issue на
|
||||
Medium-находки вне скоупа задачи (#202), кладёт документ в `docs/reviews/` ветки
|
||||
задачи и возвращает вердикт структурированным JSON. **Метку переставляет отдельный
|
||||
детерминированный шаг по вердикту, а не модель.**
|
||||
|
||||
Четыре вещи, без которых конвейер молча не работает:
|
||||
|
||||
@@ -1163,6 +1179,10 @@ Golden, браузерные смоки, performance и полный HA-харн
|
||||
→ закрытие пачкой при выпуске беты. Оба ревью возвращают на правки не более 4
|
||||
циклов; пятый заход — разбор у владельца (разделить / отклонить / арбитраж).
|
||||
|
||||
Инфраструктурная задача (ни одного файла класса A) делается сразу любым агентом:
|
||||
без `S*` → `S7-code-review` ↔ `S6-in-progress` → `S8-merged`. ТЗ и ревью ТЗ нет,
|
||||
код-ревью обязательно.
|
||||
|
||||
Ревью запускается **само** от меток `S4-spec-review` и `S7-code-review` и идёт до
|
||||
45 минут. Поставив такую метку, автор не заканчивает работу, а ждёт смены метки
|
||||
опросом и продолжает по тому, чем она стала.
|
||||
|
||||
@@ -422,11 +422,11 @@ export function commitsUnderRuleOne(commits) {
|
||||
);
|
||||
}
|
||||
|
||||
// Инфраструктурная работа по решению владельца #118: признак механический —
|
||||
// в диапазоне НЕТ ни одного файла класса A. Такая задача идёт вне продуктового
|
||||
// флоу и по построению не имеет статусной метки, поэтому проверка 8 требовала у
|
||||
// неё невозможного и краснела на каждом инфра-коммите (#207): Validate на dev
|
||||
// был красным систематически, и сигнал догоняющей проверки обесценился.
|
||||
// Инфраструктурная работа по решению владельца #562: признак механический —
|
||||
// в диапазоне НЕТ ни одного файла класса A. Такая задача реализуется до первого
|
||||
// S-статуса и входит в общий флоу готовой веткой через S7-code-review. Поэтому
|
||||
// первоначальный push без статуса допустим; требовать S-метку на нём означало бы
|
||||
// сделать предписанный маршрут технически невозможным (#207).
|
||||
//
|
||||
// Исключение опирается на diff, а не на метку-разрешение: метку `infra` можно
|
||||
// поставить продуктовой задаче и увести продуктовый коммит от проверки статуса,
|
||||
@@ -809,7 +809,7 @@ function main(argv) {
|
||||
// нарушение и никто не узнает, что проверка не выполнялась.
|
||||
findings.push({
|
||||
level: 'warn', rule: 8, sha: '-',
|
||||
msg: `инфраструктурный диапазон (#118): файлов класса A нет, статусная метка issue не требуется`,
|
||||
msg: `инфраструктурный диапазон (#562): файлов класса A нет, статусная метка не требуется до S7-code-review`,
|
||||
});
|
||||
}
|
||||
findings.push(...checkIssueStatuses(numbers, cached, { allowed, statusOptional }));
|
||||
|
||||
+14
-4
@@ -26,11 +26,16 @@ export const STATUS_LABELS = ['S1-new', 'S2-analysis', 'S3-spec', 'S4-spec-revie
|
||||
export function rightsFor(status, labels = []) {
|
||||
const blocked = labels.includes('blocked');
|
||||
const exhausted = labels.includes('review-4');
|
||||
const code = ['S5-ready', 'S6-in-progress', 'S7-code-review'].includes(status);
|
||||
const infrastructure = labels.includes('infra');
|
||||
const code = !infrastructure && ['S5-ready', 'S6-in-progress', 'S7-code-review'].includes(status);
|
||||
const lines = [];
|
||||
if (exhausted) lines.push('review-4: лимит циклов исчерпан — решение владельца (разделить, отклонить, арбитраж); дальше не двигать');
|
||||
if (blocked) lines.push('blocked: работа стоит, ждём внешнего решения — коммиты по задаче гейт не пропустит');
|
||||
lines.push(code ? 'продуктовый код трогать МОЖНО (правило №1)' : 'продуктовый код трогать НЕЛЬЗЯ: статус не S5/S6/S7 (правило №1)');
|
||||
lines.push(code
|
||||
? 'продуктовый код трогать МОЖНО (правило №1)'
|
||||
: infrastructure
|
||||
? 'файлы класса A трогать НЕЛЬЗЯ; инфраструктурную реализацию МОЖНО вести сразу по issue (#562)'
|
||||
: 'продуктовый код трогать НЕЛЬЗЯ: статус не S5/S6/S7 (правило №1)');
|
||||
switch (status) {
|
||||
case 'S1-new': lines.push('следующий шаг: аналитика (S2) — оценки метками, критерий лёгкого трека, затем ТЗ'); break;
|
||||
case 'S2-analysis': lines.push('следующий шаг: ТЗ (S3); трек по умолчанию small — отказ от него обосновать названным критерием §5'); break;
|
||||
@@ -40,7 +45,10 @@ export function rightsFor(status, labels = []) {
|
||||
case 'S6-in-progress': lines.push('следующий шаг: gate:small + смоки по AC → push ветки → метка S7-code-review (метку после push)'); break;
|
||||
case 'S7-code-review': lines.push('идёт код-ревью: ждать вердикт, ничего не пушить в ветку — вердикт привязан к SHA (#312)'); break;
|
||||
case 'S8-merged': lines.push('код в dev, ждёт беты; ничего не делать; issue закроет владелец при выпуске'); break;
|
||||
default: lines.push('статусной метки нет — задача вне процесса; вход в процесс — присвоение первой S*-метки владельцем');
|
||||
default:
|
||||
lines.push(infrastructure
|
||||
? 'инфраструктурный вход: реализовать и проверить → push ветки → S7-code-review; ТЗ и S1–S6 не нужны (#562)'
|
||||
: 'статусной метки нет — продуктовая задача вне процесса; вход — первая S*-метка владельца');
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
@@ -104,7 +112,9 @@ export function buildPacket(inputs) {
|
||||
issue, labels = [], comments = [], owner = 'Matysh', branch = null, specs = [], reviewDocs = [], validate = null,
|
||||
} = inputs;
|
||||
const status = STATUS_LABELS.find((l) => labels.includes(l)) || null;
|
||||
const track = labels.includes('trivial') ? 'trivial' : labels.includes('small') ? 'small' : 'полный';
|
||||
const track = labels.includes('infra')
|
||||
? 'инфраструктурный'
|
||||
: labels.includes('trivial') ? 'trivial' : labels.includes('small') ? 'small' : 'полный';
|
||||
const stage = status === 'S4-spec-review' || status === 'S3-spec' || status === 'S5-ready' ? 'spec' : 'code';
|
||||
const verdict = lastVerdict(comments, reviewDocs, stage);
|
||||
// ТЗ живёт в теле issue (#517); архивный файл — источник только у задач до
|
||||
|
||||
@@ -701,7 +701,7 @@ test('the CLI falls back to origin/dev when BEFORE_SHA is orphaned by a force-pu
|
||||
}
|
||||
});
|
||||
|
||||
test('an infrastructure range is recognised by the absence of class A files (#207)', () => {
|
||||
test('an infrastructure range is recognised by the absence of class A files (#562)', () => {
|
||||
const infra = makeCommit({
|
||||
sha: 'a'.repeat(40), subject: 'Tune CI', body: 'Issue: #206\nUser-Visible: no',
|
||||
files: ['.github/workflows/validate.yml'],
|
||||
@@ -734,8 +734,8 @@ test('a support-relay-only change stays on the reviewed class-B track (#43)', ()
|
||||
assert.equal(isInfrastructureRange([relay]), true);
|
||||
});
|
||||
|
||||
test('statusOptional waives the status label but keeps every other rule-8 refusal (#207)', () => {
|
||||
// Метки инфраструктурного issue по #118: тип, приоритет, тема — без S*.
|
||||
test('statusOptional permits the pre-S7 infra push but keeps every other rule-8 refusal (#562)', () => {
|
||||
// Первый push инфраструктурного issue по #562: тип, приоритет, тема — без S*.
|
||||
const infraIssue = () => ({
|
||||
ok: true, json: JSON.stringify({ state: 'OPEN', labels: [
|
||||
{ name: 'bug' }, { name: 'P2' }, { name: 'infra' },
|
||||
@@ -764,10 +764,10 @@ test('statusOptional waives the status label but keeps every other rule-8 refusa
|
||||
assert.deepEqual(rules(checkIssueStatuses(['206'], garbage, { statusOptional: true })), [8]);
|
||||
});
|
||||
|
||||
// AC #207: сквозной прогон CLI по настоящему репозиторию с подставным gh.
|
||||
// AC #562: сквозной прогон CLI по настоящему репозиторию с подставным gh.
|
||||
// Диапазон без класса A и с issue без статусной метки обязан быть зелёным;
|
||||
// тот же диапазон плюс один файл `src/**` — красным по проверке 8.
|
||||
test('the CLI waives the issue status for a class-B-only range but not with class A (#207)', (t) => {
|
||||
test('the CLI permits a pre-S7 class-B-only range but not one with class A (#562)', (t) => {
|
||||
if (process.platform === 'win32') {
|
||||
t.skip('нужен исполняемый stub gh — прогон в Linux CI');
|
||||
return;
|
||||
@@ -794,7 +794,7 @@ test('the CLI waives the issue status for a class-B-only range but not with clas
|
||||
git('-c', 'user.name=t', '-c', 'user.email=t@t', '-c', 'core.hooksPath=/dev/null',
|
||||
'commit', '-q', '-m', message);
|
||||
};
|
||||
// Подставной gh: инфраструктурный issue по #118 — тип, приоритет, тема, без S*.
|
||||
// Подставной gh: первый push инфраструктурного issue по #562 — без S*.
|
||||
const ghStub = join(dir, 'gh-stub.mjs');
|
||||
writeFileSync(ghStub, '#!/usr/bin/env node\n'
|
||||
+ 'process.stdout.write(JSON.stringify({ number: 206, state: "OPEN", labels: '
|
||||
|
||||
@@ -16,7 +16,11 @@ test('права выводятся из статусной метки по пр
|
||||
assert.ok(rightsFor('S7-code-review').some((l) => l.includes('#312')));
|
||||
assert.ok(rightsFor('S6-in-progress', ['blocked'])[0].startsWith('blocked'));
|
||||
assert.ok(rightsFor('S7-code-review', ['review-4'])[0].startsWith('review-4'));
|
||||
assert.ok(rightsFor(null).some((l) => l.includes('вне процесса')));
|
||||
assert.ok(rightsFor(null).some((l) => l.includes('продуктовая задача вне процесса')));
|
||||
const infrastructure = rightsFor(null, ['infra']);
|
||||
assert.ok(infrastructure.some((l) => l.includes('инфраструктурную реализацию МОЖНО')));
|
||||
assert.ok(infrastructure.some((l) => l.includes('S7-code-review')));
|
||||
assert.ok(infrastructure.every((l) => !l.includes('продуктовый код трогать МОЖНО')));
|
||||
});
|
||||
|
||||
test('AC распознаются из таблицы ТЗ и из строк тела issue (#496)', () => {
|
||||
@@ -90,6 +94,18 @@ test('пакет собирается и рендерится: статус, м
|
||||
assert.match(renderPacket(buildPacket({ issue: { number: 1, title: 't', state: 'OPEN', body: '' }, labels: [] })), /материал не запушен/);
|
||||
});
|
||||
|
||||
test('#562: statusless infra issue is the accelerated track ending at S7 review', () => {
|
||||
const packet = buildPacket({
|
||||
issue: { number: 562, title: 'process', state: 'OPEN', url: 'u', body: '' },
|
||||
labels: ['P1', 'infra', 'process', 'tech-debt'],
|
||||
});
|
||||
assert.equal(packet.status, null);
|
||||
assert.equal(packet.track, 'инфраструктурный');
|
||||
const md = renderPacket(packet);
|
||||
assert.match(md, /инфраструктурный вход/);
|
||||
assert.match(md, /S7-code-review/);
|
||||
});
|
||||
|
||||
test('#517 AC5: AC берутся из тела issue, файл ТЗ — только когда в теле их нет', () => {
|
||||
const base = {
|
||||
issue: { number: 700, title: 'x', state: 'OPEN', url: 'u', body: '## ТЗ\n\n- AC1. Из тела\n' },
|
||||
|
||||
Reference in New Issue
Block a user