@spec-box/sdd 0.6.4
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/README.md +81 -0
- package/assets/adapters/openspec/instructions.md +47 -0
- package/assets/adapters/spec-box/instructions.md +44 -0
- package/assets/ci/Dockerfile +9 -0
- package/assets/ci/github-sbox-run.yml +85 -0
- package/assets/ci/github-tests.yml +25 -0
- package/assets/project/architecture.md +29 -0
- package/assets/project/contracts.md +29 -0
- package/assets/project/conventions.md +29 -0
- package/assets/project/decisions.README.md +3 -0
- package/assets/project/glossary.md +13 -0
- package/assets/project/overview.md +29 -0
- package/assets/project/testing.md +34 -0
- package/assets/project/workflow.md +29 -0
- package/assets/prompts/fill-project-docs.md +43 -0
- package/assets/roles/challenger.md +33 -0
- package/assets/roles/distiller.md +18 -0
- package/assets/roles/implementer.md +39 -0
- package/assets/roles/planner.md +58 -0
- package/assets/roles/researcher.md +47 -0
- package/assets/roles/reviewer.md +68 -0
- package/assets/roles/tester.md +36 -0
- package/assets/roles/verifier.md +57 -0
- package/assets/schema/default.yaml +121 -0
- package/assets/schema/sbox-answer.schema.json +42 -0
- package/assets/templates/coverage.yaml +12 -0
- package/assets/templates/design.md +48 -0
- package/assets/templates/proposal.md +27 -0
- package/assets/templates/tasks.md +9 -0
- package/assets/templates/test-plan.md +7 -0
- package/bin/sbox.js +5 -0
- package/dist/adapters/host/claude/index.js +112 -0
- package/dist/adapters/host/claude/index.js.map +1 -0
- package/dist/adapters/repo/git.js +81 -0
- package/dist/adapters/repo/git.js.map +1 -0
- package/dist/adapters/repo/github/index.js +136 -0
- package/dist/adapters/repo/github/index.js.map +1 -0
- package/dist/adapters/repo/index.js +4 -0
- package/dist/adapters/repo/index.js.map +1 -0
- package/dist/adapters/repo/local.js +47 -0
- package/dist/adapters/repo/local.js.map +1 -0
- package/dist/adapters/runner/claude.js +129 -0
- package/dist/adapters/runner/claude.js.map +1 -0
- package/dist/adapters/runner/codex.js +151 -0
- package/dist/adapters/runner/codex.js.map +1 -0
- package/dist/adapters/runner/index.js +4 -0
- package/dist/adapters/runner/index.js.map +1 -0
- package/dist/adapters/spec/index.js +4 -0
- package/dist/adapters/spec/index.js.map +1 -0
- package/dist/adapters/spec/openspec/delta.js +268 -0
- package/dist/adapters/spec/openspec/delta.js.map +1 -0
- package/dist/adapters/spec/openspec/index.js +107 -0
- package/dist/adapters/spec/openspec/index.js.map +1 -0
- package/dist/adapters/spec/openspec/parser.js +188 -0
- package/dist/adapters/spec/openspec/parser.js.map +1 -0
- package/dist/adapters/spec/spec-box/delta.js +198 -0
- package/dist/adapters/spec/spec-box/delta.js.map +1 -0
- package/dist/adapters/spec/spec-box/index.js +112 -0
- package/dist/adapters/spec/spec-box/index.js.map +1 -0
- package/dist/adapters/spec/spec-box/yaml.js +74 -0
- package/dist/adapters/spec/spec-box/yaml.js.map +1 -0
- package/dist/cli/commands/artifacts.js +86 -0
- package/dist/cli/commands/artifacts.js.map +1 -0
- package/dist/cli/commands/change.js +111 -0
- package/dist/cli/commands/change.js.map +1 -0
- package/dist/cli/commands/changeset.js +81 -0
- package/dist/cli/commands/changeset.js.map +1 -0
- package/dist/cli/commands/ci.js +45 -0
- package/dist/cli/commands/ci.js.map +1 -0
- package/dist/cli/commands/coverage.js +45 -0
- package/dist/cli/commands/coverage.js.map +1 -0
- package/dist/cli/commands/deliver.js +71 -0
- package/dist/cli/commands/deliver.js.map +1 -0
- package/dist/cli/commands/doctor.js +55 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/host.js +30 -0
- package/dist/cli/commands/host.js.map +1 -0
- package/dist/cli/commands/init.js +150 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/metrics.js +62 -0
- package/dist/cli/commands/metrics.js.map +1 -0
- package/dist/cli/commands/prompt.js +68 -0
- package/dist/cli/commands/prompt.js.map +1 -0
- package/dist/cli/commands/protocol.js +151 -0
- package/dist/cli/commands/protocol.js.map +1 -0
- package/dist/cli/commands/run.js +157 -0
- package/dist/cli/commands/run.js.map +1 -0
- package/dist/cli/commands/spec.js +84 -0
- package/dist/cli/commands/spec.js.map +1 -0
- package/dist/cli/commands/wiring.js +50 -0
- package/dist/cli/commands/wiring.js.map +1 -0
- package/dist/cli/context.js +18 -0
- package/dist/cli/context.js.map +1 -0
- package/dist/cli/main.js +63 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/output.js +31 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/core/archive.js +77 -0
- package/dist/core/archive.js.map +1 -0
- package/dist/core/change.js +240 -0
- package/dist/core/change.js.map +1 -0
- package/dist/core/changeset.js +98 -0
- package/dist/core/changeset.js.map +1 -0
- package/dist/core/config.js +144 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/coverage.js +69 -0
- package/dist/core/coverage.js.map +1 -0
- package/dist/core/deliver.js +190 -0
- package/dist/core/deliver.js.map +1 -0
- package/dist/core/diagnostics.js +12 -0
- package/dist/core/diagnostics.js.map +1 -0
- package/dist/core/drift.js +44 -0
- package/dist/core/drift.js.map +1 -0
- package/dist/core/errors.js +12 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/gates.js +76 -0
- package/dist/core/gates.js.map +1 -0
- package/dist/core/lock.js +57 -0
- package/dist/core/lock.js.map +1 -0
- package/dist/core/metrics.js +140 -0
- package/dist/core/metrics.js.map +1 -0
- package/dist/core/packet.js +172 -0
- package/dist/core/packet.js.map +1 -0
- package/dist/core/paths.js +63 -0
- package/dist/core/paths.js.map +1 -0
- package/dist/core/phases.js +203 -0
- package/dist/core/phases.js.map +1 -0
- package/dist/core/pr-body.js +64 -0
- package/dist/core/pr-body.js.map +1 -0
- package/dist/core/project-docs.js +246 -0
- package/dist/core/project-docs.js.map +1 -0
- package/dist/core/protect.js +47 -0
- package/dist/core/protect.js.map +1 -0
- package/dist/core/repo-host.js +11 -0
- package/dist/core/repo-host.js.map +1 -0
- package/dist/core/report.js +286 -0
- package/dist/core/report.js.map +1 -0
- package/dist/core/result.js +90 -0
- package/dist/core/result.js.map +1 -0
- package/dist/core/roles.js +18 -0
- package/dist/core/roles.js.map +1 -0
- package/dist/core/run.js +222 -0
- package/dist/core/run.js.map +1 -0
- package/dist/core/runner.js +40 -0
- package/dist/core/runner.js.map +1 -0
- package/dist/core/schema.js +104 -0
- package/dist/core/schema.js.map +1 -0
- package/dist/core/spec-adapter.js +12 -0
- package/dist/core/spec-adapter.js.map +1 -0
- package/dist/core/spec-model.js +8 -0
- package/dist/core/spec-model.js.map +1 -0
- package/dist/core/tasks.js +15 -0
- package/dist/core/tasks.js.map +1 -0
- package/dist/core/test-reports.js +60 -0
- package/dist/core/test-reports.js.map +1 -0
- package/dist/core/wiki.js +86 -0
- package/dist/core/wiki.js.map +1 -0
- package/dist/core/wiring.js +65 -0
- package/dist/core/wiring.js.map +1 -0
- package/docs/design.md +829 -0
- package/package.json +66 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { archiveChange } from './archive.js';
|
|
4
|
+
import { loadChange, saveChange } from './change.js';
|
|
5
|
+
import { isGitRepo } from './changeset.js';
|
|
6
|
+
import { reconcileDrift } from './drift.js';
|
|
7
|
+
import { diag } from './diagnostics.js';
|
|
8
|
+
import { SboxError } from './errors.js';
|
|
9
|
+
import { exists, readText } from './paths.js';
|
|
10
|
+
import { renderPullRequestBody } from './pr-body.js';
|
|
11
|
+
import { parseRoleResult } from './result.js';
|
|
12
|
+
import { taskProgress } from './tasks.js';
|
|
13
|
+
/** Последний ответ ревьюера в фазе review с delivery_narrative. */
|
|
14
|
+
export function lastReviewResult(dir, change) {
|
|
15
|
+
const runs = [...change.runs].reverse().filter((r) => r.role === 'reviewer' && r.phase === 'review' && r.status === 'done');
|
|
16
|
+
for (const r of runs) {
|
|
17
|
+
const file = path.join(dir, r.dir ?? `runs/${r.id}`, 'result.md');
|
|
18
|
+
if (!exists(file))
|
|
19
|
+
continue;
|
|
20
|
+
try {
|
|
21
|
+
return parseRoleResult(readText(file));
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
/** Условия готовности к влитию (docs/design.md, раздел 1). */
|
|
30
|
+
export async function readinessChecklist(root, config, dir, change, adapter) {
|
|
31
|
+
const diagnostics = [];
|
|
32
|
+
const dod = [];
|
|
33
|
+
const tasksFile = path.join(dir, 'tasks.md');
|
|
34
|
+
const tasks = exists(tasksFile) ? taskProgress(readText(tasksFile)) : null;
|
|
35
|
+
dod.push({ id: 1, text: 'Все задачи из tasks.md отмечены', ok: tasks ? tasks.remaining === 0 : false, detail: tasks ? `${tasks.done}/${tasks.total}` : 'нет tasks.md' });
|
|
36
|
+
const verification = change.verification;
|
|
37
|
+
const failed = verification?.checks.filter((c) => c.result === 'FAIL') ?? [];
|
|
38
|
+
dod.push({ id: 2, text: 'Автотесты фазы cover проходят, отчёт верификатора приложен', ok: Boolean(verification) && failed.length === 0, detail: verification ? `${verification.checks.length} проверок, FAIL ${failed.length}` : 'нет отчёта' });
|
|
39
|
+
dod.push({ id: 3, text: 'Защищённые тестовые файлы не изменены реализацией', ok: true, detail: `${change.protected.length} шаблонов` });
|
|
40
|
+
dod.push({ id: 4, text: 'Проверки проекта проходят', ok: Boolean(verification) && failed.length === 0 });
|
|
41
|
+
const review = lastReviewResult(dir, change);
|
|
42
|
+
const reviewOk = review?.status === 'готово' && Boolean(review.delivery_narrative);
|
|
43
|
+
const pendingItems = new Set();
|
|
44
|
+
for (const c of verification?.checks ?? [])
|
|
45
|
+
if (c.result !== 'PASS')
|
|
46
|
+
pendingItems.add(c.id);
|
|
47
|
+
for (const g of verification?.gaps ?? [])
|
|
48
|
+
pendingItems.add(g.id);
|
|
49
|
+
for (const d of review?.dispositions ?? [])
|
|
50
|
+
pendingItems.delete(d.item);
|
|
51
|
+
dod.push({ id: 5, text: 'Ревью завершено вердиктом «готово», каждый не-PASS пункт получил disposition', ok: reviewOk && pendingItems.size === 0, detail: pendingItems.size ? `без disposition: ${[...pendingItems].join(', ')}` : undefined });
|
|
52
|
+
let deltasOk = true;
|
|
53
|
+
try {
|
|
54
|
+
if (!change.skip_specs) {
|
|
55
|
+
const truth = await adapter.readTruth();
|
|
56
|
+
const deltas = await adapter.readDelta(path.join(dir, 'specs'));
|
|
57
|
+
const errors = adapter.validate(truth, deltas).filter((d) => d.severity === 'error');
|
|
58
|
+
deltasOk = deltas.length > 0 && errors.length === 0;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
deltasOk = false;
|
|
63
|
+
}
|
|
64
|
+
dod.push({ id: 6, text: 'Дельты спецификаций валидны и будут применены к истине', ok: deltasOk });
|
|
65
|
+
const manualPlan = path.join(dir, 'test-plan.md');
|
|
66
|
+
const needsPlan = config.testing.manualPlan === 'always' || change.accepted_gaps.length > 0;
|
|
67
|
+
dod.push({ id: 7, text: 'План ручного тестирования приложен, если требуется', ok: !needsPlan || exists(manualPlan), detail: needsPlan ? (exists(manualPlan) ? 'есть' : 'требуется, но нет') : 'не требуется' });
|
|
68
|
+
const p0p1 = change.blocker?.category === 'пользователь';
|
|
69
|
+
dod.push({ id: 8, text: 'Открытых вопросов P0 и P1 нет', ok: !p0p1 });
|
|
70
|
+
let bound = true;
|
|
71
|
+
let boundDetail;
|
|
72
|
+
if (change.changeset && isGitRepo(root)) {
|
|
73
|
+
const drift = reconcileDrift(root, config, dir, change, { rebind: true });
|
|
74
|
+
if (drift.rebound) {
|
|
75
|
+
saveChange(dir, change);
|
|
76
|
+
boundDetail = `после ревью изменились только ${drift.benign.join(', ')}; change-set перепривязан`;
|
|
77
|
+
}
|
|
78
|
+
if (!drift.ok) {
|
|
79
|
+
bound = false;
|
|
80
|
+
boundDetail = `после ревью изменился код: ${drift.invalidating.join(', ')}. Выполните \`sbox changeset seal\` и пройдите verify и review заново`;
|
|
81
|
+
}
|
|
82
|
+
else if (change.reviewed_digest !== change.changeset.digest) {
|
|
83
|
+
bound = false;
|
|
84
|
+
boundDetail = 'после последнего запечатывания фазы verify и review не завершались: запустите `/sbox-run` или `sbox run`, они пройдут verify и review по текущему change-set';
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
dod.push({ id: 9, text: 'Вердикты verify и review привязаны к текущему change-set', ok: bound, ...(boundDetail ? { detail: boundDetail } : {}) });
|
|
88
|
+
for (const item of dod)
|
|
89
|
+
if (!item.ok)
|
|
90
|
+
diagnostics.push(diag('error', `DOD_${item.id}`, `${item.text}${item.detail ? ` (${item.detail})` : ''}`, 'deliver'));
|
|
91
|
+
return { dod, diagnostics };
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Доставка (docs/design.md, фаза deliver): чеклист, write-ahead intent, архивация внутри ветки,
|
|
95
|
+
* коммит с ключом, push, пул-реквест. Повторный вызов после сбоя продолжает с записанного состояния.
|
|
96
|
+
*/
|
|
97
|
+
export async function deliverChange(opts) {
|
|
98
|
+
const { root, config, dir, adapter, host } = opts;
|
|
99
|
+
const log = opts.log ?? (() => { });
|
|
100
|
+
let change = opts.change;
|
|
101
|
+
const resuming = Boolean(change.delivery);
|
|
102
|
+
if (!resuming && change.phase !== 'deliver') {
|
|
103
|
+
throw new SboxError('NOT_DELIVERABLE', `Изменение в фазе ${change.phase}, а не deliver.`);
|
|
104
|
+
}
|
|
105
|
+
const { dod, diagnostics } = await readinessChecklist(root, config, dir, change, adapter);
|
|
106
|
+
change = loadChange(dir); // readiness могла перепривязать change-set
|
|
107
|
+
if (diagnostics.length > 0 && !change.delivery && !opts.force) {
|
|
108
|
+
throw new SboxError('DOD_FAILED', `Не выполнены условия готовности:\n${dod.filter((i) => !i.ok).map((i) => ` ${i.id}. ${i.text}${i.detail ? `: ${i.detail}` : ''}`).join('\n')}`, 'Устраните пункты или доставьте как есть: `sbox deliver --force` (невыполненные пункты останутся в чеклисте пул-реквеста).');
|
|
109
|
+
}
|
|
110
|
+
const review = lastReviewResult(dir, change);
|
|
111
|
+
const narrative = review?.delivery_narrative;
|
|
112
|
+
if (!narrative)
|
|
113
|
+
throw new SboxError('DELIVERY_NARRATIVE_MISSING', 'Нет delivery_narrative ревьюера.');
|
|
114
|
+
const deltas = change.skip_specs ? [] : await adapter.readDelta(path.join(dir, 'specs'));
|
|
115
|
+
const branch = change.branch ?? `${config.repo.branchPrefix}${change.id}`;
|
|
116
|
+
const intentKey = change.delivery?.intent_key ?? `${change.id}:${(change.changeset?.digest ?? createHash('sha256').update(change.id).digest('hex')).replace(/^sha256:/, '').slice(0, 16)}`;
|
|
117
|
+
const prBody = renderPullRequestBody({ change, dir, narrative, deltas, dod });
|
|
118
|
+
const prTitle = `${narrative.title}`;
|
|
119
|
+
const commitMessage = `${narrative.title}\n\n${narrative.delta}`;
|
|
120
|
+
// 1. Write-ahead intent.
|
|
121
|
+
if (!change.delivery) {
|
|
122
|
+
change.delivery = { intent_key: intentKey, created_at: new Date().toISOString(), state: 'intent', commit: null, pr: null, error: null };
|
|
123
|
+
change.branch = branch;
|
|
124
|
+
saveChange(dir, change);
|
|
125
|
+
log(`intent ${intentKey} записан`);
|
|
126
|
+
}
|
|
127
|
+
if (isGitRepo(root) && host.currentBranch() !== branch)
|
|
128
|
+
host.ensureBranch(branch, config.repo.baseBranch);
|
|
129
|
+
// 2. Архивация внутри ветки: дельты в истину, папка в архив.
|
|
130
|
+
let archivedTo;
|
|
131
|
+
if (change.delivery.state === 'intent') {
|
|
132
|
+
const result = await archiveChange(root, config, adapter, dir, change, { force: opts.force });
|
|
133
|
+
if (result.cleanedStaleArchive)
|
|
134
|
+
log('удалён пустой каталог архива от прерванной попытки');
|
|
135
|
+
archivedTo = result.archivedTo;
|
|
136
|
+
change = loadChange(archivedTo);
|
|
137
|
+
change.delivery.state = 'archived';
|
|
138
|
+
saveChange(archivedTo, change, { allowTerminalReopen: true });
|
|
139
|
+
log(`архив: ${path.relative(root, archivedTo)}`);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
archivedTo = dir;
|
|
143
|
+
}
|
|
144
|
+
const workDir = archivedTo;
|
|
145
|
+
const receipt = { commit: '', pushed: false, pr: null, reused: { commit: false, pr: false } };
|
|
146
|
+
try {
|
|
147
|
+
// 3. Коммит с ключом идемпотентности.
|
|
148
|
+
const existing = host.findCommitByIntent(intentKey);
|
|
149
|
+
if (existing) {
|
|
150
|
+
receipt.commit = existing;
|
|
151
|
+
receipt.reused.commit = true;
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
receipt.commit = host.commitAll(commitMessage, intentKey);
|
|
155
|
+
}
|
|
156
|
+
change.delivery.commit = receipt.commit;
|
|
157
|
+
change.delivery.state = 'committed';
|
|
158
|
+
saveChange(workDir, change, { allowTerminalReopen: true });
|
|
159
|
+
log(`коммит ${receipt.commit.slice(0, 10)}${receipt.reused.commit ? ' (существующий)' : ''}`);
|
|
160
|
+
// 4. Push и пул-реквест.
|
|
161
|
+
host.push(branch);
|
|
162
|
+
receipt.pushed = true;
|
|
163
|
+
change.delivery.state = 'pushed';
|
|
164
|
+
saveChange(workDir, change, { allowTerminalReopen: true });
|
|
165
|
+
let pr = await host.findPullRequest(branch);
|
|
166
|
+
if (pr)
|
|
167
|
+
receipt.reused.pr = true;
|
|
168
|
+
else if (host.name !== 'local')
|
|
169
|
+
pr = await host.openPullRequest({ branch, base: config.repo.baseBranch, title: prTitle, body: prBody, draft: true });
|
|
170
|
+
if (pr) {
|
|
171
|
+
pr = await host.updatePullRequest(pr, { title: prTitle, body: prBody, draft: opts.keepDraft ? undefined : false });
|
|
172
|
+
change.pr = { number: pr.number, url: pr.url, draft: pr.draft };
|
|
173
|
+
change.delivery.pr = { number: pr.number, url: pr.url };
|
|
174
|
+
log(`пул-реквест ${pr.url}${receipt.reused.pr ? ' (существующий)' : ''}`);
|
|
175
|
+
}
|
|
176
|
+
receipt.pr = pr;
|
|
177
|
+
change.delivery.state = 'done';
|
|
178
|
+
change.status = 'done';
|
|
179
|
+
saveChange(workDir, change, { allowTerminalReopen: true });
|
|
180
|
+
}
|
|
181
|
+
catch (e) {
|
|
182
|
+
// Внешняя операция могла выполниться: фиксируем неизвестный исход, повтор только через resume.
|
|
183
|
+
change.status = 'delivery_unknown';
|
|
184
|
+
change.delivery.error = e.message;
|
|
185
|
+
saveChange(workDir, change, { allowTerminalReopen: true });
|
|
186
|
+
throw new SboxError('DELIVERY_UNKNOWN', `Доставка прервана после внешней операции (${change.delivery.state}): ${e.message}`, 'Проверьте коммит и пул-реквест вручную, затем `sbox change resume` и `sbox deliver`.');
|
|
187
|
+
}
|
|
188
|
+
return { dod, receipt, archivedTo: workDir, prBody };
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=deliver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deliver.js","sourceRoot":"","sources":["../../src/core/deliver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function diag(severity, code, message, target, fix) {
|
|
2
|
+
const d = { severity, code, message };
|
|
3
|
+
if (target)
|
|
4
|
+
d.target = target;
|
|
5
|
+
if (fix)
|
|
6
|
+
d.fix = fix;
|
|
7
|
+
return d;
|
|
8
|
+
}
|
|
9
|
+
export function hasErrors(diagnostics) {
|
|
10
|
+
return diagnostics.some((d) => d.severity === 'error');
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=diagnostics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../../src/core/diagnostics.ts"],"names":[],"mappings":"AAWA,MAAM,UAAU,IAAI,CAClB,QAAkB,EAClB,IAAY,EACZ,OAAe,EACf,MAAe,EACf,GAAY;IAEZ,MAAM,CAAC,GAAe,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IAClD,IAAI,MAAM;QAAE,CAAC,CAAC,MAAM,GAAG,MAAM,CAAC;IAC9B,IAAI,GAAG;QAAE,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC;IACrB,OAAO,CAAC,CAAC;AACX,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,WAAyB;IACjD,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC;AACzD,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { changeSetDrift, classifyDrift, isGitRepo, readChangeSet, sealChangeSet, writeChangeSet } from './changeset.js';
|
|
3
|
+
import { diag } from './diagnostics.js';
|
|
4
|
+
import { changesetExclude } from './report.js';
|
|
5
|
+
import { toPosix } from './paths.js';
|
|
6
|
+
/**
|
|
7
|
+
* Сверка рабочей копии с запечатанным change-set. Безобидный дрейф (документация, .gitignore, материалы хостов)
|
|
8
|
+
* перепривязывается: change-set запечатывается заново, вердикты остаются в силе, факт записывается в change.yaml.
|
|
9
|
+
* Код и конфигурация приложения после ревью меняться не могут: такой дрейф возвращает в фазу verify.
|
|
10
|
+
*/
|
|
11
|
+
export function reconcileDrift(root, config, dir, change, opts) {
|
|
12
|
+
const out = { ok: true, invalidating: [], benign: [], rebound: false, diagnostics: [] };
|
|
13
|
+
if (!change.changeset || !isGitRepo(root))
|
|
14
|
+
return out;
|
|
15
|
+
const sealed = readChangeSet(dir);
|
|
16
|
+
if (!sealed)
|
|
17
|
+
return out;
|
|
18
|
+
const exclude = changesetExclude(config, toPosix(path.relative(root, dir)));
|
|
19
|
+
const drift = changeSetDrift(root, sealed, exclude);
|
|
20
|
+
if (!drift.drifted)
|
|
21
|
+
return out;
|
|
22
|
+
const cls = classifyDrift(drift, config.changeset.nonInvalidating);
|
|
23
|
+
out.invalidating = cls.invalidating;
|
|
24
|
+
out.benign = cls.benign;
|
|
25
|
+
if (cls.invalidating.length > 0) {
|
|
26
|
+
out.ok = false;
|
|
27
|
+
out.diagnostics.push(diag('error', 'CHANGESET_DRIFT', `Код изменился после запечатывания: ${cls.invalidating.slice(0, 10).join(', ')}${cls.benign.length ? ` (плюс безобидные: ${cls.benign.join(', ')})` : ''}`, 'changeset', 'Верните файлы к запечатанному состоянию либо выполните `sbox changeset seal` и пройдите verify и review заново.'));
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
if (!opts.rebind) {
|
|
31
|
+
out.diagnostics.push(diag('info', 'CHANGESET_BENIGN_DRIFT', `После запечатывания изменились только безобидные файлы: ${cls.benign.join(', ')}`, 'changeset'));
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
34
|
+
const fresh = sealChangeSet(root, sealed.base, exclude, sealed.sealed_after);
|
|
35
|
+
writeChangeSet(dir, { ...fresh, sealed_at: sealed.sealed_at });
|
|
36
|
+
const wasBound = change.reviewed_digest === change.changeset.digest;
|
|
37
|
+
change.changeset = { ...change.changeset, digest: fresh.digest, paths: fresh.paths, rebound: [...change.changeset.rebound, { from: change.changeset.digest, to: fresh.digest, files: cls.benign, at: new Date().toISOString() }] };
|
|
38
|
+
if (wasBound)
|
|
39
|
+
change.reviewed_digest = fresh.digest;
|
|
40
|
+
out.rebound = true;
|
|
41
|
+
out.diagnostics.push(diag('info', 'CHANGESET_REBOUND', `Change-set перепривязан: после ревью изменились только ${cls.benign.join(', ')}`, 'changeset'));
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=drift.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drift.js","sourceRoot":"","sources":["../../src/core/drift.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,SAAS,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACxH,OAAO,EAAE,IAAI,EAAmB,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAarC;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,MAAc,EAAE,GAAW,EAAE,MAAc,EAAE,IAAyB;IACjH,MAAM,GAAG,GAAiB,EAAE,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC;IACtG,IAAI,CAAC,MAAM,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC;IACtD,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IAClC,IAAI,CAAC,MAAM;QAAE,OAAO,GAAG,CAAC;IACxB,MAAM,OAAO,GAAG,gBAAgB,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;IAC5E,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IACpD,IAAI,CAAC,KAAK,CAAC,OAAO;QAAE,OAAO,GAAG,CAAC;IAC/B,MAAM,GAAG,GAAG,aAAa,CAAC,KAAK,EAAE,MAAM,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;IACnE,GAAG,CAAC,YAAY,GAAG,GAAG,CAAC,YAAY,CAAC;IACpC,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;IACxB,IAAI,GAAG,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChC,GAAG,CAAC,EAAE,GAAG,KAAK,CAAC;QACf,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,iBAAiB,EAAE,sCAAsC,GAAG,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,sBAAsB,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,WAAW,EAAE,iHAAiH,CAAC,CAAC,CAAC;QACnV,OAAO,GAAG,CAAC;IACb,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QACjB,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,wBAAwB,EAAE,2DAA2D,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC,CAAC;QAC9J,OAAO,GAAG,CAAC;IACb,CAAC;IACD,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;IAC7E,cAAc,CAAC,GAAG,EAAE,EAAE,GAAG,KAAK,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;IAC/D,MAAM,QAAQ,GAAG,MAAM,CAAC,eAAe,KAAK,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC;IACpE,MAAM,CAAC,SAAS,GAAG,EAAE,GAAG,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,EAAE,CAAC;IACnO,IAAI,QAAQ;QAAE,MAAM,CAAC,eAAe,GAAG,KAAK,CAAC,MAAM,CAAC;IACpD,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC;IACnB,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,mBAAmB,EAAE,0DAA0D,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC,CAAC;IACxJ,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Ошибка инструмента с машиночитаемым кодом: попадает в JSON-конверт диагностики. */
|
|
2
|
+
export class SboxError extends Error {
|
|
3
|
+
code;
|
|
4
|
+
fix;
|
|
5
|
+
constructor(code, message, fix) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.code = code;
|
|
8
|
+
this.fix = fix;
|
|
9
|
+
this.name = 'SboxError';
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,MAAM,OAAO,SAAU,SAAQ,KAAK;IAEhB,IAAI;IAEJ,GAAG;IAHrB,YACkB,IAAY,EAC5B,OAAe,EACC,GAAY;QAE5B,KAAK,CAAC,OAAO,CAAC,CAAC;oBAJC,IAAI;mBAEJ,GAAG;QAGnB,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;IAC1B,CAAC;CACF"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { loadChange, saveChange } from './change.js';
|
|
3
|
+
import { approveGate, nextStep, rejectGate } from './phases.js';
|
|
4
|
+
import { artifactStates, loadWorkflow } from './schema.js';
|
|
5
|
+
import { toPosix } from './paths.js';
|
|
6
|
+
const COMMAND_RE = /^\s*\/sbox\s+(approve|reject)\s+(proposal|plan|tests)\b\s*([\s\S]*)$/im;
|
|
7
|
+
export function parseGateCommand(body, author, at) {
|
|
8
|
+
const m = body.match(COMMAND_RE);
|
|
9
|
+
if (!m)
|
|
10
|
+
return null;
|
|
11
|
+
const action = m[1].toLowerCase();
|
|
12
|
+
const gate = m[2].toLowerCase();
|
|
13
|
+
const rest = (m[3] ?? '').trim();
|
|
14
|
+
const answers = {};
|
|
15
|
+
const comment = rest
|
|
16
|
+
.split(/\s+/)
|
|
17
|
+
.filter((tok) => {
|
|
18
|
+
const a = tok.match(/^(Q\d+)=(.+)$/i);
|
|
19
|
+
if (a) {
|
|
20
|
+
answers[a[1].toUpperCase()] = a[2];
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
return true;
|
|
24
|
+
})
|
|
25
|
+
.join(' ')
|
|
26
|
+
.trim();
|
|
27
|
+
return { action, gate, ...(comment ? { comment } : {}), ...(Object.keys(answers).length ? { answers } : {}), author, at };
|
|
28
|
+
}
|
|
29
|
+
export function gateQuestion(root, dir, change, gate) {
|
|
30
|
+
const states = artifactStates(loadWorkflow(root), change, dir);
|
|
31
|
+
const files = states.filter((s) => s.existing.length > 0).flatMap((s) => s.existing.map((f) => `- \`${toPosix(path.relative(root, f))}\``));
|
|
32
|
+
const what = gate === 'proposal' ? 'предложение' : gate === 'plan' ? 'план: дизайн, дельты спецификаций и задачи' : 'тесты и план тестирования';
|
|
33
|
+
return [
|
|
34
|
+
`### sbox: гейт \`${gate}\``,
|
|
35
|
+
'',
|
|
36
|
+
`Нужно решение человека по изменению \`${change.id}\` (${change.title}): ${what}.`,
|
|
37
|
+
'',
|
|
38
|
+
'Посмотрите:',
|
|
39
|
+
...files,
|
|
40
|
+
'',
|
|
41
|
+
`Утвердить: \`/sbox approve ${gate}\` (ответы на вопросы: \`/sbox approve ${gate} Q1=B Q2=A\`)`,
|
|
42
|
+
`Отклонить: \`/sbox reject ${gate} <замечание>\``,
|
|
43
|
+
].join('\n');
|
|
44
|
+
}
|
|
45
|
+
/** Опубликовать вопрос гейта в пул-реквесте (один раз) и применить первую найденную команду. */
|
|
46
|
+
export async function pollPullRequestGate(root, config, dir, host, pr) {
|
|
47
|
+
let change = loadChange(dir);
|
|
48
|
+
const step = nextStep(change, config);
|
|
49
|
+
if (step.kind !== 'gate')
|
|
50
|
+
return { posted: false, applied: null };
|
|
51
|
+
const gate = step.gate;
|
|
52
|
+
const state = change.gates[gate];
|
|
53
|
+
let posted = false;
|
|
54
|
+
if (!state.posted_at) {
|
|
55
|
+
await host.postComment(pr, gateQuestion(root, dir, change, gate));
|
|
56
|
+
change.gates[gate] = { ...state, posted_at: new Date().toISOString() };
|
|
57
|
+
saveChange(dir, change);
|
|
58
|
+
posted = true;
|
|
59
|
+
change = loadChange(dir);
|
|
60
|
+
}
|
|
61
|
+
const since = change.gates[gate].posted_at;
|
|
62
|
+
const comments = await host.readComments(pr, since);
|
|
63
|
+
for (const c of comments) {
|
|
64
|
+
const cmd = parseGateCommand(c.body, c.author, c.createdAt);
|
|
65
|
+
if (!cmd || cmd.gate !== gate)
|
|
66
|
+
continue;
|
|
67
|
+
if (cmd.action === 'approve')
|
|
68
|
+
approveGate(change, gate, cmd.author, cmd.comment, cmd.answers);
|
|
69
|
+
else
|
|
70
|
+
rejectGate(change, gate, cmd.author, cmd.comment ?? 'отклонено без комментария');
|
|
71
|
+
saveChange(dir, change);
|
|
72
|
+
return { posted, applied: cmd };
|
|
73
|
+
}
|
|
74
|
+
return { posted, applied: null };
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=gates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gates.js","sourceRoot":"","sources":["../../src/core/gates.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { SboxError } from './errors.js';
|
|
4
|
+
export const LOCK_FILE = '.lock';
|
|
5
|
+
function pidAlive(pid) {
|
|
6
|
+
try {
|
|
7
|
+
process.kill(pid, 0);
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
catch (e) {
|
|
11
|
+
return e.code === 'EPERM';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function readLock(dir) {
|
|
15
|
+
const file = path.join(dir, LOCK_FILE);
|
|
16
|
+
if (!fs.existsSync(file))
|
|
17
|
+
return null;
|
|
18
|
+
try {
|
|
19
|
+
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Эксклюзивная блокировка папки изменения (docs/design.md, «Файл change.yaml»).
|
|
27
|
+
* Мёртвый владелец (pid не жив) считается устаревшей блокировкой и снимается.
|
|
28
|
+
*/
|
|
29
|
+
export function acquireLock(dir, owner) {
|
|
30
|
+
const file = path.join(dir, LOCK_FILE);
|
|
31
|
+
const existing = readLock(dir);
|
|
32
|
+
if (existing && pidAlive(existing.pid)) {
|
|
33
|
+
throw new SboxError('CHANGE_BUSY', `Изменение занято процессом ${existing.pid} (${existing.owner}) с ${existing.started}.`, 'Дождитесь завершения или остановите его командой `sbox stop`.');
|
|
34
|
+
}
|
|
35
|
+
if (existing)
|
|
36
|
+
fs.rmSync(file, { force: true });
|
|
37
|
+
const record = { pid: process.pid, owner, started: new Date().toISOString() };
|
|
38
|
+
const fd = fs.openSync(file, 'wx');
|
|
39
|
+
fs.writeSync(fd, JSON.stringify(record));
|
|
40
|
+
fs.closeSync(fd);
|
|
41
|
+
let released = false;
|
|
42
|
+
return () => {
|
|
43
|
+
if (released)
|
|
44
|
+
return;
|
|
45
|
+
released = true;
|
|
46
|
+
const current = readLock(dir);
|
|
47
|
+
if (current && current.pid === process.pid)
|
|
48
|
+
fs.rmSync(file, { force: true });
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export function withLock(dir, owner, fn) {
|
|
52
|
+
const release = acquireLock(dir, owner);
|
|
53
|
+
return Promise.resolve()
|
|
54
|
+
.then(fn)
|
|
55
|
+
.finally(release);
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/core/lock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,MAAM,CAAC,MAAM,SAAS,GAAG,OAAO,CAAC;AAQjC,SAAS,QAAQ,CAAC,GAAW;IAC3B,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAQ,CAA2B,CAAC,IAAI,KAAK,OAAO,CAAC;IACvD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,GAAW;IAClC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IACvC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACtC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAe,CAAC;IACjE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,GAAW,EAAE,KAAa;IACpD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IACvC,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC/B,IAAI,QAAQ,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,SAAS,CAAC,aAAa,EAAE,8BAA8B,QAAQ,CAAC,GAAG,KAAK,QAAQ,CAAC,KAAK,OAAO,QAAQ,CAAC,OAAO,GAAG,EAAE,+DAA+D,CAAC,CAAC;IAC/L,CAAC;IACD,IAAI,QAAQ;QAAE,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/C,MAAM,MAAM,GAAe,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;IAC1F,MAAM,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACnC,EAAE,CAAC,SAAS,CAAC,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;IACzC,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IACjB,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,OAAO,GAAG,EAAE;QACV,IAAI,QAAQ;YAAE,OAAO;QACrB,QAAQ,GAAG,IAAI,CAAC;QAChB,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,OAAO,IAAI,OAAO,CAAC,GAAG,KAAK,OAAO,CAAC,GAAG;YAAE,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/E,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,QAAQ,CAAI,GAAW,EAAE,KAAa,EAAE,EAAwB;IAC9E,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACxC,OAAO,OAAO,CAAC,OAAO,EAAE;SACrB,IAAI,CAAC,EAAE,CAAC;SACR,OAAO,CAAC,OAAO,CAAC,CAAC;AACtB,CAAC"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { archiveDir, loadChange } from './change.js';
|
|
4
|
+
import { exists, readText } from './paths.js';
|
|
5
|
+
function minutes(a, b) {
|
|
6
|
+
if (!a || !b)
|
|
7
|
+
return null;
|
|
8
|
+
const ms = new Date(b).getTime() - new Date(a).getTime();
|
|
9
|
+
return Number.isFinite(ms) && ms >= 0 ? ms / 60_000 : null;
|
|
10
|
+
}
|
|
11
|
+
function readReceipts(dir) {
|
|
12
|
+
const runs = path.join(dir, 'runs');
|
|
13
|
+
if (!exists(runs))
|
|
14
|
+
return [];
|
|
15
|
+
return fs
|
|
16
|
+
.readdirSync(runs, { withFileTypes: true })
|
|
17
|
+
.filter((e) => e.isDirectory())
|
|
18
|
+
.map((e) => {
|
|
19
|
+
const rdir = path.join(runs, e.name);
|
|
20
|
+
const receiptFile = path.join(rdir, 'receipt.json');
|
|
21
|
+
const packetFile = path.join(rdir, 'packet.json');
|
|
22
|
+
const resultFile = path.join(rdir, 'result.md');
|
|
23
|
+
const receipt = exists(receiptFile) ? JSON.parse(readText(receiptFile)) : {};
|
|
24
|
+
// В интерактивном режиме время старта это момент выдачи пакета, окончания это момент записи ответа.
|
|
25
|
+
const started = receipt.started ?? (exists(packetFile) ? fs.statSync(packetFile).mtime.toISOString() : null);
|
|
26
|
+
const finished = receipt.finished ?? (exists(resultFile) ? fs.statSync(resultFile).mtime.toISOString() : null);
|
|
27
|
+
return { id: e.name, role: receipt.role ?? '', phase: receipt.phase ?? '', started, finished, cost_usd: receipt.cost_usd ?? null, status: receipt.status ?? 'done' };
|
|
28
|
+
})
|
|
29
|
+
.sort((a, b) => Number(a.id.slice(1)) - Number(b.id.slice(1)));
|
|
30
|
+
}
|
|
31
|
+
export function changeMetrics(dir, change) {
|
|
32
|
+
const receipts = readReceipts(dir);
|
|
33
|
+
const phases = {};
|
|
34
|
+
let agentMinutes = 0;
|
|
35
|
+
let cost = 0;
|
|
36
|
+
let hasCost = false;
|
|
37
|
+
for (const r of receipts) {
|
|
38
|
+
const p = (phases[r.phase] ??= { runs: 0, agent_minutes: 0, returns: 0, rejections: 0 });
|
|
39
|
+
p.runs += 1;
|
|
40
|
+
const m = minutes(r.started, r.finished) ?? 0;
|
|
41
|
+
p.agent_minutes += m;
|
|
42
|
+
agentMinutes += m;
|
|
43
|
+
if (r.cost_usd !== null) {
|
|
44
|
+
cost += r.cost_usd;
|
|
45
|
+
hasCost = true;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
for (const [key, n] of Object.entries(change.returns)) {
|
|
49
|
+
if (key.endsWith(':rejections'))
|
|
50
|
+
(phases[key.replace(':rejections', '')] ??= { runs: 0, agent_minutes: 0, returns: 0, rejections: 0 }).rejections = n;
|
|
51
|
+
else
|
|
52
|
+
(phases[key] ??= { runs: 0, agent_minutes: 0, returns: 0, rejections: 0 }).returns = n;
|
|
53
|
+
}
|
|
54
|
+
const gates = {};
|
|
55
|
+
let gateRejections = 0;
|
|
56
|
+
for (const [gate, g] of Object.entries(change.gates)) {
|
|
57
|
+
// Ожидание человека: от последнего запуска фазы перед гейтом до решения.
|
|
58
|
+
const phaseOfGate = gate === 'proposal' ? 'propose' : gate === 'plan' ? 'plan' : 'tests_review';
|
|
59
|
+
const lastRun = [...receipts].reverse().find((r) => r.phase === phaseOfGate);
|
|
60
|
+
const wait = g.at ? minutes(lastRun?.finished ?? null, g.at) : null;
|
|
61
|
+
gates[gate] = { state: g.state, wait_minutes: wait, rejected_times: g.rejected_times ?? 0 };
|
|
62
|
+
gateRejections += g.rejected_times ?? 0;
|
|
63
|
+
}
|
|
64
|
+
const resumes = change.interventions?.resumes ?? 0;
|
|
65
|
+
const userBlockers = change.interventions?.user_blockers ?? 0;
|
|
66
|
+
const reportRejections = Object.values(phases).reduce((n, p) => n + p.rejections, 0);
|
|
67
|
+
const total = gateRejections + resumes + userBlockers + reportRejections;
|
|
68
|
+
const finished = change.status === 'done' || change.status === 'archived' ? (receipts.at(-1)?.finished ?? null) : null;
|
|
69
|
+
return {
|
|
70
|
+
id: change.id,
|
|
71
|
+
title: change.title,
|
|
72
|
+
status: change.status,
|
|
73
|
+
phase: change.phase,
|
|
74
|
+
size: change.size,
|
|
75
|
+
autonomy: change.autonomy ?? 'по конфигу',
|
|
76
|
+
created: change.created,
|
|
77
|
+
finished,
|
|
78
|
+
wall_hours: finished ? (minutes(`${change.created}T00:00:00Z`, finished) ?? 0) / 60 : null,
|
|
79
|
+
runs: receipts.length,
|
|
80
|
+
agent_minutes: Math.round(agentMinutes * 10) / 10,
|
|
81
|
+
cost_usd: hasCost ? Math.round(cost * 100) / 100 : null,
|
|
82
|
+
phases,
|
|
83
|
+
gates,
|
|
84
|
+
interventions: { gate_rejections: gateRejections, resumes, user_blockers: userBlockers, report_rejections: reportRejections, total },
|
|
85
|
+
autonomous: total === 0,
|
|
86
|
+
rating: change.rating ?? null,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/** Все изменения проекта: активные и архивные. */
|
|
90
|
+
export function allChangeDirs(root, config) {
|
|
91
|
+
const base = path.join(root, config.changes.dir);
|
|
92
|
+
const out = [];
|
|
93
|
+
if (exists(base)) {
|
|
94
|
+
for (const e of fs.readdirSync(base, { withFileTypes: true })) {
|
|
95
|
+
if (e.isDirectory() && e.name !== 'archive' && exists(path.join(base, e.name, 'change.yaml')))
|
|
96
|
+
out.push(path.join(base, e.name));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const archive = archiveDir(root, config);
|
|
100
|
+
if (exists(archive)) {
|
|
101
|
+
for (const e of fs.readdirSync(archive, { withFileTypes: true })) {
|
|
102
|
+
if (e.isDirectory() && exists(path.join(archive, e.name, 'change.yaml')))
|
|
103
|
+
out.push(path.join(archive, e.name));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return out;
|
|
107
|
+
}
|
|
108
|
+
export function findChangeDir(root, config, id) {
|
|
109
|
+
return allChangeDirs(root, config).find((d) => {
|
|
110
|
+
try {
|
|
111
|
+
return loadChange(d).id === id;
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
}) ?? null;
|
|
117
|
+
}
|
|
118
|
+
function median(values) {
|
|
119
|
+
if (values.length === 0)
|
|
120
|
+
return null;
|
|
121
|
+
const s = [...values].sort((a, b) => a - b);
|
|
122
|
+
const mid = Math.floor(s.length / 2);
|
|
123
|
+
return s.length % 2 ? s[mid] : (s[mid - 1] + s[mid]) / 2;
|
|
124
|
+
}
|
|
125
|
+
export function summarize(all) {
|
|
126
|
+
const finished = all.filter((m) => m.finished);
|
|
127
|
+
const costs = all.filter((m) => m.cost_usd !== null).map((m) => m.cost_usd);
|
|
128
|
+
const ratings = all.filter((m) => m.rating).map((m) => m.rating.score);
|
|
129
|
+
return {
|
|
130
|
+
changes: all.length,
|
|
131
|
+
finished: finished.length,
|
|
132
|
+
autonomous_share: finished.length ? Math.round((finished.filter((m) => m.autonomous).length / finished.length) * 100) / 100 : null,
|
|
133
|
+
median_agent_minutes: median(all.map((m) => m.agent_minutes)),
|
|
134
|
+
median_runs: median(all.map((m) => m.runs)),
|
|
135
|
+
total_cost_usd: costs.length ? Math.round(costs.reduce((a, b) => a + b, 0) * 100) / 100 : null,
|
|
136
|
+
mean_rating: ratings.length ? Math.round((ratings.reduce((a, b) => a + b, 0) / ratings.length) * 10) / 10 : null,
|
|
137
|
+
interventions_per_change: all.length ? Math.round((all.reduce((n, m) => n + m.interventions.total, 0) / all.length) * 10) / 10 : null,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=metrics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.js","sourceRoot":"","sources":["../../src/core/metrics.ts"],"names":[],"mappings":"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"}
|