@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.
Files changed (161) hide show
  1. package/README.md +81 -0
  2. package/assets/adapters/openspec/instructions.md +47 -0
  3. package/assets/adapters/spec-box/instructions.md +44 -0
  4. package/assets/ci/Dockerfile +9 -0
  5. package/assets/ci/github-sbox-run.yml +85 -0
  6. package/assets/ci/github-tests.yml +25 -0
  7. package/assets/project/architecture.md +29 -0
  8. package/assets/project/contracts.md +29 -0
  9. package/assets/project/conventions.md +29 -0
  10. package/assets/project/decisions.README.md +3 -0
  11. package/assets/project/glossary.md +13 -0
  12. package/assets/project/overview.md +29 -0
  13. package/assets/project/testing.md +34 -0
  14. package/assets/project/workflow.md +29 -0
  15. package/assets/prompts/fill-project-docs.md +43 -0
  16. package/assets/roles/challenger.md +33 -0
  17. package/assets/roles/distiller.md +18 -0
  18. package/assets/roles/implementer.md +39 -0
  19. package/assets/roles/planner.md +58 -0
  20. package/assets/roles/researcher.md +47 -0
  21. package/assets/roles/reviewer.md +68 -0
  22. package/assets/roles/tester.md +36 -0
  23. package/assets/roles/verifier.md +57 -0
  24. package/assets/schema/default.yaml +121 -0
  25. package/assets/schema/sbox-answer.schema.json +42 -0
  26. package/assets/templates/coverage.yaml +12 -0
  27. package/assets/templates/design.md +48 -0
  28. package/assets/templates/proposal.md +27 -0
  29. package/assets/templates/tasks.md +9 -0
  30. package/assets/templates/test-plan.md +7 -0
  31. package/bin/sbox.js +5 -0
  32. package/dist/adapters/host/claude/index.js +112 -0
  33. package/dist/adapters/host/claude/index.js.map +1 -0
  34. package/dist/adapters/repo/git.js +81 -0
  35. package/dist/adapters/repo/git.js.map +1 -0
  36. package/dist/adapters/repo/github/index.js +136 -0
  37. package/dist/adapters/repo/github/index.js.map +1 -0
  38. package/dist/adapters/repo/index.js +4 -0
  39. package/dist/adapters/repo/index.js.map +1 -0
  40. package/dist/adapters/repo/local.js +47 -0
  41. package/dist/adapters/repo/local.js.map +1 -0
  42. package/dist/adapters/runner/claude.js +129 -0
  43. package/dist/adapters/runner/claude.js.map +1 -0
  44. package/dist/adapters/runner/codex.js +151 -0
  45. package/dist/adapters/runner/codex.js.map +1 -0
  46. package/dist/adapters/runner/index.js +4 -0
  47. package/dist/adapters/runner/index.js.map +1 -0
  48. package/dist/adapters/spec/index.js +4 -0
  49. package/dist/adapters/spec/index.js.map +1 -0
  50. package/dist/adapters/spec/openspec/delta.js +268 -0
  51. package/dist/adapters/spec/openspec/delta.js.map +1 -0
  52. package/dist/adapters/spec/openspec/index.js +107 -0
  53. package/dist/adapters/spec/openspec/index.js.map +1 -0
  54. package/dist/adapters/spec/openspec/parser.js +188 -0
  55. package/dist/adapters/spec/openspec/parser.js.map +1 -0
  56. package/dist/adapters/spec/spec-box/delta.js +198 -0
  57. package/dist/adapters/spec/spec-box/delta.js.map +1 -0
  58. package/dist/adapters/spec/spec-box/index.js +112 -0
  59. package/dist/adapters/spec/spec-box/index.js.map +1 -0
  60. package/dist/adapters/spec/spec-box/yaml.js +74 -0
  61. package/dist/adapters/spec/spec-box/yaml.js.map +1 -0
  62. package/dist/cli/commands/artifacts.js +86 -0
  63. package/dist/cli/commands/artifacts.js.map +1 -0
  64. package/dist/cli/commands/change.js +111 -0
  65. package/dist/cli/commands/change.js.map +1 -0
  66. package/dist/cli/commands/changeset.js +81 -0
  67. package/dist/cli/commands/changeset.js.map +1 -0
  68. package/dist/cli/commands/ci.js +45 -0
  69. package/dist/cli/commands/ci.js.map +1 -0
  70. package/dist/cli/commands/coverage.js +45 -0
  71. package/dist/cli/commands/coverage.js.map +1 -0
  72. package/dist/cli/commands/deliver.js +71 -0
  73. package/dist/cli/commands/deliver.js.map +1 -0
  74. package/dist/cli/commands/doctor.js +55 -0
  75. package/dist/cli/commands/doctor.js.map +1 -0
  76. package/dist/cli/commands/host.js +30 -0
  77. package/dist/cli/commands/host.js.map +1 -0
  78. package/dist/cli/commands/init.js +150 -0
  79. package/dist/cli/commands/init.js.map +1 -0
  80. package/dist/cli/commands/metrics.js +62 -0
  81. package/dist/cli/commands/metrics.js.map +1 -0
  82. package/dist/cli/commands/prompt.js +68 -0
  83. package/dist/cli/commands/prompt.js.map +1 -0
  84. package/dist/cli/commands/protocol.js +151 -0
  85. package/dist/cli/commands/protocol.js.map +1 -0
  86. package/dist/cli/commands/run.js +157 -0
  87. package/dist/cli/commands/run.js.map +1 -0
  88. package/dist/cli/commands/spec.js +84 -0
  89. package/dist/cli/commands/spec.js.map +1 -0
  90. package/dist/cli/commands/wiring.js +50 -0
  91. package/dist/cli/commands/wiring.js.map +1 -0
  92. package/dist/cli/context.js +18 -0
  93. package/dist/cli/context.js.map +1 -0
  94. package/dist/cli/main.js +63 -0
  95. package/dist/cli/main.js.map +1 -0
  96. package/dist/cli/output.js +31 -0
  97. package/dist/cli/output.js.map +1 -0
  98. package/dist/core/archive.js +77 -0
  99. package/dist/core/archive.js.map +1 -0
  100. package/dist/core/change.js +240 -0
  101. package/dist/core/change.js.map +1 -0
  102. package/dist/core/changeset.js +98 -0
  103. package/dist/core/changeset.js.map +1 -0
  104. package/dist/core/config.js +144 -0
  105. package/dist/core/config.js.map +1 -0
  106. package/dist/core/coverage.js +69 -0
  107. package/dist/core/coverage.js.map +1 -0
  108. package/dist/core/deliver.js +190 -0
  109. package/dist/core/deliver.js.map +1 -0
  110. package/dist/core/diagnostics.js +12 -0
  111. package/dist/core/diagnostics.js.map +1 -0
  112. package/dist/core/drift.js +44 -0
  113. package/dist/core/drift.js.map +1 -0
  114. package/dist/core/errors.js +12 -0
  115. package/dist/core/errors.js.map +1 -0
  116. package/dist/core/gates.js +76 -0
  117. package/dist/core/gates.js.map +1 -0
  118. package/dist/core/lock.js +57 -0
  119. package/dist/core/lock.js.map +1 -0
  120. package/dist/core/metrics.js +140 -0
  121. package/dist/core/metrics.js.map +1 -0
  122. package/dist/core/packet.js +172 -0
  123. package/dist/core/packet.js.map +1 -0
  124. package/dist/core/paths.js +63 -0
  125. package/dist/core/paths.js.map +1 -0
  126. package/dist/core/phases.js +203 -0
  127. package/dist/core/phases.js.map +1 -0
  128. package/dist/core/pr-body.js +64 -0
  129. package/dist/core/pr-body.js.map +1 -0
  130. package/dist/core/project-docs.js +246 -0
  131. package/dist/core/project-docs.js.map +1 -0
  132. package/dist/core/protect.js +47 -0
  133. package/dist/core/protect.js.map +1 -0
  134. package/dist/core/repo-host.js +11 -0
  135. package/dist/core/repo-host.js.map +1 -0
  136. package/dist/core/report.js +286 -0
  137. package/dist/core/report.js.map +1 -0
  138. package/dist/core/result.js +90 -0
  139. package/dist/core/result.js.map +1 -0
  140. package/dist/core/roles.js +18 -0
  141. package/dist/core/roles.js.map +1 -0
  142. package/dist/core/run.js +222 -0
  143. package/dist/core/run.js.map +1 -0
  144. package/dist/core/runner.js +40 -0
  145. package/dist/core/runner.js.map +1 -0
  146. package/dist/core/schema.js +104 -0
  147. package/dist/core/schema.js.map +1 -0
  148. package/dist/core/spec-adapter.js +12 -0
  149. package/dist/core/spec-adapter.js.map +1 -0
  150. package/dist/core/spec-model.js +8 -0
  151. package/dist/core/spec-model.js.map +1 -0
  152. package/dist/core/tasks.js +15 -0
  153. package/dist/core/tasks.js.map +1 -0
  154. package/dist/core/test-reports.js +60 -0
  155. package/dist/core/test-reports.js.map +1 -0
  156. package/dist/core/wiki.js +86 -0
  157. package/dist/core/wiki.js.map +1 -0
  158. package/dist/core/wiring.js +65 -0
  159. package/dist/core/wiring.js.map +1 -0
  160. package/docs/design.md +829 -0
  161. package/package.json +66 -0
