atlas-workflow 0.9.2 → 0.9.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 +18 -9
- package/VERSION +1 -1
- package/build/bump-version.mjs +6 -21
- package/build/cli/atlas-init.mjs +92 -5
- package/build/tests/etapa3.test.mjs +36 -6
- package/hosts/opencode/.opencode/atlas/VERSION +1 -1
- package/hosts/opencode/.opencode/atlas/orchestrator/README.md +15 -2
- package/hosts/opencode/.opencode/atlas/orchestrator/commands/workflow.md +7 -5
- package/hosts/opencode/.opencode/atlas/orchestrator/references/host-adapters.md +13 -12
- package/hosts/opencode/.opencode/atlas/orchestrator/references/subagent_dispatch.md +11 -1
- package/hosts/opencode/.opencode/atlas/orchestrator/skills/atlas-workflow-orchestrator/SKILL.md +32 -10
- package/hosts/opencode/.opencode/atlas/packages/mcp-server/README.md +2 -2
- package/hosts/opencode/.opencode/atlas/packages/mcp-server/package.json +1 -1
- package/hosts/opencode/.opencode/atlas/packages/mcp-server/server.js +149 -21
- package/hosts/opencode/.opencode/skills/_shared/references/stack-profiles.md +36 -0
- package/hosts/opencode/.opencode/skills/_shared/scripts/document_quality.mjs +37 -1
- package/hosts/opencode/.opencode/skills/atlas-audit/SKILL.md +201 -0
- package/hosts/opencode/.opencode/skills/atlas-audit/agents/openai.yaml +7 -0
- package/hosts/opencode/.opencode/skills/atlas-task-validator/SKILL.md +6 -0
- package/hosts/opencode/.opencode/skills/atlas-workflow-orchestrator/SKILL.md +32 -10
- package/hosts/pi/atlas/VERSION +1 -1
- package/hosts/pi/atlas/orchestrator/README.md +15 -2
- package/hosts/pi/atlas/orchestrator/commands/workflow.md +7 -5
- package/hosts/pi/atlas/orchestrator/references/host-adapters.md +13 -12
- package/hosts/pi/atlas/orchestrator/references/subagent_dispatch.md +11 -1
- package/hosts/pi/atlas/orchestrator/skills/atlas-workflow-orchestrator/SKILL.md +32 -10
- package/hosts/pi/atlas/packages/mcp-server/README.md +2 -2
- package/hosts/pi/atlas/packages/mcp-server/package.json +1 -1
- package/hosts/pi/atlas/packages/mcp-server/server.js +149 -21
- package/hosts/pi/skills/_shared/references/stack-profiles.md +36 -0
- package/hosts/pi/skills/_shared/scripts/document_quality.mjs +37 -1
- package/hosts/pi/skills/atlas-audit/SKILL.md +201 -0
- package/hosts/pi/skills/atlas-audit/agents/openai.yaml +7 -0
- package/hosts/pi/skills/atlas-task-validator/SKILL.md +6 -0
- package/hosts/pi/skills/atlas-workflow-orchestrator/SKILL.md +32 -10
- package/hosts/zcode/.zcode-plugin/plugin.json +27 -0
- package/hosts/zcode/agents/atlas-direct-execute.md +31 -0
- package/hosts/zcode/agents/atlas-findings-repair.md +39 -0
- package/hosts/zcode/agents/atlas-plan-execute.md +33 -0
- package/hosts/zcode/agents/atlas-slice-review.md +27 -0
- package/hosts/zcode/agents/atlas-task-validator.md +138 -0
- package/hosts/zcode/packages/mcp-server/README.md +29 -0
- package/hosts/zcode/packages/mcp-server/VERSION +1 -0
- package/hosts/zcode/packages/mcp-server/package.json +15 -0
- package/hosts/zcode/packages/mcp-server/server.js +3963 -0
- package/hosts/zcode/packages/orchestrator/README.md +283 -0
- package/hosts/zcode/packages/orchestrator/commands/workflow.md +39 -0
- package/hosts/zcode/packages/orchestrator/defaults/paths.md +21 -0
- package/hosts/zcode/packages/orchestrator/references/host-adapters.md +106 -0
- package/hosts/zcode/packages/orchestrator/references/qa_s13_matrix.md +141 -0
- package/hosts/zcode/packages/orchestrator/references/subagent_dispatch.md +52 -0
- package/hosts/zcode/packages/orchestrator/skills/atlas-workflow-orchestrator/SKILL.md +411 -0
- package/hosts/zcode/packages/templates/BACKLOG_MESTRE_TEMPLATE.md +855 -0
- package/hosts/zcode/packages/templates/BOUNDARY_PRD_PLAN.md +93 -0
- package/hosts/zcode/packages/templates/PERGUNTAS_EM_ABERTO_TEMPLATE.md +139 -0
- package/hosts/zcode/packages/templates/PLAN_TEMPLATE.md +146 -0
- package/hosts/zcode/packages/templates/PRD_TEMPLATE.md +150 -0
- package/hosts/zcode/packages/templates/STATE_FILE_SCHEMA.md +56 -0
- package/hosts/zcode/skills/_shared/references/stack-profiles.md +72 -0
- package/hosts/zcode/skills/_shared/scripts/document_quality.mjs +288 -0
- package/hosts/zcode/skills/atlas-audit/SKILL.md +201 -0
- package/hosts/zcode/skills/atlas-audit/agents/openai.yaml +7 -0
- package/hosts/zcode/skills/atlas-backlog-generator/SKILL.md +93 -0
- package/hosts/zcode/skills/atlas-backlog-generator/agents/openai.yaml +4 -0
- package/hosts/zcode/skills/atlas-direct-execute/SKILL.md +221 -0
- package/hosts/zcode/skills/atlas-direct-execute/agents/openai.yaml +7 -0
- package/hosts/zcode/skills/atlas-findings-repair/SKILL.md +158 -0
- package/hosts/zcode/skills/atlas-findings-repair/agents/openai.yaml +7 -0
- package/hosts/zcode/skills/atlas-plan-execute/SKILL.md +175 -0
- package/hosts/zcode/skills/atlas-plan-execute/agents/openai.yaml +7 -0
- package/hosts/zcode/skills/atlas-plan-execute/references/plan-contract.md +88 -0
- package/hosts/zcode/skills/atlas-plan-execute/references/quality-gates.md +60 -0
- package/hosts/zcode/skills/atlas-plan-execute/scripts/check_budget_state.py +96 -0
- package/hosts/zcode/skills/atlas-plan-execute/scripts/extract_plan_contract.py +191 -0
- package/hosts/zcode/skills/atlas-plan-execute/scripts/validate_gate_result.py +56 -0
- package/hosts/zcode/skills/atlas-plan-handoff/SKILL.md +183 -0
- package/hosts/zcode/skills/atlas-plan-handoff/agents/openai.yaml +7 -0
- package/hosts/zcode/skills/atlas-prd-interview/SKILL.md +82 -0
- package/hosts/zcode/skills/atlas-prd-interview/agents/openai.yaml +7 -0
- package/hosts/zcode/skills/atlas-slice-review/SKILL.md +156 -0
- package/hosts/zcode/skills/atlas-slice-review/agents/openai.yaml +4 -0
- package/hosts/zcode/skills/atlas-slice-review/references/review-contract.md +58 -0
- package/hosts/zcode/skills/atlas-slice-review/references/scenario-lenses.md +57 -0
- package/hosts/zcode/skills/atlas-slice-review/scripts/classify_findings.mjs +60 -0
- package/hosts/zcode/skills/atlas-slice-review/scripts/classify_findings.py +24 -0
- package/hosts/zcode/skills/atlas-slice-review/scripts/extract_review_slice.py +158 -0
- package/hosts/zcode/skills/atlas-sprint-prd-generator/SKILL.md +77 -0
- package/hosts/zcode/skills/atlas-sprint-prd-generator/agents/openai.yaml +7 -0
- package/hosts/zcode/skills/atlas-task-validator/SKILL.md +179 -0
- package/hosts/zcode/skills/atlas-task-validator/agents/openai.yaml +7 -0
- package/hosts/zcode/skills/atlas-workflow-orchestrator/SKILL.md +411 -0
- package/package.json +1 -1
- package/plugins/atlas-workflow-orchestrator/.codex-plugin/plugin.json +5 -4
- package/plugins/atlas-workflow-orchestrator/VERSION +1 -1
- package/plugins/atlas-workflow-orchestrator/orchestrator/README.md +15 -2
- package/plugins/atlas-workflow-orchestrator/orchestrator/commands/workflow.md +7 -5
- package/plugins/atlas-workflow-orchestrator/orchestrator/references/host-adapters.md +13 -12
- package/plugins/atlas-workflow-orchestrator/orchestrator/references/subagent_dispatch.md +11 -1
- package/plugins/atlas-workflow-orchestrator/orchestrator/skills/atlas-workflow-orchestrator/SKILL.md +32 -10
- package/plugins/atlas-workflow-orchestrator/packages/mcp-server/README.md +2 -2
- package/plugins/atlas-workflow-orchestrator/packages/mcp-server/package.json +1 -1
- package/plugins/atlas-workflow-orchestrator/packages/mcp-server/server.js +149 -21
- 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 +37 -1
- package/plugins/atlas-workflow-orchestrator/packages/skills/atlas-audit/SKILL.md +201 -0
- package/plugins/atlas-workflow-orchestrator/packages/skills/atlas-audit/agents/openai.yaml +7 -0
- package/plugins/atlas-workflow-orchestrator/packages/skills/atlas-task-validator/SKILL.md +6 -0
- package/plugins/atlas-workflow-orchestrator/skills/_shared/references/stack-profiles.md +36 -0
- package/plugins/atlas-workflow-orchestrator/skills/_shared/scripts/document_quality.mjs +37 -1
- package/plugins/atlas-workflow-orchestrator/skills/atlas-audit/SKILL.md +201 -0
- package/plugins/atlas-workflow-orchestrator/skills/atlas-audit/agents/openai.yaml +7 -0
- package/plugins/atlas-workflow-orchestrator/skills/atlas-task-validator/SKILL.md +6 -0
- package/plugins/atlas-workflow-orchestrator/skills/atlas-workflow-orchestrator/SKILL.md +32 -10
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
const VALID = Object.freeze({
|
|
6
|
+
moscow: new Set(['Must', 'Should', 'Could', "Won't now"]),
|
|
7
|
+
gain: new Set(['alto', 'médio', 'baixo']),
|
|
8
|
+
effort: new Set(['alto', 'médio', 'baixo']),
|
|
9
|
+
priority: new Set(['P0', 'P1', 'P2', 'P3']),
|
|
10
|
+
state: new Set(['backlog', 'ready', 'doing', 'review', 'done', 'blocked']),
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const STACK_MANIFESTS = [
|
|
14
|
+
'package.json', 'tsconfig.json', 'pubspec.yaml', 'pyproject.toml', 'requirements.txt', 'setup.py',
|
|
15
|
+
'go.mod', 'Cargo.toml', 'pom.xml', 'build.gradle', 'build.gradle.kts', 'settings.gradle', 'settings.gradle.kts',
|
|
16
|
+
'firebase.json', '.firebaserc', 'openapi.yaml', 'openapi.yml', 'openapi.json', 'swagger.yaml', 'swagger.yml', 'swagger.json',
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
function boundaryRoot(projectRoot, boundary) {
|
|
20
|
+
const project = path.resolve(projectRoot);
|
|
21
|
+
let current = path.resolve(project, boundary ?? '.');
|
|
22
|
+
const relative = path.relative(project, current);
|
|
23
|
+
if (relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
|
|
24
|
+
throw new Error(`BOUNDARY_OUTSIDE_PROJECT:${boundary}`);
|
|
25
|
+
}
|
|
26
|
+
if (fs.existsSync(current) && fs.statSync(current).isFile()) current = path.dirname(current);
|
|
27
|
+
while (current === project || current.startsWith(`${project}${path.sep}`)) {
|
|
28
|
+
if (STACK_MANIFESTS.some((name) => fs.existsSync(path.join(current, name)))) return current;
|
|
29
|
+
if (current === project) break;
|
|
30
|
+
current = path.dirname(current);
|
|
31
|
+
}
|
|
32
|
+
return path.resolve(project, boundary ?? '.');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function containsGetxImport(root) {
|
|
36
|
+
const queue = ['lib', 'test'].map((name) => path.join(root, name)).filter((dir) => fs.existsSync(dir));
|
|
37
|
+
let inspected = 0;
|
|
38
|
+
while (queue.length > 0 && inspected < 5000) {
|
|
39
|
+
const current = queue.pop();
|
|
40
|
+
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
|
|
41
|
+
const target = path.join(current, entry.name);
|
|
42
|
+
if (entry.isDirectory()) queue.push(target);
|
|
43
|
+
else if (entry.isFile() && entry.name.endsWith('.dart')) {
|
|
44
|
+
inspected += 1;
|
|
45
|
+
if (/package:get\/get(?:_core)?\.dart/.test(fs.readFileSync(target, 'utf8'))) return true;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function detectBoundaryProfile(root, declaredCommands) {
|
|
53
|
+
const exists = (name) => fs.existsSync(path.join(root, name));
|
|
54
|
+
const readIfExists = (name) => {
|
|
55
|
+
try { return exists(name) ? fs.readFileSync(path.join(root, name), 'utf8') : ''; } catch { return ''; }
|
|
56
|
+
};
|
|
57
|
+
const commands = declaredCommands.filter((v) => typeof v === 'string');
|
|
58
|
+
let packageJson = null;
|
|
59
|
+
if (exists('package.json')) {
|
|
60
|
+
try { packageJson = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')); } catch {}
|
|
61
|
+
}
|
|
62
|
+
let pubspec = '';
|
|
63
|
+
if (exists('pubspec.yaml')) {
|
|
64
|
+
try { pubspec = fs.readFileSync(path.join(root, 'pubspec.yaml'), 'utf8'); } catch {}
|
|
65
|
+
}
|
|
66
|
+
const packageCommands = Object.values(packageJson?.scripts ?? {}).filter((v) => typeof v === 'string');
|
|
67
|
+
const packageDeps = Object.keys({
|
|
68
|
+
...(packageJson?.dependencies ?? {}),
|
|
69
|
+
...(packageJson?.devDependencies ?? {}),
|
|
70
|
+
...(packageJson?.peerDependencies ?? {}),
|
|
71
|
+
...(packageJson?.optionalDependencies ?? {}),
|
|
72
|
+
});
|
|
73
|
+
const cargo = readIfExists('Cargo.toml');
|
|
74
|
+
const pom = readIfExists('pom.xml');
|
|
75
|
+
const gradle = `${readIfExists('build.gradle')}\n${readIfExists('build.gradle.kts')}\n${readIfExists('settings.gradle')}\n${readIfExists('settings.gradle.kts')}`;
|
|
76
|
+
const allCommands = [...commands, ...packageCommands];
|
|
77
|
+
const hasCommand = (re) => allCommands.some((command) => re.test(command));
|
|
78
|
+
const hasPackageDep = (re) => packageDeps.some((dep) => re.test(dep));
|
|
79
|
+
const javaKotlinSignal = exists('pom.xml') || exists('build.gradle') || exists('build.gradle.kts')
|
|
80
|
+
|| exists('settings.gradle') || exists('settings.gradle.kts') || hasCommand(/\b(gradle|mvn|java|javac|kotlinc)\b/);
|
|
81
|
+
const restSignal = exists('openapi.yaml') || exists('openapi.yml') || exists('openapi.json')
|
|
82
|
+
|| exists('swagger.yaml') || exists('swagger.yml') || exists('swagger.json')
|
|
83
|
+
|| hasPackageDep(/\b(openapi|swagger|express|fastify|koa|hono|axios|ky)\b/i)
|
|
84
|
+
|| /openapi|swagger|spring-boot-starter-web|ktor|retrofit/i.test(`${pom}\n${gradle}\n${pubspec}`);
|
|
85
|
+
return {
|
|
86
|
+
universal: true,
|
|
87
|
+
flutter_dart: exists('pubspec.yaml') || hasCommand(/\b(flutter|dart)\b/),
|
|
88
|
+
node_typescript: exists('package.json') || exists('tsconfig.json') || hasCommand(/\b(node|npm|pnpm|yarn|bun|tsc)\b/),
|
|
89
|
+
python: exists('pyproject.toml') || exists('requirements.txt') || exists('setup.py') || hasCommand(/\b(python3?|pytest|ruff|mypy)\b/),
|
|
90
|
+
go: exists('go.mod') || hasCommand(/\bgo\s+(test|build|run|vet|fmt)\b/),
|
|
91
|
+
rust: exists('Cargo.toml') || hasCommand(/\bcargo\s+(test|build|run|check|clippy|fmt)\b/) || /^\s*\[package\]/m.test(cargo),
|
|
92
|
+
java_kotlin: javaKotlinSignal,
|
|
93
|
+
firebase: exists('firebase.json') || exists('.firebaserc') || hasPackageDep(/^firebase$|^@firebase\/|firebase-admin/i)
|
|
94
|
+
|| /firebase_core|cloud_firestore|firebase_auth|firebase_messaging|firebase_storage/i.test(pubspec),
|
|
95
|
+
supabase: hasPackageDep(/^@supabase\/|supabase-js/i) || /supabase_flutter|supabase|postgrest/i.test(pubspec),
|
|
96
|
+
rest_openapi: restSignal,
|
|
97
|
+
getx: /^\s{0,4}get\s*:/m.test(pubspec) || containsGetxImport(root),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function detectStackProfiles(root, declaredCommands = [], boundaryPaths = ['.']) {
|
|
102
|
+
const project = path.resolve(root);
|
|
103
|
+
const requested = boundaryPaths.length > 0 ? boundaryPaths : ['.'];
|
|
104
|
+
const boundaries = [...new Set(requested.map((boundary) => boundaryRoot(project, boundary)))].map((dir) => ({
|
|
105
|
+
boundary: path.relative(project, dir).replaceAll('\\', '/') || '.',
|
|
106
|
+
...detectBoundaryProfile(dir, declaredCommands),
|
|
107
|
+
}));
|
|
108
|
+
return {
|
|
109
|
+
universal: true,
|
|
110
|
+
flutter_dart: boundaries.some((profile) => profile.flutter_dart),
|
|
111
|
+
node_typescript: boundaries.some((profile) => profile.node_typescript),
|
|
112
|
+
python: boundaries.some((profile) => profile.python),
|
|
113
|
+
go: boundaries.some((profile) => profile.go),
|
|
114
|
+
rust: boundaries.some((profile) => profile.rust),
|
|
115
|
+
java_kotlin: boundaries.some((profile) => profile.java_kotlin),
|
|
116
|
+
firebase: boundaries.some((profile) => profile.firebase),
|
|
117
|
+
supabase: boundaries.some((profile) => profile.supabase),
|
|
118
|
+
rest_openapi: boundaries.some((profile) => profile.rest_openapi),
|
|
119
|
+
getx: boundaries.some((profile) => profile.getx),
|
|
120
|
+
boundaries,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function parseTable(markdown, heading) {
|
|
125
|
+
const start = markdown.indexOf(heading);
|
|
126
|
+
if (start < 0) return [];
|
|
127
|
+
const tail = markdown.slice(start + heading.length);
|
|
128
|
+
const end = tail.search(/\n##?\s/);
|
|
129
|
+
return (end < 0 ? tail : tail.slice(0, end)).split('\n')
|
|
130
|
+
.filter((line) => /^\|.*\|\s*$/.test(line))
|
|
131
|
+
.map((line) => line.split('|').slice(1, -1).map((cell) => cell.trim()))
|
|
132
|
+
.filter((cells) => cells.length && !cells.every((cell) => /^-+$/.test(cell)));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function parseSprintRows(markdown) {
|
|
136
|
+
const rows = parseTable(markdown, '## 7. Registro de sprints');
|
|
137
|
+
const header = rows.findIndex((row) => row[0] === 'ID');
|
|
138
|
+
return rows.slice(header + 1).filter((row) => /^S\d{2}[a-z]?$/.test(row[0])).map((row) => ({
|
|
139
|
+
id: row[0], name: row[1], phase: row[2], objective: row[3], moscow: row[4], gain: row[5].toLowerCase(),
|
|
140
|
+
effort: row[6].toLowerCase(), priority: row[7], prd: row[8], dependencies: row[9], state: row[10], gate: row[11], raw: row,
|
|
141
|
+
}));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function parseDecisionRows(markdown) {
|
|
145
|
+
const rows = parseTable(markdown, '### Decisões bloqueantes');
|
|
146
|
+
const header = rows.findIndex((row) => row[0] === 'ID');
|
|
147
|
+
return rows.slice(header + 1).filter((row) => /^D\d+$/.test(row[0])).map((row) => ({
|
|
148
|
+
id: row[0], decision: row[1], blocks: row[2], owner: row[3], status: row[4], raw: row,
|
|
149
|
+
}));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function dependencyIds(value) {
|
|
153
|
+
if (!value || value === '—') return [];
|
|
154
|
+
return [...value.matchAll(/S\d{2}[a-z]?/g)].map((match) => match[0]);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function findCycle(rows) {
|
|
158
|
+
const graph = new Map(rows.map((row) => [row.id, dependencyIds(row.dependencies)]));
|
|
159
|
+
const visiting = new Set(); const visited = new Set();
|
|
160
|
+
const walk = (id, chain = []) => {
|
|
161
|
+
if (visiting.has(id)) return [...chain.slice(chain.indexOf(id)), id];
|
|
162
|
+
if (visited.has(id)) return null;
|
|
163
|
+
visiting.add(id);
|
|
164
|
+
for (const dep of graph.get(id) ?? []) {
|
|
165
|
+
const cycle = walk(dep, [...chain, id]);
|
|
166
|
+
if (cycle) return cycle;
|
|
167
|
+
}
|
|
168
|
+
visiting.delete(id); visited.add(id); return null;
|
|
169
|
+
};
|
|
170
|
+
for (const id of graph.keys()) { const cycle = walk(id); if (cycle) return cycle; }
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function changeLogBody(markdown) {
|
|
175
|
+
const match = /^##\s+(?:Registro de alterações|Histórico de alterações)\s*$/im.exec(markdown);
|
|
176
|
+
if (!match) return null;
|
|
177
|
+
const tail = markdown.slice(match.index + match[0].length);
|
|
178
|
+
const end = tail.search(/\n##\s+/);
|
|
179
|
+
return (end < 0 ? tail : tail.slice(0, end)).trim();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function validateBacklogUpdate(before, after, { authorizedIds = [] } = {}) {
|
|
183
|
+
const errors = [];
|
|
184
|
+
const authorized = new Set(authorizedIds);
|
|
185
|
+
const oldRows = parseSprintRows(before); const newRows = parseSprintRows(after);
|
|
186
|
+
const oldById = new Map(oldRows.map((row) => [row.id, row]));
|
|
187
|
+
const newById = new Map(newRows.map((row) => [row.id, row]));
|
|
188
|
+
if (oldById.size !== oldRows.length) errors.push('DUPLICATE_SPRINT_ID_BEFORE');
|
|
189
|
+
if (newById.size !== newRows.length) errors.push('DUPLICATE_SPRINT_ID_AFTER');
|
|
190
|
+
for (const [id, oldRow] of oldById) {
|
|
191
|
+
const next = newById.get(id);
|
|
192
|
+
if (!next) errors.push(`SPRINT_REMOVED:${id}`);
|
|
193
|
+
else if (JSON.stringify(oldRow.raw) !== JSON.stringify(next.raw) && !authorized.has(id)) {
|
|
194
|
+
errors.push(oldRow.state === 'done' ? `DONE_SPRINT_CHANGED:${id}` : `UNAUTHORIZED_SPRINT_CHANGED:${id}`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
const oldDecisions = new Map(parseDecisionRows(before).map((row) => [row.id, row]));
|
|
198
|
+
const newDecisions = new Map(parseDecisionRows(after).map((row) => [row.id, row]));
|
|
199
|
+
for (const [id, row] of oldDecisions) {
|
|
200
|
+
const next = newDecisions.get(id);
|
|
201
|
+
if (!next) errors.push(`DECISION_REMOVED:${id}`);
|
|
202
|
+
else if (/^(decidido|fechado|aprovado)$/i.test(row.status)
|
|
203
|
+
&& JSON.stringify(row.raw) !== JSON.stringify(next.raw) && !authorized.has(id)) errors.push(`CLOSED_DECISION_CHANGED:${id}`);
|
|
204
|
+
}
|
|
205
|
+
for (const row of newRows) {
|
|
206
|
+
for (const [field, values] of Object.entries(VALID)) if (!values.has(row[field])) errors.push(`INVALID_ENUM:${row.id}:${field}:${row[field]}`);
|
|
207
|
+
for (const dependency of dependencyIds(row.dependencies)) {
|
|
208
|
+
if (!newById.has(dependency)) errors.push(`DEPENDENCY_NOT_FOUND:${row.id}:${dependency}`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
const cycle = findCycle(newRows); if (cycle) errors.push(`DEPENDENCY_CYCLE:${cycle.join('>')}`);
|
|
212
|
+
if (/\[(?:NOME_|RESULTADO_|observação|decisão|slug|pendente\])/i.test(after)) errors.push('UNRESOLVED_PLACEHOLDER');
|
|
213
|
+
if (before !== after) {
|
|
214
|
+
const oldLog = changeLogBody(before);
|
|
215
|
+
const newLog = changeLogBody(after);
|
|
216
|
+
if (newLog === null) errors.push('CHANGELOG_REQUIRED');
|
|
217
|
+
else if (oldLog !== null && !newLog.startsWith(oldLog)) errors.push('CHANGELOG_REWRITTEN');
|
|
218
|
+
else if (newLog === oldLog) errors.push('CHANGELOG_ENTRY_REQUIRED');
|
|
219
|
+
}
|
|
220
|
+
return { valid: errors.length === 0, errors };
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export function resolveSprintAuthority({ sprintId, explicitPath, canonicalPath, candidates }) {
|
|
224
|
+
const normalized = candidates.map((candidate) => ({ ...candidate, path: path.resolve(candidate.path) }));
|
|
225
|
+
const byPath = (target) => normalized.find((candidate) => candidate.path === path.resolve(target));
|
|
226
|
+
if (explicitPath) {
|
|
227
|
+
const match = byPath(explicitPath); if (!match?.sprints.includes(sprintId)) throw new Error(`SPRINT_NOT_FOUND_IN_EXPLICIT_PATH:${sprintId}`); return match;
|
|
228
|
+
}
|
|
229
|
+
if (canonicalPath) {
|
|
230
|
+
const match = byPath(canonicalPath); if (!match?.sprints.includes(sprintId)) throw new Error(`SPRINT_NOT_FOUND_IN_CANONICAL_BACKLOG:${sprintId}`); return match;
|
|
231
|
+
}
|
|
232
|
+
const matches = normalized.filter((candidate) => candidate.sprints.includes(sprintId));
|
|
233
|
+
if (matches.length !== 1) throw new Error(matches.length ? `AMBIGUOUS_BACKLOG_AUTHORITY:${matches.map((m) => m.path).join(',')}` : `SPRINT_NOT_FOUND:${sprintId}`);
|
|
234
|
+
return matches[0];
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export function closedDecisionIds(prd) {
|
|
238
|
+
return new Set([...prd.matchAll(/^\|\s*(D\d+)\s*\|\s*(?!<|\[)(.+?)\s*\|\s*$/gm)].map((match) => match[1]));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export function pendingInterviewQuestions(prd, questions) {
|
|
242
|
+
const closed = closedDecisionIds(prd);
|
|
243
|
+
return questions.filter((question) => !closed.has(question.decision_id));
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export function applyInterviewRound(prd, answers, date = new Date().toISOString().slice(0, 10)) {
|
|
247
|
+
const ids = new Set();
|
|
248
|
+
for (const answer of answers) {
|
|
249
|
+
if (!answer || typeof answer.decision_id !== 'string' || !/^D\d+$/.test(answer.decision_id)) throw new Error('INVALID_DECISION_ID');
|
|
250
|
+
if (ids.has(answer.decision_id)) throw new Error(`DUPLICATE_DECISION_ID:${answer.decision_id}`);
|
|
251
|
+
if (typeof answer.value !== 'string' || !answer.value.trim()) throw new Error(`EMPTY_DECISION_VALUE:${answer.decision_id}`);
|
|
252
|
+
ids.add(answer.decision_id);
|
|
253
|
+
}
|
|
254
|
+
let updated = prd;
|
|
255
|
+
for (const answer of answers) {
|
|
256
|
+
const row = new RegExp(`^\\|\\s*${answer.decision_id}\\s*\\|.*$`, 'm');
|
|
257
|
+
const replacement = `| ${answer.decision_id} | ${answer.value} |`;
|
|
258
|
+
updated = row.test(updated) ? updated.replace(row, replacement) : updated.replace(/(\| ID \| Decisão \|\n\|[-| ]+\|)/, `$1\n${replacement}`);
|
|
259
|
+
}
|
|
260
|
+
const log = `${date} — entrevista: ${answers.map((answer) => answer.decision_id).join(', ')} persistida(s)`;
|
|
261
|
+
updated = /\*\*Histórico:\*\*/.test(updated)
|
|
262
|
+
? updated.replace(/(\*\*Histórico:\*\*[^\n]*)/, `$1 · ${log}`)
|
|
263
|
+
: `${updated.trimEnd()}\n\n**Histórico:** ${log}\n`;
|
|
264
|
+
return updated;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export function persistInterviewRound(prdPath, answers, date = new Date().toISOString().slice(0, 10)) {
|
|
268
|
+
const absolute = path.resolve(prdPath);
|
|
269
|
+
const temporary = path.join(path.dirname(absolute), `.${path.basename(absolute)}.${process.pid}.${Date.now()}.tmp`);
|
|
270
|
+
try {
|
|
271
|
+
const current = fs.readFileSync(absolute, 'utf8');
|
|
272
|
+
const updated = applyInterviewRound(current, answers, date);
|
|
273
|
+
const materialized = closedDecisionIds(updated);
|
|
274
|
+
const missingBeforeWrite = answers.filter((answer) => !materialized.has(answer.decision_id));
|
|
275
|
+
if (missingBeforeWrite.length > 0) {
|
|
276
|
+
throw new Error(`DECISION_NOT_MATERIALIZED:${missingBeforeWrite.map((answer) => answer.decision_id).join(',')}`);
|
|
277
|
+
}
|
|
278
|
+
const mode = fs.statSync(absolute).mode;
|
|
279
|
+
fs.writeFileSync(temporary, updated, { encoding: 'utf8', mode });
|
|
280
|
+
fs.renameSync(temporary, absolute);
|
|
281
|
+
const readback = fs.readFileSync(absolute, 'utf8');
|
|
282
|
+
if (readback !== updated) throw new Error('READBACK_DIVERGENT');
|
|
283
|
+
return readback;
|
|
284
|
+
} catch (error) {
|
|
285
|
+
try { fs.rmSync(temporary, { force: true }); } catch {}
|
|
286
|
+
throw new Error(`INTERVIEW_PERSISTENCE_FAILED:${error.message}`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: atlas-audit
|
|
3
|
+
description: Skill/mode universal de auditoria Atlas. Use para `/workflow audit <target>` com flags opcionais `--handoff` e `--scope <descrição>`. Audita código contra regras locais, boas práticas da stack detectada e complexidade acidental estilo Ponytail, sem corrigir código nem executar plano.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Atlas Audit
|
|
7
|
+
|
|
8
|
+
Auditoria universal, framework-agnóstica. Esta skill lê o repositório real, audita o `target` informado e entrega relatório de achados. Opcionalmente gera handoff Atlas-style para correção futura. **Nunca altera código. Nunca executa plano.**
|
|
9
|
+
|
|
10
|
+
## Sintaxe
|
|
11
|
+
|
|
12
|
+
```text
|
|
13
|
+
/workflow audit <target>
|
|
14
|
+
/workflow audit <target> --handoff
|
|
15
|
+
/workflow audit <target> --scope <descrição>
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`target` pode ser arquivo, diretório, pacote, módulo, feature, PRD/plano como referência ou descrição que aponte para um boundary localizável. Se não for possível resolver o boundary em disco, pare e peça um target mais preciso.
|
|
19
|
+
|
|
20
|
+
## Contrato duro
|
|
21
|
+
|
|
22
|
+
- Auditar o target informado, não o repo inteiro por padrão.
|
|
23
|
+
- Diagnóstico factual vem antes de proposta de correção.
|
|
24
|
+
- Todo achado precisa de evidência concreta `arquivo:linha`.
|
|
25
|
+
- Não criar finding por preferência genérica sem evidência local.
|
|
26
|
+
- Regras locais reais prevalecem sobre boas práticas genéricas.
|
|
27
|
+
- Não migrar lógica especialista de Flutter/Dart para o core universal.
|
|
28
|
+
- Adapters por stack/backend são opcionais; fallback universal deve funcionar.
|
|
29
|
+
- Não inventar comandos de validação: usar apenas manifests/configs/scripts reais.
|
|
30
|
+
- `--handoff` gera artefato de plano em `.atlas/plans/`, mas não chama executor.
|
|
31
|
+
|
|
32
|
+
## Fluxo obrigatório
|
|
33
|
+
|
|
34
|
+
1. **Resolver boundary**
|
|
35
|
+
- Identificar `project_root`, `target`, paths reais e exclusões óbvias (`node_modules`, `build`, `dist`, `.git`, caches).
|
|
36
|
+
- Se `--scope` existir, tratar como recorte adicional, não expansão automática para repo inteiro.
|
|
37
|
+
- Registrar limitações se parte do target estiver ausente, gerada ou ilegível.
|
|
38
|
+
|
|
39
|
+
2. **Ler regras locais reais**
|
|
40
|
+
- Procurar e consultar, quando existirem e forem relevantes ao boundary: `AGENTS.md`, `CLAUDE.md`, `README.md`, docs de rules, `project-rules/`, configs de lint/format/typecheck/test, manifests e arquivos de workspace.
|
|
41
|
+
- Não copiar regras integralmente no relatório; listar só fontes consultadas e regras aplicáveis.
|
|
42
|
+
|
|
43
|
+
3. **Detectar stack/perfis**
|
|
44
|
+
- Usar `../_shared/references/stack-profiles.md` como baseline.
|
|
45
|
+
- Detectar por manifests/configs/comandos reais, não só por extensão.
|
|
46
|
+
- Perfis esperados quando houver sinal: Dart/Flutter, TypeScript/Node, Python, Go, Rust, Java/Kotlin, Firebase, Supabase, REST/OpenAPI.
|
|
47
|
+
- Se múltiplos perfis coexistirem, aplicar cada regra só ao boundary onde o sinal foi ativado.
|
|
48
|
+
|
|
49
|
+
4. **Entender arquitetura antes de julgar**
|
|
50
|
+
- Mapear camadas reais, fluxo de dados, contratos, entrypoints, DI, estado, side effects, testes e consumidores afetados.
|
|
51
|
+
- Comparar com padrões existentes do repo antes de classificar algo como violação.
|
|
52
|
+
|
|
53
|
+
5. **Auditar por lentes**
|
|
54
|
+
- Arquitetura e ownership.
|
|
55
|
+
- Contrato/dados/schemas/mappers/DTOs.
|
|
56
|
+
- Erros, falhas parciais, retries, cleanup.
|
|
57
|
+
- Segurança, authz/authn, segredos, trust boundaries.
|
|
58
|
+
- Estado, concorrência, lifecycle, idempotência.
|
|
59
|
+
- Fluxos previstos e imprevistos.
|
|
60
|
+
- Testes e validação declarada.
|
|
61
|
+
- Observabilidade, logs e diagnóstico.
|
|
62
|
+
- Manutenção/DX.
|
|
63
|
+
|
|
64
|
+
6. **Ponytail pass final**
|
|
65
|
+
- Procurar abstração inútil, wrapper sem valor, código morto, duplicação simples, dependência excessiva, branching/config acidental e complexidade que não protege segurança/contrato/dados.
|
|
66
|
+
- Só registrar se houver simplificação clara e segura, com evidência local.
|
|
67
|
+
|
|
68
|
+
## Severidade
|
|
69
|
+
|
|
70
|
+
- `P0`: quebra crítica, perda/corrupção de dados, bypass de segurança, execução impossível.
|
|
71
|
+
- `P1`: bug provável em fluxo principal, contrato errado, regressão relevante, risco alto.
|
|
72
|
+
- `P2`: gap importante, cenário não coberto, arquitetura frágil, teste/validação faltante com risco real.
|
|
73
|
+
- `P3`: melhoria localizada, simplificação Ponytail, DX/manutenção, observação de baixo risco.
|
|
74
|
+
|
|
75
|
+
## Formato do achado
|
|
76
|
+
|
|
77
|
+
```md
|
|
78
|
+
### AUDIT-001 — P1 — <categoria>
|
|
79
|
+
- Arquivo: `path/to/file.ext:123`
|
|
80
|
+
- Evidência: <fato observado no código>
|
|
81
|
+
- Impacto: <falha/risco concreto>
|
|
82
|
+
- Correção proposta: <direção, sem implementar>
|
|
83
|
+
- Dependências/bloqueios: <se houver>
|
|
84
|
+
- Status: `open`
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Se não houver linha precisa, não promova a finding; registre em limitações ou perguntas.
|
|
88
|
+
|
|
89
|
+
## Relatório de auditoria
|
|
90
|
+
|
|
91
|
+
Responder em pt-BR com:
|
|
92
|
+
|
|
93
|
+
```md
|
|
94
|
+
# Auditoria Atlas — <target>
|
|
95
|
+
|
|
96
|
+
## Stack detectada
|
|
97
|
+
...
|
|
98
|
+
|
|
99
|
+
## Regras locais consultadas
|
|
100
|
+
...
|
|
101
|
+
|
|
102
|
+
## Boundary auditado
|
|
103
|
+
...
|
|
104
|
+
|
|
105
|
+
## Achados
|
|
106
|
+
### P0
|
|
107
|
+
...
|
|
108
|
+
### P1
|
|
109
|
+
...
|
|
110
|
+
### P2
|
|
111
|
+
...
|
|
112
|
+
### P3
|
|
113
|
+
...
|
|
114
|
+
|
|
115
|
+
## Gaps por área
|
|
116
|
+
- Contrato/dados:
|
|
117
|
+
- Segurança:
|
|
118
|
+
- Testes:
|
|
119
|
+
- Arquitetura:
|
|
120
|
+
|
|
121
|
+
## Limitações
|
|
122
|
+
...
|
|
123
|
+
|
|
124
|
+
## Próximo passo seguro
|
|
125
|
+
...
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Se zero achados, diga explicitamente `Nenhum achado P0/P1/P2/P3 com evidência suficiente no boundary auditado` e liste risco residual/limitações.
|
|
129
|
+
|
|
130
|
+
## `--handoff`
|
|
131
|
+
|
|
132
|
+
Quando `--handoff` estiver presente, escrever em `.atlas/plans/PLAN_AUDIT_<slug>.md` um plano **conforme ao `PLAN_TEMPLATE.md` canônico** (resolver em `<raiz-do-plugin>/packages/templates/`), para que passe no gate TC (`atlas_verify_template_conformance`) e seja consumível por `/workflow execute plan <path>` ou por `atlas-plan-execute`. O plano só pode conter tasks derivadas dos achados evidenciados. Se a escrita falhar, reportar bloqueio e não fingir que há handoff executável.
|
|
133
|
+
|
|
134
|
+
A auditoria **não tem PRD**: a fonte de verdade é o relatório de auditoria + achados + regras locais reais. O plano espelha o template, mas reancorado na auditoria — **sem inventar decisões `D*` nem referências PRD inexistentes**.
|
|
135
|
+
|
|
136
|
+
### Cabeçalho obrigatório (gate TC)
|
|
137
|
+
|
|
138
|
+
O gate TC exige literalmente a linha `| **PRD** |`, referência a `BOUNDARY_PRD_PLAN.md` e tarefas `#### T01.`. Em `execution_mode: sequencial`, a §7 Slices é dispensável.
|
|
139
|
+
|
|
140
|
+
```md
|
|
141
|
+
# PLAN AUDIT — <target> (correção de auditoria)
|
|
142
|
+
|
|
143
|
+
| Campo | Valor |
|
|
144
|
+
|-------|-------|
|
|
145
|
+
| **PRD** | N/A — origem auditoria; ver **Source audit** abaixo |
|
|
146
|
+
| **Package / app** | `<boundary auditado>` |
|
|
147
|
+
| **Tipo** | `audit-fix` |
|
|
148
|
+
| **execution_mode** | `sequencial (T01→TN)` |
|
|
149
|
+
| **Data** | <YYYY-MM-DD> |
|
|
150
|
+
|
|
151
|
+
**Escopo técnico:** boundary da auditoria. **Fora:** <o que ficou fora do target/scope>.
|
|
152
|
+
|
|
153
|
+
Política: [BOUNDARY_PRD_PLAN.md](./BOUNDARY_PRD_PLAN.md).
|
|
154
|
+
|
|
155
|
+
## Metadados de execução
|
|
156
|
+
- Plan prefix: `atlas`
|
|
157
|
+
- Execution mode: `sequencial (T01→TN)`
|
|
158
|
+
- Executor skill: `atlas-plan-execute`
|
|
159
|
+
- Internal validator: `atlas-task-validator`
|
|
160
|
+
- Source audit: `<título/path/data do relatório>`
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
> `N/A — origem auditoria` satisfaz o regex do TC declarando a proveniência sem inventar PRD.
|
|
164
|
+
|
|
165
|
+
### Seções obrigatórias (§1–§6, §8)
|
|
166
|
+
|
|
167
|
+
Sem PRD, as âncoras "derivados do PRD" do template passam a apontar para achados/regras locais:
|
|
168
|
+
|
|
169
|
+
- **§1 Tradução executiva** — 1 parágrafo do que será corrigido + resultado observável; referência ao relatório de auditoria.
|
|
170
|
+
- **§2 Invariantes de execução** — invariantes derivados das regras locais reais e do boundary (não de `D*`).
|
|
171
|
+
- **§3 Pitfalls** — anti-padrões evidenciados na auditoria → correção canônica.
|
|
172
|
+
- **§4 Estado na abertura da sprint** — 3–6 bullets do que está quebrado hoje, ancorados nos achados.
|
|
173
|
+
- **§5 Tarefas de execução** — `#### T01.`…`#### TNN.`, uma por achado evidenciado (schema abaixo).
|
|
174
|
+
- **§6 Contratos técnicos** — só se a auditoria revelou contrato/dados/schema afetado; senão `Não aplicável`.
|
|
175
|
+
- **§8 Validação e checklist (validator)** — comandos reais detectados (manifests/scripts) + checkboxes derivados dos achados. Nunca inventar `flutter`/`npm`/`pytest`.
|
|
176
|
+
|
|
177
|
+
### Schema de cada task (§5)
|
|
178
|
+
|
|
179
|
+
```md
|
|
180
|
+
#### T01. <correção curta>
|
|
181
|
+
- **Objetivo:** <resultado observável>
|
|
182
|
+
- **Referência ao achado:** `AUDIT-NNN` — `arquivo:linha`
|
|
183
|
+
- **Mudança esperada:** <o que muda concretamente>
|
|
184
|
+
- **Invariantes preservados:** <§2 / regra local>
|
|
185
|
+
- **Não mudar:** <…>
|
|
186
|
+
- **Dependências:** <nenhuma | T0X>
|
|
187
|
+
- **Riscos:** <se relevante>
|
|
188
|
+
- **Critério de done:** <sinal objetivo>
|
|
189
|
+
- **Validação local:**
|
|
190
|
+
```bash
|
|
191
|
+
cd <package> && <comando real>
|
|
192
|
+
```
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Regras do handoff
|
|
196
|
+
|
|
197
|
+
- Incluir apenas validações derivadas de manifests/configs reais.
|
|
198
|
+
- Não usar task vaga como "refatorar geral".
|
|
199
|
+
- Não incluir achado sem `arquivo:linha`; toda task aponta para um `AUDIT-NNN`.
|
|
200
|
+
- Não chamar executor automaticamente.
|
|
201
|
+
- Reportar o path final do plano e deixar claro que ele não foi executado.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
interface:
|
|
2
|
+
display_name: "Atlas Audit"
|
|
3
|
+
short_description: "Auditoria universal sem correção de código"
|
|
4
|
+
default_prompt: "Use $atlas-audit para auditar o target informado contra regras locais, stack detectada e Ponytail pass, sem alterar código."
|
|
5
|
+
|
|
6
|
+
policy:
|
|
7
|
+
allow_implicit_invocation: true
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: atlas-backlog-generator
|
|
3
|
+
description: Skill `atlas-backlog-generator`. Use somente quando o usuário acionar explicitamente `$atlas-backlog-generator` ou pedir explicitamente para criar, gerar, montar, estruturar ou atualizar um backlog mestre Atlas a partir de uma conversa, prompt, ideia de feature, briefing, PRD macro, lista solta de requisitos, roadmap ou objetivo de produto, usando `BACKLOG_MESTRE_TEMPLATE.md` como template canônico e aplicando fases, sprints, dependências, MoSCoW e esforço x ganho. Não usar por inferência implícita em pedidos genéricos de planejamento, brainstorming, PRD ou implementação.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Atlas Backlog Generator
|
|
7
|
+
|
|
8
|
+
Crie backlogs mestres Atlas em PT-BR, ancorados no template canônico, com decomposição gradual em fases e sprints pequenas, priorização MoSCoW, matriz esforço x ganho, dependências explícitas, gates, riscos e próxima sprint executável.
|
|
9
|
+
|
|
10
|
+
Esta skill é documental: ela cria ou atualiza o `BACKLOG_MESTRE*.md` no projeto consumidor. Ela não implementa código, não gera PRDs de sprint e não substitui `atlas-sprint-prd-generator`.
|
|
11
|
+
|
|
12
|
+
Acione esta skill apenas por pedido explícito. Se o usuário apenas pedir planejamento, brainstorming, PRD ou execução, não usar esta skill automaticamente.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Entradas aceitas
|
|
17
|
+
|
|
18
|
+
- Conversa livre, ideia de feature, prompt exploratório ou briefing incompleto.
|
|
19
|
+
- PRD macro, roadmap, lista de requisitos, issue/backlog item ou texto colado pelo usuário.
|
|
20
|
+
- Opcional: nome do projeto/feature, path de saída, fontes canônicas, restrições técnicas, prioridade de negócio e escopo fora do ciclo.
|
|
21
|
+
|
|
22
|
+
Se faltar informação não bloqueante, gere o backlog com premissas marcadas e registre perguntas/riscos. Pergunte antes de salvar somente quando faltar uma das decisões bloqueantes: resultado final esperado, fronteira de escopo ou plataforma/produto alvo.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Workflow obrigatório
|
|
27
|
+
|
|
28
|
+
1. **Resolver template canônico:** descubra a raiz do plugin/bundle e leia `packages/templates/BACKLOG_MESTRE_TEMPLATE.md`. Se ausente, aborte com: `Template canônico ausente: BACKLOG_MESTRE_TEMPLATE.md`.
|
|
29
|
+
2. **Entender pedido:** extraia objetivo, usuários, resultado final esperado, fora de escopo, restrições, dependências, riscos, stakeholders e sinais de valor.
|
|
30
|
+
3. **Inspecionar contexto real:** quando houver repo/projeto ativo, busque documentos existentes (`BACKLOG_MESTRE*.md`, `PRD*.md`, `ROADMAP*.md`, specs, OpenAPI, docs de arquitetura) e código que influencie dependências. Não invente contrato técnico.
|
|
31
|
+
4. **Fechar ambiguidade crítica:** se uma decisão bloquear a decomposição segura, faça até 3 perguntas objetivas. Se o usuário não responder e houver caminho razoável, registre a premissa como risco/decisão pendente.
|
|
32
|
+
5. **Preencher o template:** mantenha todas as seções do template e substitua placeholders por conteúdo específico. Não apague seções; use `não aplicável` apenas com justificativa curta.
|
|
33
|
+
6. **Decompor em sprints:** transforme o objetivo em fatias verticais pequenas, cada uma com objetivo único, dependências e PRD futuro (`PRD_S<NN>_<slug>.md`).
|
|
34
|
+
7. **Priorizar:** para cada sprint, preencha MoSCoW, ganho, esforço e prioridade usando a regra da seção 8.3 do template.
|
|
35
|
+
8. **Selecionar próxima sprint:** escolha a primeira sprint executável respeitando dependências, DoR, MoSCoW, esforço x ganho e risco. Registre a justificativa na seção 20.
|
|
36
|
+
9. **Atualização não destrutiva:** se o arquivo já existe, compare antes/depois com `validateBacklogUpdate(before, after, { authorizedIds })` de `../_shared/scripts/document_quality.mjs`. `authorizedIds` contém somente IDs cuja mudança foi explicitamente decidida pelo usuário. Preserve demais IDs, linhas `done`, decisões `decidido|fechado|aprovado`, itens/sprints e ordem histórica.
|
|
37
|
+
10. **Registrar alterações:** toda atualização acrescenta `## Registro de alterações` (data, IDs afetados, motivo e fonte). Não reescreva histórico anterior.
|
|
38
|
+
11. **Salvar artefato:** grave o backlog no path pedido ou, se não houver path, crie o diretório `.atlas/backlog/` no projeto consumidor e use `.atlas/backlog/BACKLOG_MESTRE_<slug>.md`.
|
|
39
|
+
12. **Validar antes de finalizar:** bloqueie se `validateBacklogUpdate` apontar sprint/decisão removida, sprint `done` alterada, enum inválido, ciclo de dependência, placeholder acidental ou falta de registro. Confirme também que dependências referenciam IDs existentes.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Regras de decomposição
|
|
44
|
+
|
|
45
|
+
- Gere sprints como unidades de entrega, não períodos de tempo.
|
|
46
|
+
- Mantenha cada sprint com 6 a 8 tasks no máximo quando o PRD futuro for detalhado; se uma sprint tiver mais de um objetivo, quebre em `S<NN>a/b/c`.
|
|
47
|
+
- Comece com descoberta/contrato quando houver ambiguidade ou integração. Não pule para implementação quando o contrato ainda for desconhecido.
|
|
48
|
+
- Preserve a ordem natural: descoberta → especificação/contrato → backend/infra quando necessário → front/app → hardening → QA → rollout.
|
|
49
|
+
- Use dependências para permitir paralelismo seguro; não transforme fase em fila rígida se duas sprints independentes puderem avançar.
|
|
50
|
+
- Inclua estados de erro, loading, empty, permission, observabilidade, QA e rollout onde aplicável. Esses itens não são “extras”; são parte do produto pronto.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Regras de priorização
|
|
55
|
+
|
|
56
|
+
- `Must`: obrigatório para resultado final, segurança, compliance, contrato ou desbloqueio.
|
|
57
|
+
- `Should`: importante para qualidade/adoção, mas contornável por um ciclo.
|
|
58
|
+
- `Could`: refinamento ou melhoria desejável que não bloqueia entrega.
|
|
59
|
+
- `Won't now`: fora do ciclo atual; registre para reduzir reabertura de discussão.
|
|
60
|
+
- `P0`: alto ganho com baixo/médio esforço ou Must desbloqueador.
|
|
61
|
+
- `P1`: alto ganho com alto esforço, ou médio ganho com baixo esforço.
|
|
62
|
+
- `P2`: médio ganho/médio esforço, ou baixo ganho/baixo esforço.
|
|
63
|
+
- `P3`: baixo ganho com médio/alto esforço, candidato a adiar.
|
|
64
|
+
|
|
65
|
+
Se MoSCoW e esforço x ganho conflitarem, MoSCoW vence; uma sprint `Must` de esforço alto deve ser quebrada, não rebaixada silenciosamente.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Qualidade esperada do backlog
|
|
70
|
+
|
|
71
|
+
O backlog final deve:
|
|
72
|
+
|
|
73
|
+
- Declarar precedência documental e fontes canônicas.
|
|
74
|
+
- Explicitar resultado esperado e fora do escopo.
|
|
75
|
+
- Ter dependências internas/externas e decisões bloqueantes com dono/status.
|
|
76
|
+
- Ter registro de sprints com MoSCoW, ganho, esforço, prioridade, PRD futuro, dependências, estado e gate.
|
|
77
|
+
- Ter grafo de dependência coerente com a tabela de sprints.
|
|
78
|
+
- Ter catálogo de fases preservado e adaptado apenas quando necessário.
|
|
79
|
+
- Ter riscos, decisões e próxima sprint executável preenchidos.
|
|
80
|
+
- Ser específico o bastante para gerar PRDs de sprint depois com `atlas-sprint-prd-generator`.
|
|
81
|
+
- Preservar histórico/IDs em update e passar validação de ciclos/enums/placeholders.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Proibições
|
|
86
|
+
|
|
87
|
+
- Não entregar uma lista genérica de tarefas sem usar o template.
|
|
88
|
+
- Não remover gates, DoR/DoD, riscos, decisões ou trilhas transversais.
|
|
89
|
+
- Não inventar endpoints, tabelas, schemas, fornecedores, métricas ou responsabilidades como fatos. Quando forem hipóteses, marcar como premissa.
|
|
90
|
+
- Não transformar o backlog em plano técnico de implementação. Código, classes e comandos entram no plano/PRD quando apropriado, não no backlog mestre.
|
|
91
|
+
- Não deixar `[...]` ou placeholders óbvios no arquivo final, salvo quando o campo estiver deliberadamente pendente e explicado.
|
|
92
|
+
- Não renumerar IDs, reabrir/editar sprint `done`, alterar decisão fechada ou remover item não relacionado por conveniência editorial.
|
|
93
|
+
- Não ser acionada pelo orquestrador: permanece **explicit-only**, fora de qualquer cadeia automática.
|