mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
fix: подсказывать infra-вход до появления ветки (#562)
Issue: #562 User-Visible: no
This commit is contained in:
committed by
claude[bot]
parent
a66649a13b
commit
d6180f7f5d
+10
-3
@@ -24,7 +24,7 @@ import { classify } from './process-gate.mjs';
|
||||
export const STATUS_LABELS = ['S1-new', 'S2-analysis', 'S3-spec', 'S4-spec-review', 'S5-ready', 'S6-in-progress', 'S7-code-review', 'S8-merged'];
|
||||
|
||||
/** Что разрешено в статусе — по PROCESS.md, без домыслов. */
|
||||
export function rightsFor(status, labels = [], { infrastructure = false } = {}) {
|
||||
export function rightsFor(status, labels = [], { infrastructure = false, infrastructureHint = false } = {}) {
|
||||
const blocked = labels.includes('blocked');
|
||||
const exhausted = labels.includes('review-4');
|
||||
const code = !infrastructure && ['S5-ready', 'S6-in-progress', 'S7-code-review'].includes(status);
|
||||
@@ -36,6 +36,9 @@ export function rightsFor(status, labels = [], { infrastructure = false } = {})
|
||||
: infrastructure
|
||||
? 'файлы класса A трогать НЕЛЬЗЯ; инфраструктурную реализацию МОЖНО вести сразу по issue (#562)'
|
||||
: 'продуктовый код трогать НЕЛЬЗЯ: статус не S5/S6/S7 (правило №1)');
|
||||
if (infrastructureHint) {
|
||||
lines.push('метка infra — только подсказка, не доказательство и не право: до ветки проверь предполагаемые пути; без class A начинай сразу, при любом class A нужен продуктовый S-flow (#562)');
|
||||
}
|
||||
switch (status) {
|
||||
case 'S1-new': lines.push('следующий шаг: аналитика (S2) — оценки метками, критерий лёгкого трека, затем ТЗ'); break;
|
||||
case 'S2-analysis': lines.push('следующий шаг: ТЗ (S3); трек по умолчанию small — отказ от него обосновать названным критерием §5'); break;
|
||||
@@ -48,7 +51,9 @@ export function rightsFor(status, labels = [], { infrastructure = false } = {})
|
||||
default:
|
||||
lines.push(infrastructure
|
||||
? 'инфраструктурный вход: реализовать и проверить → push ветки → S7-code-review; ТЗ и S1–S6 не нужны (#562)'
|
||||
: 'статусной метки нет — продуктовая задача вне процесса; вход — первая S*-метка владельца');
|
||||
: infrastructureHint
|
||||
? 'предварительный инфраструктурный вход: после проверки отсутствия class A реализовать → push ветки → S7-code-review; diff станет окончательным доказательством'
|
||||
: 'статусной метки нет — продуктовая задача вне процесса; вход — первая S*-метка владельца');
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
@@ -116,8 +121,10 @@ export function buildPacket(inputs) {
|
||||
// доказывается тем же механическим признаком, что process-gate: в реальном
|
||||
// diff опубликованной ветки нет ни одного файла класса A.
|
||||
const infrastructure = branch?.infrastructure === true;
|
||||
const infrastructureHint = branch == null && status == null && labels.includes('infra');
|
||||
const track = infrastructure
|
||||
? 'инфраструктурный'
|
||||
: infrastructureHint ? 'инфраструктурный (предварительно; подтвердить путями/diff)'
|
||||
: 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);
|
||||
@@ -132,7 +139,7 @@ export function buildPacket(inputs) {
|
||||
const unverified = acs.filter((a) => a.evidence.startsWith('без записи'));
|
||||
const packet = {
|
||||
issue: { number: issue.number, title: issue.title, state: issue.state, url: issue.url },
|
||||
status, track, labels, rights: rightsFor(status, labels, { infrastructure }),
|
||||
status, track, labels, rights: rightsFor(status, labels, { infrastructure, infrastructureHint }),
|
||||
decisions: ownerDecisions(comments, owner),
|
||||
material: branch ? {
|
||||
branch: branch.name, tip: branch.tip, base: branch.base, ahead: branch.ahead, behind: branch.behind,
|
||||
|
||||
@@ -23,6 +23,9 @@ test('права выводятся из статусной метки по пр
|
||||
assert.ok(infrastructure.every((l) => !l.includes('продуктовый код трогать МОЖНО')));
|
||||
assert.ok(rightsFor('S6-in-progress', ['infra']).some((l) => l.includes('продуктовый код трогать МОЖНО')),
|
||||
'the thematic infra label alone must not override an S6 product status');
|
||||
const hint = rightsFor(null, ['infra'], { infrastructureHint: true });
|
||||
assert.ok(hint.some((l) => l.includes('только подсказка, не доказательство и не право')));
|
||||
assert.ok(hint.some((l) => l.includes('предварительный инфраструктурный вход')));
|
||||
});
|
||||
|
||||
test('AC распознаются из таблицы ТЗ и из строк тела issue (#496)', () => {
|
||||
@@ -119,6 +122,19 @@ test('#562: the infra label alone never grants the accelerated track', () => {
|
||||
assert.ok(packet.rights.some((l) => l.includes('продуктовый код трогать МОЖНО')));
|
||||
});
|
||||
|
||||
test('#562: before a branch exists the infra label prompts classification but grants no rights', () => {
|
||||
const packet = buildPacket({
|
||||
issue: { number: 1000, title: 'unpublished infra candidate', state: 'OPEN', url: 'u', body: '' },
|
||||
labels: ['infra'],
|
||||
branch: null,
|
||||
});
|
||||
assert.equal(packet.status, null);
|
||||
assert.match(packet.track, /предварительно/);
|
||||
assert.ok(packet.rights.some((l) => l.includes('без class A начинай сразу')));
|
||||
assert.ok(packet.rights.some((l) => l.includes('не доказательство и не право')));
|
||||
assert.ok(packet.rights.every((l) => !l.includes('продуктовый код трогать МОЖНО')));
|
||||
});
|
||||
|
||||
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