@@ -0,0 +1,172 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { loadRoleText } from './roles.js';
4
+ import { artifactInstructions, artifactStates, loadWorkflow, pendingArtifacts } from './schema.js';
5
+ import { applicableDecisions, docsDir, docsForRole, loadDecisions } from './project-docs.js';
6
+ import { readChangeSet } from './changeset.js';
7
+ import { loadWiki } from './wiki.js';
8
+ import { wiringForChange } from './wiring.js';
9
+ import { exists, toPosix } from './paths.js';
10
+ const RESULT_FORMAT = `Ответ заканчивается блоком:
11
+ \`\`\`yaml
12
+ # sbox-result
13
+ status: готово | утверждение | заблокировано
14
+ blocker: { category: артефакт | тесты | реализация | внешний | пользователь | нет, artifact: <id>, message: <текст> }
15
+ \`\`\`
16
+ Дополнительные поля по роли: complexity и size (planner на propose), questions (planner на plan), findings (reviewer), checks и gaps (verifier), dispositions и delivery_narrative (reviewer в фазе review), protected (tester), verified (implementer, verifier).`;
17
+ const OBJECTIVES = {
18
+ 'researcher:research': 'Собрать Evidence Pack по запросу: текущее поведение, затронутые capability и код, границы, доказательства, пробелы. Записать в evidence/research.md.',
19
+ 'planner:propose': 'Написать proposal.md по запросу и evidence. Оценить размер изменения, сложность реализации и ревью.',
20
+ 'planner:plan': 'Написать дельты спецификаций, design.md с общей картиной, контрактами, решениями и приоритизированными вопросами, затем tasks.md.',
21
+ 'tester:cover': 'Написать автотесты по сценариям дельт на уровнях из testing.md, заполнить coverage.yaml и при необходимости test-plan.md, запустить тесты и убедиться, что новые падают по ожидаемой причине.',
22
+ 'reviewer:tests_review': 'Проверить тесты против дельт спецификаций и дизайна: полнота покрытия сценариев, соответствие сценарию, отсутствие продуктовой логики в тестах, именование. Вернуть findings.',
23
+ 'implementer:implement': 'Выполнить задачи из tasks.md, не изменяя защищённые файлы. Довести тесты из coverage.yaml до зелёных, отметить выполненные задачи.',
24
+ 'verifier:verify': 'Независимо проверить запечатанный change-set: прогнать тесты из coverage.yaml и проверки из testing.md, сопоставить каждый сценарий и решение дизайна с кодом. Вернуть checks с результатами PASS, FAIL, PARTIAL, NOT_RUN и gaps.',
25
+ 'reviewer:review': 'Проверить семантическую дельту запечатанного change-set против спецификаций, дизайна, задач и правил проекта, распорядиться каждым не-PASS пунктом верификатора и при вердикте «готово» написать delivery_narrative.',
26
+ };
27
+ const WRITE_OWNERSHIP = {
28
+ researcher: ['evidence/research.md'],
29
+ planner: ['proposal.md', 'specs/**', 'design.md', 'tasks.md'],
30
+ challenger: ['evidence/challenge.md'],
31
+ tester: ['coverage.yaml', 'test-plan.md', '<тестовые файлы по testing.md>'],
32
+ implementer: ['<продуктовый код>', 'tasks.md (только отметки [x])'],
33
+ reviewer: [],
34
+ verifier: [],
35
+ distiller: ['.sbox/wiki/**'],
36
+ };
37
+ export function buildPacket(ctx) {
38
+ const { root, config, change, dir, role, phase } = ctx;
39
+ const workflow = loadWorkflow(root);
40
+ const states = artifactStates(workflow, change, dir);
41
+ const artifacts = {};
42
+ for (const s of states)
43
+ artifacts[s.id] = s.existing.map((f) => toPosix(path.relative(root, f)));
44
+ const docs = docsForRole(role)
45
+ .map((c) => ({ category: c.id, title: c.title, file: toPosix(path.relative(root, path.join(docsDir(root, config), c.file))) }))
46
+ .filter((d) => exists(path.join(root, d.file)));
47
+ const decisions = applicableDecisions(loadDecisions(root, config), null).map((d) => ({ id: d.id, title: d.title, rule: d.rule, file: d.file }));
48
+ const instructions = [];
49
+ if (role === 'planner' || role === 'tester') {
50
+ const wanted = phase === 'propose' ? states.filter((s) => s.id === 'proposal') : pendingArtifacts(states, phase);
51
+ for (const s of wanted) {
52
+ const extra = s.id === 'specs' ? ctx.adapter.instructions() : undefined;
53
+ const ins = artifactInstructions(root, config, workflow, change, dir, s.id, extra);
54
+ if (ins)
55
+ instructions.push(ins);
56
+ }
57
+ }
58
+ const evidenceDir = path.join(dir, 'evidence');
59
+ const evidence = exists(evidenceDir) ? fs.readdirSync(evidenceDir).sort().map((f) => toPosix(path.relative(root, path.join(evidenceDir, f)))) : [];
60
+ const runId = `r${change.runs.length + 1}`;
61
+ const rel = (p) => toPosix(path.relative(root, p));
62
+ const constraints = [
63
+ 'Не вызывать других агентов и не расширять объём задачи.',
64
+ 'Не менять истину спецификаций: только дельты в specs/ изменения.',
65
+ 'Соблюдать соглашения проекта из conventions.md и правила проекта из списка rules.',
66
+ ];
67
+ if (role === 'implementer' && change.protected.length > 0) {
68
+ constraints.push(`Защищённые файлы, менять запрещено: ${change.protected.join(', ')}`);
69
+ }
70
+ if ((role === 'verifier' || role === 'reviewer') && change.changeset) {
71
+ constraints.push('Не менять файлы рабочей копии: любое изменение после запечатывания change-set отклоняет отчёт.');
72
+ }
73
+ if (change.size === 'small')
74
+ constraints.push('Размер small: поведение продукта не меняется, спецификации не трогаются.');
75
+ const wiki = loadWiki(root, config).map((w) => ({ file: w.file, summary: w.summary, read_when: w.read_when, source_roots: w.source_roots }));
76
+ if (wiki.length > 0)
77
+ constraints.push('Перед решениями прочитай страницы wiki из files.wiki, чьи read_when подходят к задаче; единообразие с описанными там образцами обязательно.');
78
+ const sealed = (phase === 'verify' || phase === 'review') ? readChangeSet(dir) : null;
79
+ const wiring = phase === 'verify' || phase === 'review' ? wiringForChange(root, dir) : null;
80
+ if (wiring && wiring.gaps.length > 0)
81
+ constraints.push(`Подключение по образцу: образец «${wiring.analog}» зарегистрирован в ${wiring.registrationFiles.length} файлах, нового модуля нет в ${wiring.gaps.length} из них (files.wiring). Для каждого такого файла реши: нужна регистрация (FAIL) или файл к задаче не относится (пункт с обоснованием).`);
82
+ return {
83
+ version: 1,
84
+ change: { id: change.id, title: change.title, dir: rel(dir), phase, size: change.size, status: change.status, base_revision: change.base_revision },
85
+ role,
86
+ phase,
87
+ runId,
88
+ objective: OBJECTIVES[`${role}:${phase}`] ?? `Выполнить роль ${role} в фазе ${phase}.`,
89
+ ownership: { write: WRITE_OWNERSHIP[role], readOnly: WRITE_OWNERSHIP[role].length === 0 },
90
+ files: {
91
+ request: rel(path.join(dir, 'request.md')),
92
+ log: rel(path.join(dir, 'log.md')),
93
+ artifacts,
94
+ evidence,
95
+ docs,
96
+ wiki,
97
+ specsTruth: ctx.truthSources,
98
+ },
99
+ constraints,
100
+ rules: decisions,
101
+ authority: WRITE_OWNERSHIP[role].length === 0 ? 'Только чтение и запуск проверок.' : `Запись только в: ${WRITE_OWNERSHIP[role].join(', ')}.`,
102
+ verification: verificationFor(role),
103
+ resultFormat: RESULT_FORMAT,
104
+ resultFile: rel(path.join(dir, 'runs', runId, 'result.md')),
105
+ stop: 'Остановись и верни статус «заблокировано», если нужен ответ человека, если план невыполним или если тест противоречит спецификации.',
106
+ feedback: feedbackFor(change, phase),
107
+ instructions,
108
+ specAdapterInstructions: role === 'planner' && phase === 'plan' && !change.skip_specs ? ctx.adapter.instructions() : null,
109
+ changeset: sealed ? { base: sealed.base, digest: sealed.digest, files: sealed.files.map((f) => ({ path: f.path, status: f.status })) } : null,
110
+ verificationReport: role === 'reviewer' && phase === 'review' ? change.verification : null,
111
+ wiring,
112
+ context: config.context ?? null,
113
+ commands: {
114
+ status: `sbox status --change ${change.id} --json`,
115
+ instructions: `sbox instructions <artifact> --change ${change.id} --json`,
116
+ validate: `sbox validate --change ${change.id} --json`,
117
+ specList: 'sbox spec list --json',
118
+ specShow: 'sbox spec show <capability-id> --json',
119
+ changeset: `sbox changeset show --change ${change.id} --json`,
120
+ report: `sbox report --change ${change.id} --role ${role} --file ${rel(path.join(dir, 'runs', runId, 'result.md'))}`,
121
+ },
122
+ rolePrompt: loadRoleText(root, role),
123
+ };
124
+ }
125
+ function verificationFor(role) {
126
+ switch (role) {
127
+ case 'planner':
128
+ return 'После записи артефактов выполнить `sbox validate --json` и добиться отсутствия ошибок.';
129
+ case 'tester':
130
+ return 'Запустить написанные тесты командами из testing.md: новые падают по ожидаемой причине, регрессионные проходят.';
131
+ case 'implementer':
132
+ return 'Прогнать тесты из coverage.yaml и проверки из testing.md; все задачи tasks.md отмечены.';
133
+ case 'verifier':
134
+ return 'Запустить полные проверки из testing.md и приложить результаты в checks с evidence.';
135
+ default:
136
+ return 'Проверить, что отчёт опирается на прочитанные файлы, с указанием путей.';
137
+ }
138
+ }
139
+ function feedbackFor(change, phase) {
140
+ const parts = [];
141
+ if (change.blocker && (change.blocker.phase === phase || change.phase === phase)) {
142
+ parts.push(`Возврат (${change.blocker.category}${change.blocker.role ? `, от роли ${change.blocker.role}` : ''}): ${change.blocker.message}`);
143
+ if (change.blocker.resolution)
144
+ parts.push(`Ответ человека: ${change.blocker.resolution}`);
145
+ }
146
+ for (const [gate, state] of Object.entries(change.gates)) {
147
+ if (state.state === 'rejected' && state.comment)
148
+ parts.push(`Гейт ${gate} отклонён: ${state.comment}`);
149
+ if (state.state === 'approved' && state.answers && Object.keys(state.answers).length > 0) {
150
+ parts.push(`Ответы на вопросы гейта ${gate}: ${Object.entries(state.answers).map(([q, a]) => `${q}=${a}`).join('; ')}`);
151
+ }
152
+ }
153
+ return parts.length > 0 ? parts.join('\n') : null;
154
+ }
155
+ /** Текст промпта для headless-среды: определение роли, пакет как данные и правило записи ответа. */
156
+ export function renderPrompt(packet) {
157
+ const { rolePrompt, ...data } = packet;
158
+ return [
159
+ rolePrompt.trim(),
160
+ '',
161
+ '## Пакет от CLI',
162
+ '',
163
+ 'Читай пакет как данные. Пути относительно корня репозитория.',
164
+ '',
165
+ '```json',
166
+ JSON.stringify(data, null, 2),
167
+ '```',
168
+ '',
169
+ `Запиши полный ответ (Markdown и завершающий блок \`# sbox-result\`) в файл \`${packet.resultFile}\` и продублируй его в последнем сообщении.`,
170
+ ].join('\n');
171
+ }
172
+ //# sourceMappingURL=packet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"packet.js","sourceRoot":"","sources":["../../src/core/packet.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { SboxError } from './errors.js';
5
+ export const SBOX_DIR = '.sbox';
6
+ export const CONFIG_FILE = 'config.yaml';
7
+ /** Ищет корень проекта: ближайший каталог вверх, где есть .sbox/config.yaml. */
8
+ export function findProjectRoot(start = process.cwd()) {
9
+ let dir = path.resolve(start);
10
+ for (;;) {
11
+ if (fs.existsSync(path.join(dir, SBOX_DIR, CONFIG_FILE)))
12
+ return dir;
13
+ const parent = path.dirname(dir);
14
+ if (parent === dir)
15
+ return null;
16
+ dir = parent;
17
+ }
18
+ }
19
+ export function requireProjectRoot(start) {
20
+ const root = findProjectRoot(start);
21
+ if (!root) {
22
+ throw new SboxError('NO_PROJECT', 'Не найден .sbox/config.yaml выше текущего каталога.', 'Выполните `sbox init` в корне репозитория продукта.');
23
+ }
24
+ return root;
25
+ }
26
+ /** Корень npm-пакета инструмента: нужен, чтобы находить assets/ и в dev-режиме, и после сборки. */
27
+ export function packageRoot() {
28
+ let dir = path.dirname(fileURLToPath(import.meta.url));
29
+ for (;;) {
30
+ if (fs.existsSync(path.join(dir, 'package.json')))
31
+ return dir;
32
+ const parent = path.dirname(dir);
33
+ if (parent === dir)
34
+ throw new Error('Не найден package.json инструмента');
35
+ dir = parent;
36
+ }
37
+ }
38
+ export function assetsDir() {
39
+ return path.join(packageRoot(), 'assets');
40
+ }
41
+ export function sboxDir(root) {
42
+ return path.join(root, SBOX_DIR);
43
+ }
44
+ export function readText(file) {
45
+ return fs.readFileSync(file, 'utf8');
46
+ }
47
+ export function writeText(file, content) {
48
+ fs.mkdirSync(path.dirname(file), { recursive: true });
49
+ fs.writeFileSync(file, content, 'utf8');
50
+ }
51
+ export function exists(file) {
52
+ return fs.existsSync(file);
53
+ }
54
+ export function toPosix(p) {
55
+ return p.split(path.sep).join('/');
56
+ }
57
+ /** Локальная дата YYYY-MM-DD (не UTC), чтобы имена архивов совпадали с календарём разработчика. */
58
+ export function today() {
59
+ const d = new Date();
60
+ const pad = (n) => String(n).padStart(2, '0');
61
+ return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
62
+ }
63
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/core/paths.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,203 @@
1
+ import { enabledGates } from './config.js';
2
+ import { SboxError } from './errors.js';
3
+ export const ROLES = ['researcher', 'planner', 'challenger', 'tester', 'implementer', 'reviewer', 'verifier', 'distiller'];
4
+ /** Порядок фаз (docs/design.md, раздел 4). intake выполняет CLI при создании изменения. */
5
+ export const PHASE_ORDER = [
6
+ 'intake',
7
+ 'research',
8
+ 'propose',
9
+ 'plan',
10
+ 'cover',
11
+ 'tests_review',
12
+ 'implement',
13
+ 'verify',
14
+ 'review',
15
+ 'deliver',
16
+ 'archived',
17
+ ];
18
+ export const ROLE_BY_PHASE = {
19
+ research: 'researcher',
20
+ propose: 'planner',
21
+ plan: 'planner',
22
+ cover: 'tester',
23
+ tests_review: 'reviewer',
24
+ implement: 'implementer',
25
+ review: 'reviewer',
26
+ verify: 'verifier',
27
+ };
28
+ /** Гейт стоит после фазы: пройдена фаза, ждём человека, потом следующая фаза. */
29
+ export const GATE_AFTER = {
30
+ propose: 'proposal',
31
+ plan: 'plan',
32
+ tests_review: 'tests',
33
+ };
34
+ export const PHASE_OF_GATE = {
35
+ proposal: 'propose',
36
+ plan: 'plan',
37
+ tests: 'tests_review',
38
+ };
39
+ /** Куда возвращает блокер каждой категории. */
40
+ export const RETURN_PHASE = {
41
+ артефакт: 'plan',
42
+ тесты: 'cover',
43
+ реализация: 'implement',
44
+ внешний: null,
45
+ пользователь: null,
46
+ };
47
+ export function nextPhase(phase) {
48
+ const i = PHASE_ORDER.indexOf(phase);
49
+ return i >= 0 && i + 1 < PHASE_ORDER.length ? PHASE_ORDER[i + 1] : null;
50
+ }
51
+ export function isRoleName(value) {
52
+ return ROLES.includes(value);
53
+ }
54
+ export function gateEnabled(change, config, gate) {
55
+ return enabledGates(config, change.autonomy).includes(gate);
56
+ }
57
+ /** Что делать дальше по состоянию изменения. Чистая функция, ничего не меняет. */
58
+ export function nextStep(change, config) {
59
+ switch (change.status) {
60
+ case 'waiting_approval': {
61
+ const pending = (Object.entries(change.gates).find(([, g]) => g.state === 'pending') ?? [])[0];
62
+ if (pending)
63
+ return { kind: 'gate', gate: pending, phase: PHASE_OF_GATE[pending] };
64
+ break;
65
+ }
66
+ case 'waiting_user':
67
+ case 'waiting_peer':
68
+ case 'parked':
69
+ case 'blocked':
70
+ case 'stopped':
71
+ case 'delivery_unknown':
72
+ case 'failed':
73
+ return { kind: 'wait', status: change.status, blocker: change.blocker };
74
+ case 'archived':
75
+ case 'done':
76
+ return { kind: 'done' };
77
+ case 'active':
78
+ break;
79
+ }
80
+ const phase = change.phase === 'intake' ? 'research' : change.phase;
81
+ if (phase === 'deliver')
82
+ return { kind: 'deliver' };
83
+ if (phase === 'archived')
84
+ return { kind: 'done' };
85
+ const role = ROLE_BY_PHASE[phase];
86
+ if (!role)
87
+ throw new SboxError('BAD_PHASE', `Для фазы ${phase} не задана роль`);
88
+ return { kind: 'role', role, phase };
89
+ }
90
+ /** Фаза завершена успешно: либо гейт, либо следующая фаза. Мутирует change. */
91
+ export function completePhase(change, config, phase) {
92
+ change.blocker = null;
93
+ // Доработка тестов по замечанию человека с гейта: ИИ-ревью уже было, идём сразу на гейт.
94
+ if (phase === 'cover' && !config.testing.reviewReworks && change.gates.tests?.state === 'rejected') {
95
+ change.phase = 'tests_review';
96
+ completePhase(change, config, 'tests_review');
97
+ return;
98
+ }
99
+ const gate = GATE_AFTER[phase];
100
+ if (gate) {
101
+ const state = change.gates[gate];
102
+ if (gateEnabled(change, config, gate) && state?.state !== 'approved') {
103
+ change.gates[gate] = { state: 'pending', ...(state?.rejected_times ? { rejected_times: state.rejected_times } : {}) };
104
+ change.status = 'waiting_approval';
105
+ change.phase = phase;
106
+ return;
107
+ }
108
+ if (!gateEnabled(change, config, gate) && state?.state !== 'approved') {
109
+ change.gates[gate] = { state: 'skipped', reason: 'autonomy' };
110
+ }
111
+ }
112
+ advance(change, phase);
113
+ }
114
+ export function advance(change, from) {
115
+ const next = nextPhase(from);
116
+ if (!next) {
117
+ change.status = 'done';
118
+ return;
119
+ }
120
+ change.phase = next;
121
+ change.status = next === 'archived' ? 'archived' : 'active';
122
+ }
123
+ export function approveGate(change, gate, by, comment, answers) {
124
+ const state = change.gates[gate];
125
+ if (!state || state.state !== 'pending') {
126
+ throw new SboxError('GATE_NOT_PENDING', `Гейт ${gate} не ожидает решения (состояние: ${state?.state ?? 'нет'}).`);
127
+ }
128
+ change.gates[gate] = { state: 'approved', by, at: new Date().toISOString(), ...(comment ? { comment } : {}), ...(answers ? { answers } : {}), ...(state.rejected_times ? { rejected_times: state.rejected_times } : {}) };
129
+ change.status = 'active';
130
+ advance(change, PHASE_OF_GATE[gate]);
131
+ }
132
+ export function rejectGate(change, gate, by, comment) {
133
+ const state = change.gates[gate];
134
+ if (!state || state.state !== 'pending') {
135
+ throw new SboxError('GATE_NOT_PENDING', `Гейт ${gate} не ожидает решения (состояние: ${state?.state ?? 'нет'}).`);
136
+ }
137
+ change.gates[gate] = { state: 'rejected', by, at: new Date().toISOString(), comment, rejected_times: (state.rejected_times ?? 0) + 1 };
138
+ change.status = 'active';
139
+ // Возврат на фазу, которая производит утверждаемый артефакт.
140
+ change.phase = gate === 'tests' ? 'cover' : PHASE_OF_GATE[gate];
141
+ change.blocker = { category: 'пользователь', message: comment, phase: change.phase };
142
+ }
143
+ /** Маршрутизация блокера. Возвращает фазу возврата или null, если запуск останавливается. */
144
+ export function routeBlocker(change, config, input) {
145
+ change.blocker = { category: input.category, message: input.message, role: input.role, phase: input.phase, ...(input.artifact ? { artifact: input.artifact } : {}) };
146
+ const target = RETURN_PHASE[input.category] ?? null;
147
+ if (input.category === 'пользователь') {
148
+ change.status = 'waiting_user';
149
+ change.interventions.user_blockers += 1;
150
+ return null;
151
+ }
152
+ if (!target) {
153
+ change.status = 'blocked';
154
+ return null;
155
+ }
156
+ const limit = change.returns_limit ?? config.limits.returnsPerPhase;
157
+ const count = (change.returns[target] ?? 0) + 1;
158
+ change.returns[target] = count;
159
+ if (count > limit) {
160
+ change.status = 'parked';
161
+ change.blocker.message = `Лимит возвратов в фазу ${target} (${limit}) исчерпан. Последний блокер: ${input.message}`;
162
+ return null;
163
+ }
164
+ change.phase = target;
165
+ change.status = 'active';
166
+ return target;
167
+ }
168
+ export const RESUMABLE_STATUSES = new Set(['parked', 'blocked', 'failed', 'waiting_user', 'stopped', 'delivery_unknown']);
169
+ /**
170
+ * Явное продолжение человеком (docs/design.md, «Возвраты»): снимает parked/blocked,
171
+ * при необходимости поднимает бюджет возвратов и записывает ответ на блокер как фидбэк.
172
+ */
173
+ export function resumeChange(change, config, opts = {}) {
174
+ if (!RESUMABLE_STATUSES.has(change.status)) {
175
+ throw new SboxError('NOT_RESUMABLE', `Статус ${change.status} не требует продолжения.`);
176
+ }
177
+ const category = change.blocker?.category;
178
+ const target = category ? (RETURN_PHASE[category] ?? null) : null;
179
+ if (change.status === 'parked' && target) {
180
+ const used = change.returns[target] ?? 0;
181
+ const current = change.returns_limit ?? config.limits.returnsPerPhase;
182
+ if (opts.returns === undefined) {
183
+ throw new SboxError('RETURNS_REQUIRED', `Бюджет возвратов в фазу ${target} исчерпан (${used} из ${current}).`, 'Укажите --returns N с числом больше использованного.');
184
+ }
185
+ if (opts.returns <= used)
186
+ throw new SboxError('RETURNS_TOO_SMALL', `--returns ${opts.returns} не больше использованных ${used}.`);
187
+ change.returns_limit = opts.returns;
188
+ change.phase = target;
189
+ }
190
+ else if (opts.returns !== undefined) {
191
+ change.returns_limit = opts.returns;
192
+ }
193
+ if (change.status === 'delivery_unknown') {
194
+ change.phase = 'deliver';
195
+ }
196
+ if (change.blocker && opts.comment)
197
+ change.blocker.resolution = opts.comment;
198
+ change.interventions.resumes += 1;
199
+ change.stop_requested = false;
200
+ change.status = 'active';
201
+ return change.phase;
202
+ }
203
+ //# sourceMappingURL=phases.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phases.js","sourceRoot":"","sources":["../../src/core/phases.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ import path from 'node:path';
2
+ import { exists, readText } from './paths.js';
3
+ /**
4
+ * Текст пул-реквеста собирается только из delivery_narrative ревьюера, отчёта верификатора,
5
+ * сводки дельт и чеклиста готовности (docs/design.md, «Правила ревьюера»).
6
+ */
7
+ export function renderPullRequestBody(input) {
8
+ const { change, narrative, deltas, dod } = input;
9
+ const lines = [];
10
+ lines.push(`## ${narrative.title}`, '');
11
+ lines.push(narrative.delta, '');
12
+ lines.push('### Почему это работает', '', narrative.why, '');
13
+ if (narrative.preserved)
14
+ lines.push('### Что сохранено', '', narrative.preserved, '');
15
+ if (narrative.rollout || narrative.rollback) {
16
+ lines.push('### Выкатка и откат', '');
17
+ if (narrative.rollout)
18
+ lines.push(`- Выкатка: ${narrative.rollout}`);
19
+ if (narrative.rollback)
20
+ lines.push(`- Откат: ${narrative.rollback}`);
21
+ lines.push('');
22
+ }
23
+ if (deltas.length > 0) {
24
+ lines.push('### Спецификации', '');
25
+ for (const d of deltas) {
26
+ const ops = d.ops.map((op) => (op.op === 'add-requirement' ? `+ ${op.requirement.title}` : op.op === 'modify-requirement' ? `~ ${op.requirement.title}` : op.op === 'remove-requirement' ? `- ${op.requirementTitle}` : `→ ${op.from} ⇒ ${op.to}`));
27
+ lines.push(`- \`${d.capabilityId}\`${d.isNew ? ' (новая)' : ''}: ${ops.join('; ')}`);
28
+ }
29
+ lines.push('');
30
+ }
31
+ if (change.verification) {
32
+ const v = change.verification;
33
+ const counts = { PASS: 0, FAIL: 0, PARTIAL: 0, NOT_RUN: 0 };
34
+ for (const c of v.checks)
35
+ counts[c.result] += 1;
36
+ lines.push('### Верификация', '', `Проверок: ${v.checks.length} (PASS ${counts.PASS}, PARTIAL ${counts.PARTIAL}, NOT_RUN ${counts.NOT_RUN}).`);
37
+ for (const c of v.checks)
38
+ lines.push(`- ${c.id} ${c.result}${c.purpose ? ` — ${c.purpose}` : ''}${c.evidence ? `: ${c.evidence}` : ''}`);
39
+ if (change.accepted_gaps.length > 0) {
40
+ lines.push('', 'Принятые пробелы (ручная или CI-проверка):');
41
+ for (const g of change.accepted_gaps) {
42
+ const gap = v.gaps.find((x) => x.id === g.item);
43
+ lines.push(`- ${g.item}${gap?.environment ? ` (${gap.environment})` : ''}${g.reason ? `: ${g.reason}` : ''}`);
44
+ }
45
+ }
46
+ lines.push('');
47
+ }
48
+ if (change.changeset?.rebound.length) {
49
+ lines.push('### После ревью', '', 'Изменялись только файлы вне кода, вердикты verify и review сохранены:');
50
+ for (const r of change.changeset.rebound)
51
+ lines.push(`- ${r.files.join(', ')} (${r.at.slice(0, 16).replace('T', ' ')})`);
52
+ lines.push('');
53
+ }
54
+ const testPlan = path.join(input.dir, 'test-plan.md');
55
+ if (exists(testPlan)) {
56
+ lines.push('### План ручного тестирования', '', readText(testPlan).trim(), '');
57
+ }
58
+ lines.push('### Готовность к влитию', '');
59
+ for (const item of dod)
60
+ lines.push(`- [${item.ok ? 'x' : ' '}] ${item.id}. ${item.text}${item.detail ? ` (${item.detail})` : ''}`);
61
+ lines.push('', `<sub>sbox: изменение \`${change.id}\`, change-set \`${change.changeset?.digest ?? '—'}\`</sub>`);
62
+ return lines.join('\n');
63
+ }
64
+ //# sourceMappingURL=pr-body.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pr-body.js","sourceRoot":"","sources":["../../src/core/pr-body.ts"],"names":[],"mappings":"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"}