atlas-workflow 0.9.0 → 0.9.2
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 +2 -2
- package/VERSION +1 -1
- package/build/cli/atlas-init.mjs +12 -14
- package/build/tests/classify-findings.test.mjs +20 -0
- package/build/tests/etapa3.test.mjs +161 -0
- package/build/tests/test_classify_findings.py +79 -0
- package/hosts/opencode/.opencode/agents/atlas-findings-repair.md +4 -0
- package/hosts/opencode/.opencode/agents/atlas-task-validator.md +18 -1
- package/hosts/opencode/.opencode/atlas/VERSION +1 -1
- package/hosts/opencode/.opencode/atlas/orchestrator/README.md +7 -5
- package/hosts/opencode/.opencode/atlas/orchestrator/commands/workflow.md +1 -1
- package/hosts/opencode/.opencode/atlas/orchestrator/skills/atlas-workflow-orchestrator/SKILL.md +22 -17
- package/hosts/opencode/.opencode/atlas/packages/mcp-server/README.md +1 -1
- package/hosts/opencode/.opencode/atlas/packages/mcp-server/package.json +1 -1
- package/hosts/opencode/.opencode/atlas/packages/mcp-server/server.js +446 -14
- package/hosts/opencode/.opencode/atlas/packages/templates/BACKLOG_MESTRE_TEMPLATE.md +14 -3
- package/hosts/opencode/.opencode/atlas/packages/templates/PRD_TEMPLATE.md +2 -1
- package/hosts/opencode/.opencode/atlas/packages/templates/STATE_FILE_SCHEMA.md +25 -1
- package/hosts/opencode/.opencode/skills/_shared/references/stack-profiles.md +36 -0
- package/hosts/opencode/.opencode/skills/_shared/scripts/document_quality.mjs +252 -0
- package/hosts/opencode/.opencode/skills/atlas-backlog-generator/SKILL.md +7 -2
- package/hosts/opencode/.opencode/skills/atlas-direct-execute/SKILL.md +6 -2
- package/hosts/opencode/.opencode/skills/atlas-findings-repair/SKILL.md +11 -1
- package/hosts/opencode/.opencode/skills/atlas-plan-execute/SKILL.md +16 -2
- package/hosts/opencode/.opencode/skills/atlas-plan-handoff/SKILL.md +6 -4
- package/hosts/opencode/.opencode/skills/atlas-prd-interview/SKILL.md +7 -2
- package/hosts/opencode/.opencode/skills/atlas-slice-review/SKILL.md +37 -2
- package/hosts/opencode/.opencode/skills/atlas-slice-review/references/scenario-lenses.md +8 -0
- package/hosts/opencode/.opencode/skills/atlas-slice-review/scripts/classify_findings.mjs +60 -0
- package/hosts/opencode/.opencode/skills/atlas-slice-review/scripts/classify_findings.py +9 -41
- package/hosts/opencode/.opencode/skills/atlas-sprint-prd-generator/SKILL.md +7 -4
- package/hosts/opencode/.opencode/skills/atlas-task-validator/SKILL.md +29 -14
- package/hosts/opencode/.opencode/skills/atlas-workflow-orchestrator/SKILL.md +22 -17
- package/hosts/pi/.pi/agents/atlas-direct-execute.md +6 -2
- package/hosts/pi/.pi/agents/atlas-findings-repair.md +15 -1
- package/hosts/pi/.pi/agents/atlas-plan-execute.md +16 -2
- package/hosts/pi/.pi/agents/atlas-slice-review.md +37 -2
- package/hosts/pi/.pi/agents/atlas-task-validator.md +18 -1
- package/hosts/pi/atlas/VERSION +1 -1
- package/hosts/pi/atlas/orchestrator/README.md +7 -5
- package/hosts/pi/atlas/orchestrator/commands/workflow.md +1 -1
- package/hosts/pi/atlas/orchestrator/skills/atlas-workflow-orchestrator/SKILL.md +22 -17
- package/hosts/pi/atlas/packages/mcp-server/README.md +1 -1
- package/hosts/pi/atlas/packages/mcp-server/package.json +1 -1
- package/hosts/pi/atlas/packages/mcp-server/server.js +446 -14
- package/hosts/pi/atlas/packages/templates/BACKLOG_MESTRE_TEMPLATE.md +14 -3
- package/hosts/pi/atlas/packages/templates/PRD_TEMPLATE.md +2 -1
- package/hosts/pi/atlas/packages/templates/STATE_FILE_SCHEMA.md +25 -1
- package/hosts/pi/skills/_shared/references/stack-profiles.md +36 -0
- package/hosts/pi/skills/_shared/scripts/document_quality.mjs +252 -0
- package/hosts/pi/skills/atlas-backlog-generator/SKILL.md +7 -2
- package/hosts/pi/skills/atlas-direct-execute/SKILL.md +6 -2
- package/hosts/pi/skills/atlas-findings-repair/SKILL.md +11 -1
- package/hosts/pi/skills/atlas-plan-execute/SKILL.md +16 -2
- package/hosts/pi/skills/atlas-plan-handoff/SKILL.md +6 -4
- package/hosts/pi/skills/atlas-prd-interview/SKILL.md +7 -2
- package/hosts/pi/skills/atlas-slice-review/SKILL.md +37 -2
- package/hosts/pi/skills/atlas-slice-review/references/scenario-lenses.md +8 -0
- package/hosts/pi/skills/atlas-slice-review/scripts/classify_findings.mjs +60 -0
- package/hosts/pi/skills/atlas-slice-review/scripts/classify_findings.py +9 -41
- package/hosts/pi/skills/atlas-sprint-prd-generator/SKILL.md +7 -4
- package/hosts/pi/skills/atlas-task-validator/SKILL.md +29 -14
- package/hosts/pi/skills/atlas-workflow-orchestrator/SKILL.md +22 -17
- package/package.json +1 -1
- package/plugins/atlas-workflow-orchestrator/.codex/agents/atlas-findings-repair.toml +1 -1
- package/plugins/atlas-workflow-orchestrator/.codex/agents/atlas-task-validator.toml +1 -1
- package/plugins/atlas-workflow-orchestrator/.codex-plugin/plugin.json +1 -1
- package/plugins/atlas-workflow-orchestrator/VERSION +1 -1
- package/plugins/atlas-workflow-orchestrator/agents/atlas-findings-repair.md +4 -0
- package/plugins/atlas-workflow-orchestrator/agents/atlas-task-validator.md +18 -1
- package/plugins/atlas-workflow-orchestrator/orchestrator/README.md +7 -5
- package/plugins/atlas-workflow-orchestrator/orchestrator/commands/workflow.md +1 -1
- package/plugins/atlas-workflow-orchestrator/orchestrator/skills/atlas-workflow-orchestrator/SKILL.md +22 -17
- package/plugins/atlas-workflow-orchestrator/packages/mcp-server/README.md +1 -1
- package/plugins/atlas-workflow-orchestrator/packages/mcp-server/package.json +1 -1
- package/plugins/atlas-workflow-orchestrator/packages/mcp-server/server.js +446 -14
- package/plugins/atlas-workflow-orchestrator/packages/skills/_shared/references/stack-profiles.md +36 -0
- package/plugins/atlas-workflow-orchestrator/packages/skills/_shared/scripts/document_quality.mjs +252 -0
- package/plugins/atlas-workflow-orchestrator/packages/skills/atlas-backlog-generator/SKILL.md +7 -2
- package/plugins/atlas-workflow-orchestrator/packages/skills/atlas-direct-execute/SKILL.md +6 -2
- package/plugins/atlas-workflow-orchestrator/packages/skills/atlas-findings-repair/SKILL.md +11 -1
- package/plugins/atlas-workflow-orchestrator/packages/skills/atlas-plan-execute/SKILL.md +16 -2
- package/plugins/atlas-workflow-orchestrator/packages/skills/atlas-plan-handoff/SKILL.md +6 -4
- package/plugins/atlas-workflow-orchestrator/packages/skills/atlas-prd-interview/SKILL.md +7 -2
- package/plugins/atlas-workflow-orchestrator/packages/skills/atlas-slice-review/SKILL.md +37 -2
- package/plugins/atlas-workflow-orchestrator/packages/skills/atlas-slice-review/references/scenario-lenses.md +8 -0
- package/plugins/atlas-workflow-orchestrator/packages/skills/atlas-slice-review/scripts/classify_findings.mjs +60 -0
- package/plugins/atlas-workflow-orchestrator/packages/skills/atlas-slice-review/scripts/classify_findings.py +9 -41
- package/plugins/atlas-workflow-orchestrator/packages/skills/atlas-sprint-prd-generator/SKILL.md +7 -4
- package/plugins/atlas-workflow-orchestrator/packages/skills/atlas-task-validator/SKILL.md +29 -14
- package/plugins/atlas-workflow-orchestrator/packages/templates/BACKLOG_MESTRE_TEMPLATE.md +14 -3
- package/plugins/atlas-workflow-orchestrator/packages/templates/PRD_TEMPLATE.md +2 -1
- package/plugins/atlas-workflow-orchestrator/packages/templates/STATE_FILE_SCHEMA.md +25 -1
- package/plugins/atlas-workflow-orchestrator/skills/_shared/references/stack-profiles.md +36 -0
- package/plugins/atlas-workflow-orchestrator/skills/_shared/scripts/document_quality.mjs +252 -0
- package/plugins/atlas-workflow-orchestrator/skills/atlas-backlog-generator/SKILL.md +7 -2
- package/plugins/atlas-workflow-orchestrator/skills/atlas-direct-execute/SKILL.md +6 -2
- package/plugins/atlas-workflow-orchestrator/skills/atlas-findings-repair/SKILL.md +11 -1
- package/plugins/atlas-workflow-orchestrator/skills/atlas-plan-execute/SKILL.md +16 -2
- package/plugins/atlas-workflow-orchestrator/skills/atlas-plan-handoff/SKILL.md +6 -4
- package/plugins/atlas-workflow-orchestrator/skills/atlas-prd-interview/SKILL.md +7 -2
- package/plugins/atlas-workflow-orchestrator/skills/atlas-slice-review/SKILL.md +37 -2
- package/plugins/atlas-workflow-orchestrator/skills/atlas-slice-review/references/scenario-lenses.md +8 -0
- package/plugins/atlas-workflow-orchestrator/skills/atlas-slice-review/scripts/classify_findings.mjs +60 -0
- package/plugins/atlas-workflow-orchestrator/skills/atlas-slice-review/scripts/classify_findings.py +9 -41
- package/plugins/atlas-workflow-orchestrator/skills/atlas-sprint-prd-generator/SKILL.md +7 -4
- package/plugins/atlas-workflow-orchestrator/skills/atlas-task-validator/SKILL.md +29 -14
- package/plugins/atlas-workflow-orchestrator/skills/atlas-workflow-orchestrator/SKILL.md +22 -17
- package/plugins/atlas-workflow-orchestrator/templates/BACKLOG_MESTRE_TEMPLATE.md +14 -3
- package/plugins/atlas-workflow-orchestrator/templates/PRD_TEMPLATE.md +2 -1
- package/plugins/atlas-workflow-orchestrator/templates/STATE_FILE_SCHEMA.md +25 -1
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Atlas Workflow
|
|
2
2
|
|
|
3
|
-
Plugin **Atlas Workflow Orchestrator** v0.9.
|
|
3
|
+
Plugin **Atlas Workflow Orchestrator** v0.9.1 — pipeline determinístico (PRD → plano → execução → validação) com skills `atlas-*`, templates e MCP. Um pacote, seis hosts: **Claude Code**, **Cursor**, **Codex App**, **Antigravity (Gemini)**, **OpenCode** e **Pi CLI**.
|
|
4
4
|
|
|
5
|
-
**Versão:** [`VERSION`](VERSION) (`0.9.
|
|
5
|
+
**Versão:** [`VERSION`](VERSION) (`0.9.1`) · **Repo:** https://github.com/pauloborini/atlas-workflow
|
|
6
6
|
|
|
7
7
|
## Hosts
|
|
8
8
|
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.9.
|
|
1
|
+
0.9.2
|
package/build/cli/atlas-init.mjs
CHANGED
|
@@ -435,29 +435,27 @@ function installAntigravity(opts) {
|
|
|
435
435
|
log(` [dry-run] mesclaria mcpServers.atlas-workflow em ${mcpFile} (args absoluto)`);
|
|
436
436
|
} else {
|
|
437
437
|
fs.mkdirSync(pluginDir, { recursive: true });
|
|
438
|
-
|
|
438
|
+
|
|
439
|
+
// Fonte: bundle shipado `plugins/atlas-workflow-orchestrator/`. A cópia raiz
|
|
440
|
+
// `/packages/` NÃO entra no tarball npm (ver .npmignore) — usá-la quebra o
|
|
441
|
+
// install via npx-from-GitHub (ENOENT). O bundle já traz skills/ completo
|
|
442
|
+
// (inclui a skill atlas-workflow-orchestrator) + packages/mcp-server.
|
|
443
|
+
const SRC = path.join(ROOT, 'plugins/atlas-workflow-orchestrator');
|
|
444
|
+
|
|
439
445
|
// Limpeza de instalações anteriores controladas por nós
|
|
440
446
|
const skillsDir = path.join(pluginDir, 'skills');
|
|
441
447
|
const packagesDir = path.join(pluginDir, 'packages');
|
|
442
448
|
rmPath(skillsDir, opts);
|
|
443
449
|
rmPath(packagesDir, opts);
|
|
444
450
|
|
|
445
|
-
// Copia as skills
|
|
446
|
-
fs.
|
|
447
|
-
fs.cpSync(path.join(ROOT, 'packages/skills'), skillsDir, { recursive: true });
|
|
448
|
-
|
|
449
|
-
// Copia a orquestradora
|
|
450
|
-
fs.cpSync(
|
|
451
|
-
path.join(ROOT, 'packages/orchestrator/skills/atlas-workflow-orchestrator'),
|
|
452
|
-
path.join(skillsDir, 'atlas-workflow-orchestrator'),
|
|
453
|
-
{ recursive: true }
|
|
454
|
-
);
|
|
451
|
+
// Copia as skills (inclui a orquestradora atlas-workflow-orchestrator)
|
|
452
|
+
fs.cpSync(path.join(SRC, 'skills'), skillsDir, { recursive: true });
|
|
455
453
|
|
|
456
454
|
// Copia o mcp-server
|
|
457
455
|
fs.mkdirSync(path.join(packagesDir, 'mcp-server'), { recursive: true });
|
|
458
|
-
fs.cpSync(path.join(
|
|
459
|
-
|
|
460
|
-
// Remove testes do mcp-server no bundle
|
|
456
|
+
fs.cpSync(path.join(SRC, 'packages/mcp-server'), path.join(packagesDir, 'mcp-server'), { recursive: true });
|
|
457
|
+
|
|
458
|
+
// Remove testes do mcp-server no bundle (defensivo; bundle shipado já não os traz)
|
|
461
459
|
fs.rmSync(path.join(packagesDir, 'mcp-server', 'server.test.js'), { force: true });
|
|
462
460
|
|
|
463
461
|
// Cria o plugin.json
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import { classifyFindings } from '../../packages/skills/atlas-slice-review/scripts/classify_findings.mjs';
|
|
4
|
+
|
|
5
|
+
const valid = () => ({
|
|
6
|
+
severity: 'P1', task_id: 'T01', title: 'Finding', file: 'a.js', line: 1,
|
|
7
|
+
failure_mode: 'falha', evidence: 'evidência', recommendation: 'corrigir', fix_validation: 'testar',
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test('classificador aceita finding completo e array vazio', () => {
|
|
11
|
+
assert.equal(classifyFindings([valid()])[0].recommendation, 'corrigir');
|
|
12
|
+
assert.deepEqual(classifyFindings([]), []);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('classificador rejeita severidade, linha e campos inválidos', () => {
|
|
16
|
+
assert.throws(() => classifyFindings([{ ...valid(), severity: 'high' }]), /invalid severity/);
|
|
17
|
+
assert.throws(() => classifyFindings([{ ...valid(), line: 0 }]), /invalid line/);
|
|
18
|
+
const missing = valid(); delete missing.recommendation;
|
|
19
|
+
assert.throws(() => classifyFindings([missing]), /recommendation/);
|
|
20
|
+
});
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { execFileSync } from 'node:child_process';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import test from 'node:test';
|
|
7
|
+
import {
|
|
8
|
+
closedDecisionIds,
|
|
9
|
+
detectStackProfiles,
|
|
10
|
+
pendingInterviewQuestions,
|
|
11
|
+
persistInterviewRound,
|
|
12
|
+
resolveSprintAuthority,
|
|
13
|
+
validateBacklogUpdate,
|
|
14
|
+
} from '../../packages/skills/_shared/scripts/document_quality.mjs';
|
|
15
|
+
|
|
16
|
+
const ROOT = path.resolve(import.meta.dirname, '../..');
|
|
17
|
+
const CLASSIFIER = path.join(ROOT, 'packages/skills/atlas-slice-review/scripts/classify_findings.mjs');
|
|
18
|
+
|
|
19
|
+
const finding = {
|
|
20
|
+
severity: 'P1', task_id: 'T01', title: 'Falha', file: 'src/a.js', line: 3,
|
|
21
|
+
failure_mode: 'Falha alcançável.', evidence: 'Guard ausente.',
|
|
22
|
+
recommendation: 'Restabelecer guard.', fix_validation: 'Teste negativo.',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
test('review: gate canônico executa diretamente com Node, sem Python no PATH', () => {
|
|
26
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'atlas-review-'));
|
|
27
|
+
const input = path.join(dir, 'findings.json');
|
|
28
|
+
fs.writeFileSync(input, JSON.stringify([finding]));
|
|
29
|
+
const output = execFileSync(process.execPath, [CLASSIFIER, input], { env: { ...process.env, PATH: dir }, encoding: 'utf8' });
|
|
30
|
+
assert.equal(JSON.parse(output)[0].title, 'Falha');
|
|
31
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test('perfis: Flutter, Node e Python ativam só regras aplicáveis', () => {
|
|
35
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'atlas-stack-'));
|
|
36
|
+
const fixture = (name, files, commands = []) => {
|
|
37
|
+
const dir = path.join(root, name); fs.mkdirSync(dir);
|
|
38
|
+
for (const [file, content] of Object.entries(files)) fs.writeFileSync(path.join(dir, file), content);
|
|
39
|
+
return detectStackProfiles(dir, commands);
|
|
40
|
+
};
|
|
41
|
+
assert.deepEqual(fixture('node', { 'package.json': '{"scripts":{"test":"node --test"}}' }).boundaries[0], {
|
|
42
|
+
boundary: '.', universal: true, flutter_dart: false, node_typescript: true, python: false, getx: false,
|
|
43
|
+
});
|
|
44
|
+
assert.deepEqual(fixture('flutter', { 'pubspec.yaml': 'name: fixture\ndependencies:\n flutter:\n sdk: flutter\n' }).boundaries[0], {
|
|
45
|
+
boundary: '.', universal: true, flutter_dart: true, node_typescript: false, python: false, getx: false,
|
|
46
|
+
});
|
|
47
|
+
assert.deepEqual(fixture('python', { 'pyproject.toml': '[project]\nname="fixture"\n' }).boundaries[0], {
|
|
48
|
+
boundary: '.', universal: true, flutter_dart: false, node_typescript: false, python: true, getx: false,
|
|
49
|
+
});
|
|
50
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('perfis: monorepo restringe stack por boundary e GetX exige evidência', () => {
|
|
54
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'atlas-monorepo-'));
|
|
55
|
+
fs.mkdirSync(path.join(root, 'packages/node'), { recursive: true });
|
|
56
|
+
fs.mkdirSync(path.join(root, 'apps/flutter'), { recursive: true });
|
|
57
|
+
fs.mkdirSync(path.join(root, 'apps/getx'), { recursive: true });
|
|
58
|
+
fs.writeFileSync(path.join(root, 'packages/node/package.json'), '{}');
|
|
59
|
+
fs.writeFileSync(path.join(root, 'apps/flutter/pubspec.yaml'), 'name: plain\ndependencies:\n flutter:\n sdk: flutter\n');
|
|
60
|
+
fs.writeFileSync(path.join(root, 'apps/getx/pubspec.yaml'), 'name: getx\ndependencies:\n flutter:\n sdk: flutter\n get: ^4.7.0\n');
|
|
61
|
+
const profiles = detectStackProfiles(root, [], ['packages/node', 'apps/flutter', 'apps/getx']);
|
|
62
|
+
assert.deepEqual(profiles.boundaries.map(({ boundary, node_typescript, flutter_dart, getx }) => (
|
|
63
|
+
{ boundary, node_typescript, flutter_dart, getx }
|
|
64
|
+
)), [
|
|
65
|
+
{ boundary: 'packages/node', node_typescript: true, flutter_dart: false, getx: false },
|
|
66
|
+
{ boundary: 'apps/flutter', node_typescript: false, flutter_dart: true, getx: false },
|
|
67
|
+
{ boundary: 'apps/getx', node_typescript: false, flutter_dart: true, getx: true },
|
|
68
|
+
]);
|
|
69
|
+
assert.throws(() => detectStackProfiles(root, [], ['../outside']), /BOUNDARY_OUTSIDE_PROJECT/);
|
|
70
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
function backlog(rows, decisions = '| D1 | Contrato fechado | S02 | Produto | decidido |', changelog = '- 2026-06-22 — baseline.') {
|
|
74
|
+
return `# Backlog\n\n### Decisões bloqueantes\n\n| ID | Decisão | Bloqueia | Dono | Status |\n|---|---|---|---|---|\n${decisions}\n\n## 7. Registro de sprints\n\n| ID | Sprint | Fase-fonte | Objetivo (1 linha) | MoSCoW | Ganho | Esforço | Prioridade | PRD | Depende de | Estado | Gate |\n|---|---|---|---|---|---|---|---|---|---|---|---|\n${rows.join('\n')}\n${changelog ? `\n## Registro de alterações\n\n${changelog}` : ''}\n`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const done = '| S01 | Base | F0 | Fechar base | Must | Alto | Baixo | P0 | `PRD_S01_base.md` | — | done | ✅ |';
|
|
78
|
+
const todo = '| S02 | Próxima | F1 | Entregar próxima | Must | Alto | Médio | P0 | `PRD_S02_proxima.md` | S01 | backlog | — |';
|
|
79
|
+
|
|
80
|
+
test('backlog update: preserva sprint done, decisão fechada e itens não relacionados', () => {
|
|
81
|
+
const before = backlog([done, todo]);
|
|
82
|
+
const after = backlog(
|
|
83
|
+
[done, todo, '| S03 | Extra | F2 | Entregar extra | Should | Médio | Baixo | P1 | `PRD_S03_extra.md` | S01 | backlog | — |'],
|
|
84
|
+
undefined,
|
|
85
|
+
'- 2026-06-22 — baseline.\n- 2026-06-22 — S03 adicionada.',
|
|
86
|
+
);
|
|
87
|
+
assert.deepEqual(validateBacklogUpdate(before, after), { valid: true, errors: [] });
|
|
88
|
+
const destructive = backlog([done.replace('Base', 'Base reescrita'), todo]);
|
|
89
|
+
assert.ok(validateBacklogUpdate(before, destructive).errors.includes('DONE_SPRINT_CHANGED:S01'));
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('backlog update: bloqueia dependência cíclica', () => {
|
|
93
|
+
const before = backlog([done, todo]);
|
|
94
|
+
const cyclic = backlog([
|
|
95
|
+
'| S01 | Base | F0 | Fechar base | Must | Alto | Baixo | P0 | `PRD_S01_base.md` | S02 | backlog | — |',
|
|
96
|
+
'| S02 | Próxima | F1 | Entregar próxima | Must | Alto | Médio | P0 | `PRD_S02_proxima.md` | S01 | backlog | — |',
|
|
97
|
+
]);
|
|
98
|
+
assert.ok(validateBacklogUpdate(before, cyclic).errors.some((error) => error.startsWith('DEPENDENCY_CYCLE:')));
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test('backlog update: bloqueia dependência inexistente, mudança não autorizada e histórico reescrito', () => {
|
|
102
|
+
const before = backlog([done, todo]);
|
|
103
|
+
const missing = backlog(
|
|
104
|
+
[done, todo.replace('S01 | backlog', 'S99 | backlog')], undefined,
|
|
105
|
+
'- 2026-06-22 — baseline.\n- 2026-06-22 — dependência alterada.',
|
|
106
|
+
);
|
|
107
|
+
assert.ok(validateBacklogUpdate(before, missing, { authorizedIds: ['S02'] }).errors.includes('DEPENDENCY_NOT_FOUND:S02:S99'));
|
|
108
|
+
const unauthorized = backlog(
|
|
109
|
+
[done, todo.replace('Próxima', 'Reescrita')], undefined,
|
|
110
|
+
'- 2026-06-22 — baseline.\n- 2026-06-22 — S02 alterada.',
|
|
111
|
+
);
|
|
112
|
+
assert.ok(validateBacklogUpdate(before, unauthorized).errors.includes('UNAUTHORIZED_SPRINT_CHANGED:S02'));
|
|
113
|
+
const rewritten = backlog(
|
|
114
|
+
[done, todo, '| S03 | Extra | F2 | Entregar extra | Should | Médio | Baixo | P1 | p | S01 | backlog | — |'],
|
|
115
|
+
undefined,
|
|
116
|
+
'- 2026-06-22 — histórico substituído.',
|
|
117
|
+
);
|
|
118
|
+
assert.ok(validateBacklogUpdate(before, rewritten).errors.includes('CHANGELOG_REWRITTEN'));
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test('Sprint PRD: múltiplos backlogs conflitantes bloqueiam autoridade', () => {
|
|
122
|
+
assert.throws(() => resolveSprintAuthority({
|
|
123
|
+
sprintId: 'S03', candidates: [
|
|
124
|
+
{ path: '/repo/a/BACKLOG_MESTRE.md', sprints: ['S03'] },
|
|
125
|
+
{ path: '/repo/b/BACKLOG_MESTRE.md', sprints: ['S03'] },
|
|
126
|
+
],
|
|
127
|
+
}), /AMBIGUOUS_BACKLOG_AUTHORITY/);
|
|
128
|
+
assert.equal(resolveSprintAuthority({
|
|
129
|
+
sprintId: 'S03', explicitPath: '/repo/b/BACKLOG_MESTRE.md', candidates: [
|
|
130
|
+
{ path: '/repo/a/BACKLOG_MESTRE.md', sprints: ['S03'] },
|
|
131
|
+
{ path: '/repo/b/BACKLOG_MESTRE.md', sprints: ['S03'] },
|
|
132
|
+
],
|
|
133
|
+
}).path, path.resolve('/repo/b/BACKLOG_MESTRE.md'));
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test('interview: persiste resposta e não repete decisão fechada', () => {
|
|
137
|
+
const prd = '## 3. Decisões de produto (fechadas)\n\n| ID | Decisão |\n|---|---|\n| D1 | Escolha anterior |\n\n## 4. Fluxos\n';
|
|
138
|
+
const questions = [{ decision_id: 'D1' }, { decision_id: 'D2' }];
|
|
139
|
+
assert.deepEqual(pendingInterviewQuestions(prd, questions), [{ decision_id: 'D2' }]);
|
|
140
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'atlas-interview-'));
|
|
141
|
+
const prdPath = path.join(dir, 'PRD.md');
|
|
142
|
+
fs.writeFileSync(prdPath, prd);
|
|
143
|
+
const updated = persistInterviewRound(prdPath, [{ decision_id: 'D2', value: 'Nova escolha' }], '2026-06-22');
|
|
144
|
+
assert.equal(fs.readFileSync(prdPath, 'utf8'), updated);
|
|
145
|
+
assert.deepEqual([...closedDecisionIds(updated)].sort(), ['D1', 'D2']);
|
|
146
|
+
assert.deepEqual(pendingInterviewQuestions(updated, questions), []);
|
|
147
|
+
assert.match(updated, /entrevista: D2 persistida/);
|
|
148
|
+
const moduleUrl = new URL('../../packages/skills/_shared/scripts/document_quality.mjs', import.meta.url).href;
|
|
149
|
+
const freshProcess = execFileSync(process.execPath, ['--input-type=module', '-e', `
|
|
150
|
+
import fs from 'node:fs';
|
|
151
|
+
import { pendingInterviewQuestions } from ${JSON.stringify(moduleUrl)};
|
|
152
|
+
process.stdout.write(JSON.stringify(pendingInterviewQuestions(fs.readFileSync(process.argv[1], 'utf8'), [{ decision_id: 'D2' }])));
|
|
153
|
+
`, prdPath], { encoding: 'utf8' });
|
|
154
|
+
assert.deepEqual(JSON.parse(freshProcess), []);
|
|
155
|
+
const invalidPath = path.join(dir, 'INVALID.md');
|
|
156
|
+
fs.writeFileSync(invalidPath, '# PRD sem tabela de decisões\n');
|
|
157
|
+
assert.throws(() => persistInterviewRound(invalidPath, [{ decision_id: 'D3', value: 'x' }]), /DECISION_NOT_MATERIALIZED/);
|
|
158
|
+
assert.equal(fs.readFileSync(invalidPath, 'utf8'), '# PRD sem tabela de decisões\n');
|
|
159
|
+
assert.throws(() => persistInterviewRound(path.join(dir, 'missing', 'PRD.md'), [{ decision_id: 'D3', value: 'x' }]), /INTERVIEW_PERSISTENCE_FAILED/);
|
|
160
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
161
|
+
});
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Testes do gate determinístico de findings da atlas-slice-review."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
import pathlib
|
|
8
|
+
import subprocess
|
|
9
|
+
import sys
|
|
10
|
+
import tempfile
|
|
11
|
+
import unittest
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
ROOT = pathlib.Path(__file__).resolve().parents[2]
|
|
15
|
+
SCRIPT = ROOT / "packages/skills/atlas-slice-review/scripts/classify_findings.py"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def valid_finding() -> dict[str, object]:
|
|
19
|
+
return {
|
|
20
|
+
"severity": "P1",
|
|
21
|
+
"task_id": "T01",
|
|
22
|
+
"title": "Finding confirmado",
|
|
23
|
+
"file": "packages/example.py",
|
|
24
|
+
"line": 12,
|
|
25
|
+
"failure_mode": "Entrada inválida alcança estado inconsistente.",
|
|
26
|
+
"evidence": "Guard ausente na linha indicada.",
|
|
27
|
+
"recommendation": "Restabelecer o guard no proprietário do invariante.",
|
|
28
|
+
"fix_validation": "Teste negativo deve manter o estado anterior.",
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def run_gate(payload: object) -> subprocess.CompletedProcess[str]:
|
|
33
|
+
with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", suffix=".json") as handle:
|
|
34
|
+
json.dump(payload, handle)
|
|
35
|
+
handle.flush()
|
|
36
|
+
return subprocess.run(
|
|
37
|
+
[sys.executable, str(SCRIPT), handle.name],
|
|
38
|
+
check=False,
|
|
39
|
+
capture_output=True,
|
|
40
|
+
text=True,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class ClassifyFindingsTest(unittest.TestCase):
|
|
45
|
+
def test_accepts_complete_finding(self) -> None:
|
|
46
|
+
result = run_gate([valid_finding()])
|
|
47
|
+
self.assertEqual(result.returncode, 0, result.stderr)
|
|
48
|
+
normalized = json.loads(result.stdout)
|
|
49
|
+
self.assertEqual(normalized[0]["recommendation"], valid_finding()["recommendation"])
|
|
50
|
+
|
|
51
|
+
def test_accepts_empty_findings(self) -> None:
|
|
52
|
+
result = run_gate([])
|
|
53
|
+
self.assertEqual(result.returncode, 0, result.stderr)
|
|
54
|
+
self.assertEqual(json.loads(result.stdout), [])
|
|
55
|
+
|
|
56
|
+
def test_rejects_missing_recommendation(self) -> None:
|
|
57
|
+
finding = valid_finding()
|
|
58
|
+
del finding["recommendation"]
|
|
59
|
+
result = run_gate([finding])
|
|
60
|
+
self.assertNotEqual(result.returncode, 0)
|
|
61
|
+
self.assertIn("recommendation", result.stderr)
|
|
62
|
+
|
|
63
|
+
def test_rejects_invalid_severity(self) -> None:
|
|
64
|
+
finding = valid_finding()
|
|
65
|
+
finding["severity"] = "high"
|
|
66
|
+
result = run_gate([finding])
|
|
67
|
+
self.assertNotEqual(result.returncode, 0)
|
|
68
|
+
self.assertIn("invalid severity", result.stderr)
|
|
69
|
+
|
|
70
|
+
def test_rejects_invalid_line(self) -> None:
|
|
71
|
+
finding = valid_finding()
|
|
72
|
+
finding["line"] = 0
|
|
73
|
+
result = run_gate([finding])
|
|
74
|
+
self.assertNotEqual(result.returncode, 0)
|
|
75
|
+
self.assertIn("invalid line", result.stderr)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
if __name__ == "__main__":
|
|
79
|
+
unittest.main()
|
|
@@ -32,4 +32,8 @@ O orquestrador passa obrigatoriamente `state_path`, findings estruturados, `vali
|
|
|
32
32
|
- Não replanejar
|
|
33
33
|
- Não ampliar escopo
|
|
34
34
|
- Atualizar o `state_path` original em lugar; não trocar o boundary para outro arquivo
|
|
35
|
+
- Consumir IDs/recommendations estruturadas; persistir correlação em `repair_evidence`
|
|
36
|
+
- Preservar `worktree_baseline`, recapturar `worktree_final` e incluir exatamente todo arquivo tocado em `files_changed`; recomputar `head_sha` e `diff_stat`
|
|
37
|
+
- Aceitar somente IDs recebidos; cada arquivo tocado deve estar atribuído a um finding recebido, sem IDs/arquivos extras ou duplicados
|
|
38
|
+
- Devolver `repairs[]` com `finding_id`, arquivos, checks e status
|
|
35
39
|
- Ao terminar, devolver `repair_complete` ou `blocked`
|
|
@@ -27,9 +27,14 @@ Leia o JSON em `.atlas/state/<run_id>/<slice>.json` usando o schema em `packages
|
|
|
27
27
|
2. **Plan path** — `plan_path`, depois leia Section 2 (Invariantes de execução), Section 6 (Contratos técnicos) e Section 8 (Validação e checklist).
|
|
28
28
|
3. **Executed task ids** — `tasks`.
|
|
29
29
|
4. **Boundary refs** — `boundary_refs`.
|
|
30
|
+
5. **Deterministic boundary** — `base_sha`, `head_sha`, `contract_kind` e arrays de evidence/probes.
|
|
31
|
+
6. **Working-tree delta** — confronte `worktree_baseline`, `worktree_final` e árvore atual; dirty preexistente intacto fica fora, mutação posterior entra.
|
|
32
|
+
7. **Repair correlation** — no attempt 2, correlacione findings por ID com `repair_evidence` no mesmo state path.
|
|
30
33
|
|
|
31
34
|
Não aceite contrato inline, diff colado ou listas de tasks coladas como boundary de validação. Se `state_path` estiver ausente, ilegível, ou faltar qualquer campo obrigatório, retorne JSON com `verdict: "fail"` e um finding P1 `Input insuficiente: <missing item>`.
|
|
32
35
|
|
|
36
|
+
Compatibilidade: state legado mínimo sem `contract_kind` só é aceito para `atlas-plan-execute`. `atlas-direct-execute` exige extensão completa e `obligations` não vazio. Compare `base_sha...head_sha`, `HEAD` atual e arquivos evidenciados no working tree com `files_changed`; nunca infira base pelo nome da branch. Divergência gera boundary violation + P1.
|
|
37
|
+
|
|
33
38
|
## State persistence
|
|
34
39
|
|
|
35
40
|
Use `atlas_run_state` como fonte primária de metadados da run e estado de gate. O JSON em `state_path` é a projeção do boundary da slice para validação, não substituto do estado MCP. Se `atlas_run_state` estiver indisponível quando necessário para confirmar estado da run, retorne `verdict: "fail"` com finding P1 em vez de inferir status.
|
|
@@ -88,7 +93,17 @@ Retorne JSON estrito como output final. Não envolva em Markdown e não anteceda
|
|
|
88
93
|
"challenge_response": "string (sha256 hex do challenge.file; null se sem challenge)",
|
|
89
94
|
"verdict": "pass | fail | pass_with_observations",
|
|
90
95
|
"findings": [
|
|
91
|
-
{
|
|
96
|
+
{
|
|
97
|
+
"id": "F-001",
|
|
98
|
+
"severity": "P0|P1|P2|P3",
|
|
99
|
+
"file": "string",
|
|
100
|
+
"line": 1,
|
|
101
|
+
"failure_mode": "string",
|
|
102
|
+
"evidence": "string",
|
|
103
|
+
"recommendation": "string",
|
|
104
|
+
"fix_validation": "string",
|
|
105
|
+
"msg": "string (deprecated; derivado por uma release)"
|
|
106
|
+
}
|
|
92
107
|
],
|
|
93
108
|
"observations": [
|
|
94
109
|
{ "file": "string", "line": 0, "msg": "string" }
|
|
@@ -101,6 +116,8 @@ Retorne JSON estrito como output final. Não envolva em Markdown e não anteceda
|
|
|
101
116
|
|
|
102
117
|
`dispatch_token` deve ser exatamente `validator_recovery.expected_dispatch_token`. `findings`, `observations` e `boundary_violations` são sempre arrays. Use arrays vazios quando não houver itens.
|
|
103
118
|
|
|
119
|
+
IDs são únicos, obrigatórios no formato `F-NNN` e estáveis nos dois ciclos; severity é estritamente `P0|P1|P2|P3`. No segundo, devolva `repaired_finding_ids` e confirme que cada ID alvo possui `repair_evidence` com arquivos, checks e `status: resolved`. O MCP rejeita shape incompleto e `pass`/`pass_with_observations` com P0/P1.
|
|
120
|
+
|
|
104
121
|
## Severity Model
|
|
105
122
|
|
|
106
123
|
Escala alinhada com `atlas-slice-review` (`P0/P1/P2/P3`).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.9.
|
|
1
|
+
0.9.2
|
|
@@ -117,7 +117,7 @@ Status:
|
|
|
117
117
|
↓
|
|
118
118
|
2. Validate PRD + Interview (condicional)
|
|
119
119
|
↓
|
|
120
|
-
3. Execute
|
|
120
|
+
3. Execute (`atlas-direct-execute`, mantendo `phase: plan_execute`)
|
|
121
121
|
↓
|
|
122
122
|
4. Review (se --review)
|
|
123
123
|
↓
|
|
@@ -127,9 +127,11 @@ Status:
|
|
|
127
127
|
### Interview-Only Mode
|
|
128
128
|
|
|
129
129
|
```
|
|
130
|
-
1.
|
|
130
|
+
1. Cria draft mínimo pelo `PRD_TEMPLATE.md` quando a entrada é brainstorm
|
|
131
131
|
↓
|
|
132
|
-
2.
|
|
132
|
+
2. Entrevista `atlas-prd-interview` com `prd_path` válido
|
|
133
|
+
↓
|
|
134
|
+
3. Output (PRD esboço + decisões)
|
|
133
135
|
```
|
|
134
136
|
|
|
135
137
|
## Sequências canônicas
|
|
@@ -140,7 +142,7 @@ Atlas é família única. Cliente (Claude Code, Cursor, Codex App) é apenas o h
|
|
|
140
142
|
|------|-----------|
|
|
141
143
|
| `full` | `atlas-sprint-prd-generator` → `atlas-prd-interview` quando necessário → `atlas-plan-handoff` → `atlas-plan-execute` → `atlas-task-validator` → `atlas-findings-repair` (no `fail`) → `atlas-slice-review` somente com `--review` |
|
|
142
144
|
| `direct` | PRD/spec existente → `atlas-direct-execute` → `atlas-task-validator` → `atlas-findings-repair` (no `fail`) → `atlas-slice-review` somente com `--review` |
|
|
143
|
-
| `interview-only` | `atlas-prd-interview` |
|
|
145
|
+
| `interview-only` | draft PRD mínimo (se brainstorm) → `atlas-prd-interview` |
|
|
144
146
|
|
|
145
147
|
## Validação automática
|
|
146
148
|
|
|
@@ -220,7 +222,7 @@ Veja este README, `packages/mcp-server/README.md` e os SKILL.md `atlas-*` para o
|
|
|
220
222
|
|
|
221
223
|
---
|
|
222
224
|
|
|
223
|
-
**Plugin version:** 0.9.
|
|
225
|
+
**Plugin version:** 0.9.1
|
|
224
226
|
**Author:** Paulo Borini
|
|
225
227
|
**Last updated:** 2026-06-16
|
|
226
228
|
|
|
@@ -34,4 +34,4 @@ Exemplos:
|
|
|
34
34
|
|
|
35
35
|
Não improvise comportamento fora do `SKILL.md`. **Pipeline é fire-and-continue**: uma vez iniciado, avança fase a fase sem pedir permissão entre gates; só para em gate duro `blocked` ou blockage de ambiente real (ver "Princípio de continuação automática"). Nunca invente "Modo Discussão" ou peça "quer que eu gere/continue?". Decisão em aberto não para — dispara entrevista, propaga e segue. Em caso de erro real, siga "Error handling".
|
|
36
36
|
|
|
37
|
-
**Gates duros (v0.3):** o pipeline é orientado a artefato e MCP. Antes de iniciar, rode a **Fase 0 (Pré-flight)** com `atlas_ping` e `atlas_preflight`; use ids `atlas-*`; garanta que cada sub-agent carregue o `SKILL.md` real antes de agir. Se MCP não responder, resultado exigido estiver ausente ou status for bloqueante, **aborte; nunca use fallback narrativo**. Respeite os Gates G1–G11 + TC da SKILL: `atlas_verify_artifact` antes de avançar (G1); em `full`, nenhum código antes de `PLAN_*.md` validado (G2); skills invocadas de verdade (G3); validador frio como sub-agent separado (G4); `atlas_scan_prd` determinístico e logado (G5); status verificado contra disco e MCP (G6);
|
|
37
|
+
**Gates duros (v0.3):** o pipeline é orientado a artefato e MCP. Antes de iniciar, rode a **Fase 0 (Pré-flight)** com `atlas_ping` e `atlas_preflight`; use ids `atlas-*`; garanta que cada sub-agent carregue o `SKILL.md` real antes de agir. Se MCP não responder, resultado exigido estiver ausente ou status for bloqueante, **aborte; nunca use fallback narrativo**. Respeite os Gates G1–G11 + TC da SKILL: `atlas_verify_artifact` antes de avançar (G1); em `full`, nenhum código antes de `PLAN_*.md` validado (G2); skills invocadas de verdade (G3); validador frio como sub-agent separado (G4); `atlas_scan_prd` determinístico e logado (G5); status verificado contra disco e MCP (G6); execução/review como sub-agents reais com `atlas_lock_dispatch`, enquanto PRD/entrevista/plano são autoria documental no pai (G7/G8); orquestrador de mãos atadas e dispatch blocking (G9); família única atlas-* via `atlas_preflight` (G10); em `full`, `atlas_assert_after_plan` exige `atlas-plan-execute` após plano (G11); `direct` usa `atlas-direct-execute`; ambos mantêm `phase: plan_execute`; PRD/PLAN exigem `atlas_verify_template_conformance` passed com `pending_count: 0` (TC). Em `interview-only brainstorm`, crie draft mínimo pelo template antes de invocar `atlas-prd-interview` com `prd_path` válido.
|
package/hosts/opencode/.opencode/atlas/orchestrator/skills/atlas-workflow-orchestrator/SKILL.md
CHANGED
|
@@ -21,7 +21,7 @@ Orquestra pipelines de desenvolvimento de features no projeto Atlas, automatizan
|
|
|
21
21
|
Três modos **canônicos de execução** — `full`, `direct`, `execute` (PRD §5 D1) — mais o modo `interview-only`, que permanece **separado** (entrevista sem execução; PRD D2, não é colapsado em `full`).
|
|
22
22
|
|
|
23
23
|
- **`full`** — pipeline completo: PRD → validação → entrevista (se necessário) → **plano (artefato obrigatório)** → executor → review (opcional)
|
|
24
|
-
- **`direct`** — pipeline enxuto: PRD → validação → entrevista (se necessário) →
|
|
24
|
+
- **`direct`** — pipeline enxuto: PRD → validação → entrevista (se necessário) → `atlas-direct-execute` → review (opcional). **Não produz plano de handoff** — a diferença real para `full` é exatamente essa.
|
|
25
25
|
- **`execute`** — recebe um **`PLAN_*.md` pronto** e o executa **sem gerar plano** (PRD D1). Entrada = caminho de plano; reverifica o artefato + conformidade de template e despacha `plan_execute` direto. Não regera nem replaneja: ajustes de plano pedem `full`. `atlas_assert_after_plan` (gate pós-plano do `full`) **não se aplica** em `execute` — o plano já é o input; o equivalente é a reverificação na entrada (PRD D13). **Não há alias `plan`**: usar `plan` como modo é ambíguo com planejamento documental e deve ser rejeitado como modo inválido.
|
|
26
26
|
- **`interview-only`** — entrevista direta (ex: brainstorm, resolução de decisões). Entrevista **sem execução**: não usa `guarantee_level` no fluxo (não há execução de código a garantir). Permanece modo separado (PRD D2).
|
|
27
27
|
|
|
@@ -51,7 +51,7 @@ Três modos **canônicos de execução** — `full`, `direct`, `execute` (PRD §
|
|
|
51
51
|
→ Gera PRD de indicação, força entrevista, plano, executor
|
|
52
52
|
|
|
53
53
|
/workflow interview-only brainstorm "que tal dark mode?"
|
|
54
|
-
→
|
|
54
|
+
→ Cria draft mínimo pelo template canônico, valida o path e entrevista esse PRD; sem execução
|
|
55
55
|
|
|
56
56
|
/workflow execute plan "/path/to/PLAN_S05_login.md"
|
|
57
57
|
→ Reverifica o plano (artifact + TC), executa direto via plan_execute + validador frio. Não gera plano.
|
|
@@ -102,7 +102,7 @@ Executar **antes** de iniciar o pipeline. Se qualquer item falhar, **parar e rep
|
|
|
102
102
|
Ação: instalar/ativar o plugin ou corrigir o pacote atlas-* disponível no host
|
|
103
103
|
```
|
|
104
104
|
**PROIBIDO o fallback "implementação direta" / "contratos equivalentes inline".** Não existe caminho onde o orquestrador faz plano ou código no próprio fio. Emulação inline e fallback direto são a falha-raiz que esta skill proíbe — se não há sub-agent, **para**. (Gate G7.)
|
|
105
|
-
8. **Rejeitar conflito de modo:** se o pedido tiver `full`/`direct` junto com "sem patch", "sem editar código", "planejamento apenas", "handoff only" ou equivalente, **pare antes de gerar artefatos**. `full
|
|
105
|
+
8. **Rejeitar conflito de modo:** se o pedido tiver `full`/`direct` junto com "sem patch", "sem editar código", "planejamento apenas", "handoff only" ou equivalente, **pare antes de gerar artefatos**. `full` executa `atlas-plan-execute`; `direct` executa `atlas-direct-execute`; não existe interpretação plan-only implícita.
|
|
106
106
|
9. **Declarar o plano de execução** (1 bloco curto): `run_id`, modo, **ids exatos de cada sub-agent**, sequência de fases, artefatos esperados e tools MCP que sustentarão cada gate. Só então iniciar a Fase 1.
|
|
107
107
|
|
|
108
108
|
---
|
|
@@ -120,7 +120,7 @@ O pipeline é **fire-and-continue**: uma vez iniciado, o orquestrador avança fa
|
|
|
120
120
|
|
|
121
121
|
**Após entrevista**: reexecuta os gates afetados (`atlas_verify_artifact`/`atlas_scan_prd`/TC) e **retoma o pipeline (plano→execução) automaticamente**, sem nova confirmação.
|
|
122
122
|
|
|
123
|
-
A única interação legítima com o usuário é **dentro de uma fase** — o `
|
|
123
|
+
A única interação legítima com o usuário é **dentro de uma fase** — o mecanismo estruturado `question_prompt` devolvido por `atlas_capabilities`, usado pela entrevista para resolver ambiguidade de produto. Resolver ambiguidade ≠ pedir permissão pra avançar. Terminada a fase, respostas são persistidas no PRD, gates são reexecutados e o pipeline segue sozinho.
|
|
124
124
|
|
|
125
125
|
## Papel do orquestrador (fronteira de determinismo pela mutação de código)
|
|
126
126
|
|
|
@@ -129,7 +129,7 @@ O orquestrador **coordena a execução**, não implementa código — maestro qu
|
|
|
129
129
|
- **ANTES do plano validado — autoria documental livre no fio principal.** Pode autorar PRD, entrevistar e escrever `PLAN_*.md` direto; fases documentais não exigem sub-agent (documento não muta o produto). **Ao finalizar um PRD inline, estampar `| Status | Aprovado para implementação |`** — é o `required_status` do gate TC; sem isso o PRD sai `Draft` e trava o TC em rodadas de correção.
|
|
130
130
|
- **DEPOIS do plano validado (`atlas_verify_artifact` + TC `passed`) — mãos atadas fortes.** Não edita mais PRD/plano/código nem roda comando mutante; só coordena (despachar sub-agent, ler artefato pra verificar gate, ecoar banner, montar output).
|
|
131
131
|
|
|
132
|
-
Execução de código é **sempre** sub-agent `
|
|
132
|
+
Execução de código é **sempre** sub-agent executor do modo (`atlas-plan-execute` em `full`/`execute`; `atlas-direct-execute` em `direct`), mantendo `phase: plan_execute`, + validador frio `task_validator` (Gate G9/G7). Dispatch blocking: despacha → espera retorno → verifica gate → próxima fase. Nunca dois sub-agents simultâneos.
|
|
133
133
|
|
|
134
134
|
### Verbo de dispatch é host-agnóstico (não assuma "Agent tool")
|
|
135
135
|
|
|
@@ -180,9 +180,9 @@ Regras inegociáveis. Violação = parar, não contornar.
|
|
|
180
180
|
|
|
181
181
|
## Fluxo de execução
|
|
182
182
|
|
|
183
|
-
### [EXEC] — passo comum de execução + validação
|
|
183
|
+
### [EXEC] — passo comum de execução + validação
|
|
184
184
|
|
|
185
|
-
`atlas_lock_dispatch(action=start, phase=plan_execute)
|
|
185
|
+
`atlas_lock_dispatch(action=start, phase=plan_execute)` em todos os modos; despachar como sub-agent blocking o `routing.executor_skill` devolvido pelo preflight: `atlas-plan-execute` em `full`/`execute`, `atlas-direct-execute` em `direct`. O executor emite checkpoints G12; sem retorno/progresso, chamar `atlas_lock_dispatch(action=status, phase=plan_execute)` e tratar `executor_bootstrap_timeout`/`executor_progress_timeout` como `stalled`/retry — nunca como execução em andamento. O executor retorna `validator_handoff_required` com `state_path`; o MCP só abre o slot após o checkpoint `state_path_created` para esse mesmo `state_path`. Validação sempre **sibling**: `atlas_lock_validator(action=start)`, despachar **um** `task_validator`, exigir no output o `dispatch_token` do slot e fechar com `validator_run_id` + `dispatch_token`. Em `fail`: `repair_start`, despachar `atlas-findings-repair` com `{state_path, findings, validator_attempt, repair_run_id, repair_budget: 1}`, exigir atualização do mesmo `state_path`, fechar com `repair_run_id` e rodar o **2º e último** validator. `passed`/`passed_with_observations` são terminais aprovados; status diferente bloqueia review e output completed.
|
|
186
186
|
|
|
187
187
|
### Full mode
|
|
188
188
|
|
|
@@ -225,8 +225,9 @@ Entrada: um **`PLAN_*.md` pronto**. Artefatos esperados: (plano já existe) →
|
|
|
225
225
|
|
|
226
226
|
### Interview-only mode
|
|
227
227
|
|
|
228
|
-
1.
|
|
229
|
-
2.
|
|
228
|
+
1. Se a entrada já for PRD válido, usar seu path. Se for `brainstorm`, criar primeiro um draft mínimo em disco com `packages/templates/PRD_TEMPLATE.md`, preservando as 6 seções canônicas e registrando o brainstorm em contexto/objetivo.
|
|
229
|
+
2. Verificar o draft com `atlas_verify_artifact` e `atlas_verify_template_conformance(artifact_type=prd)`; path ausente/inválido bloqueia.
|
|
230
|
+
3. Invocar `prd_interview` no fio principal com `prd_path` válido; persistir respostas no mesmo artefato e reverificar.
|
|
230
231
|
|
|
231
232
|
> `interview-only` é entrevista **sem execução**: não há fase `plan_execute` nem `guarantee_level` no fluxo (nada de código a garantir). A autoria do esboço é documental e livre.
|
|
232
233
|
|
|
@@ -245,9 +246,10 @@ O scan é **determinístico** e roda **dentro do MCP** (`atlas_scan_prd`): a lis
|
|
|
245
246
|
Detalhe do caminho que a "Princípio de continuação automática" exige para decisão pendente de **qualquer fonte** (scan/entrevista/validação de plano/`PERGUNTAS_EM_ABERTO.md`/`DISCUSSAO_*.md`/backlog — a fonte não muda o tratamento):
|
|
246
247
|
|
|
247
248
|
1. **Garantir o PRD primeiro.** Em `full`/`direct`, se o PRD não existe, **gerar o PRD draft** com as decisões marcadas. A entrevista é **PRD-scoped**: roda **sobre** o PRD, nunca antes. Detectar decisão não antecipa nem pula a geração do PRD.
|
|
248
|
-
2. **Disparar `atlas-prd-interview`** sobre o PRD — resolve via `
|
|
249
|
-
3. **
|
|
250
|
-
4. **
|
|
249
|
+
2. **Disparar `atlas-prd-interview`** sobre o PRD — resolve via `atlas_capabilities.question_prompt`, sem hardcode de host.
|
|
250
|
+
3. **Persistir após cada rodada** no mesmo PRD, reindexar §3–§6 e não repetir D* fechada.
|
|
251
|
+
4. **Propagar** ao PRD/plano/DEC/registro de origem.
|
|
252
|
+
5. **Reexecutar** os gates afetados (`atlas_verify_artifact`/`atlas_scan_prd`/TC) e **continuar** automaticamente.
|
|
251
253
|
|
|
252
254
|
Marcar TBD e adiar só se o usuário pedir **explicitamente** — nunca por iniciativa do orquestrador.
|
|
253
255
|
|
|
@@ -326,13 +328,16 @@ Se `full` gerou `PLAN_*.md` mas não despachou `plan_execute`, o cabeçalho deve
|
|
|
326
328
|
|
|
327
329
|
## Skills envolvidas
|
|
328
330
|
|
|
331
|
+
`atlas-backlog-generator` aparece apenas para descoberta do catálogo: é **explicit-only** e nunca integra `full`/`direct`/`execute`/`interview-only`. A cadeia automática começa em PRD/input já fornecido.
|
|
332
|
+
|
|
329
333
|
| Skill | Entrada | Saída (artefato) |
|
|
330
334
|
|-------|---------|------------------|
|
|
331
|
-
| `atlas-backlog-generator` |
|
|
335
|
+
| `atlas-backlog-generator` (**explicit-only**) | pedido explícito de backlog | `BACKLOG_MESTRE_*.md` |
|
|
332
336
|
| `atlas-sprint-prd-generator` | sprint_id/indicação | `PRD_*.md`, decisions_found |
|
|
333
337
|
| `atlas-prd-interview` | prd_path, ambiguities | `PRD_*.md` atualizado, decisions |
|
|
334
338
|
| `atlas-plan-handoff` | prd_path | `PLAN_*.md` |
|
|
335
|
-
| `atlas-plan-execute` | plan_path (full /
|
|
339
|
+
| `atlas-plan-execute` | plan_path (`full` / `execute`) | diff de código, evidência, `state_path` |
|
|
340
|
+
| `atlas-direct-execute` | prd_path/spec/task (`direct`) | diff de código, evidência, `state_path` |
|
|
336
341
|
| `atlas-slice-review` | diff/output | review_feedback |
|
|
337
342
|
|
|
338
343
|
**Sub-agent frio (Gate G4):** `atlas-task-validator` é verificado no pré-flight pelo orquestrador e sempre roda isolado como **sub-agent irmão (sibling)**, em todos os hosts: despachado pelo orquestrador a partir do `state_path` retornado pelo executor. A topologia é sempre sibling — o executor nunca despacha o validador.
|
|
@@ -356,11 +361,11 @@ Se o MCP não responder ou reportar drift, o pacote está inválido: abortar no
|
|
|
356
361
|
```
|
|
357
362
|
orquestrador
|
|
358
363
|
├─ MCP ping + preflight → atlas_ping + atlas_preflight (G10)
|
|
359
|
-
├─ PRD →
|
|
364
|
+
├─ PRD → autoria documental no pai → atlas_verify_artifact (G1)
|
|
360
365
|
├─ scan → atlas_scan_prd (G5) + TC → entrevista se bloqueado ou --interview
|
|
361
|
-
├─ PLANO →
|
|
366
|
+
├─ PLANO → autoria documental no pai → atlas_verify_artifact + atlas_verify_template_conformance
|
|
362
367
|
├─ G11 → atlas_assert_after_plan → próxima ação obrigatória = plan_execute
|
|
363
|
-
├─ EXECUÇÃO → atlas_lock_dispatch + sub-agent
|
|
368
|
+
├─ EXECUÇÃO → atlas_lock_dispatch + sub-agent atlas-plan-execute
|
|
364
369
|
├─ VALIDAÇÃO → lock_validator + task-validator irmão
|
|
365
370
|
│ └─ fail → findings-repair (budget 1, mesmo state_path) → validator final
|
|
366
371
|
└─ REVIEW → atlas_lock_dispatch + sub-agent slice_review (se --review)
|