atlas-workflow 0.9.1 → 0.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/README.md +5 -2
  2. package/VERSION +1 -1
  3. package/build/bump-version.mjs +6 -21
  4. package/build/cli/atlas-init.mjs +92 -5
  5. package/build/tests/classify-findings.test.mjs +20 -0
  6. package/build/tests/etapa3.test.mjs +161 -0
  7. package/build/tests/test_classify_findings.py +79 -0
  8. package/hosts/opencode/.opencode/agents/atlas-findings-repair.md +4 -0
  9. package/hosts/opencode/.opencode/agents/atlas-task-validator.md +18 -1
  10. package/hosts/opencode/.opencode/atlas/VERSION +1 -1
  11. package/hosts/opencode/.opencode/atlas/orchestrator/README.md +7 -5
  12. package/hosts/opencode/.opencode/atlas/orchestrator/commands/workflow.md +1 -1
  13. package/hosts/opencode/.opencode/atlas/orchestrator/references/host-adapters.md +13 -12
  14. package/hosts/opencode/.opencode/atlas/orchestrator/skills/atlas-workflow-orchestrator/SKILL.md +24 -17
  15. package/hosts/opencode/.opencode/atlas/packages/mcp-server/README.md +1 -1
  16. package/hosts/opencode/.opencode/atlas/packages/mcp-server/package.json +1 -1
  17. package/hosts/opencode/.opencode/atlas/packages/mcp-server/server.js +514 -20
  18. package/hosts/opencode/.opencode/atlas/packages/templates/BACKLOG_MESTRE_TEMPLATE.md +14 -3
  19. package/hosts/opencode/.opencode/atlas/packages/templates/PRD_TEMPLATE.md +2 -1
  20. package/hosts/opencode/.opencode/atlas/packages/templates/STATE_FILE_SCHEMA.md +25 -1
  21. package/hosts/opencode/.opencode/skills/_shared/references/stack-profiles.md +36 -0
  22. package/hosts/opencode/.opencode/skills/_shared/scripts/document_quality.mjs +252 -0
  23. package/hosts/opencode/.opencode/skills/atlas-backlog-generator/SKILL.md +7 -2
  24. package/hosts/opencode/.opencode/skills/atlas-direct-execute/SKILL.md +6 -2
  25. package/hosts/opencode/.opencode/skills/atlas-findings-repair/SKILL.md +11 -1
  26. package/hosts/opencode/.opencode/skills/atlas-plan-execute/SKILL.md +16 -2
  27. package/hosts/opencode/.opencode/skills/atlas-plan-handoff/SKILL.md +6 -4
  28. package/hosts/opencode/.opencode/skills/atlas-prd-interview/SKILL.md +7 -2
  29. package/hosts/opencode/.opencode/skills/atlas-slice-review/SKILL.md +37 -2
  30. package/hosts/opencode/.opencode/skills/atlas-slice-review/references/scenario-lenses.md +8 -0
  31. package/hosts/opencode/.opencode/skills/atlas-slice-review/scripts/classify_findings.mjs +60 -0
  32. package/hosts/opencode/.opencode/skills/atlas-slice-review/scripts/classify_findings.py +9 -41
  33. package/hosts/opencode/.opencode/skills/atlas-sprint-prd-generator/SKILL.md +7 -4
  34. package/hosts/opencode/.opencode/skills/atlas-task-validator/SKILL.md +29 -14
  35. package/hosts/opencode/.opencode/skills/atlas-workflow-orchestrator/SKILL.md +24 -17
  36. package/hosts/pi/.pi/agents/atlas-direct-execute.md +6 -2
  37. package/hosts/pi/.pi/agents/atlas-findings-repair.md +15 -1
  38. package/hosts/pi/.pi/agents/atlas-plan-execute.md +16 -2
  39. package/hosts/pi/.pi/agents/atlas-slice-review.md +37 -2
  40. package/hosts/pi/.pi/agents/atlas-task-validator.md +18 -1
  41. package/hosts/pi/atlas/VERSION +1 -1
  42. package/hosts/pi/atlas/orchestrator/README.md +7 -5
  43. package/hosts/pi/atlas/orchestrator/commands/workflow.md +1 -1
  44. package/hosts/pi/atlas/orchestrator/references/host-adapters.md +13 -12
  45. package/hosts/pi/atlas/orchestrator/skills/atlas-workflow-orchestrator/SKILL.md +24 -17
  46. package/hosts/pi/atlas/packages/mcp-server/README.md +1 -1
  47. package/hosts/pi/atlas/packages/mcp-server/package.json +1 -1
  48. package/hosts/pi/atlas/packages/mcp-server/server.js +514 -20
  49. package/hosts/pi/atlas/packages/templates/BACKLOG_MESTRE_TEMPLATE.md +14 -3
  50. package/hosts/pi/atlas/packages/templates/PRD_TEMPLATE.md +2 -1
  51. package/hosts/pi/atlas/packages/templates/STATE_FILE_SCHEMA.md +25 -1
  52. package/hosts/pi/skills/_shared/references/stack-profiles.md +36 -0
  53. package/hosts/pi/skills/_shared/scripts/document_quality.mjs +252 -0
  54. package/hosts/pi/skills/atlas-backlog-generator/SKILL.md +7 -2
  55. package/hosts/pi/skills/atlas-direct-execute/SKILL.md +6 -2
  56. package/hosts/pi/skills/atlas-findings-repair/SKILL.md +11 -1
  57. package/hosts/pi/skills/atlas-plan-execute/SKILL.md +16 -2
  58. package/hosts/pi/skills/atlas-plan-handoff/SKILL.md +6 -4
  59. package/hosts/pi/skills/atlas-prd-interview/SKILL.md +7 -2
  60. package/hosts/pi/skills/atlas-slice-review/SKILL.md +37 -2
  61. package/hosts/pi/skills/atlas-slice-review/references/scenario-lenses.md +8 -0
  62. package/hosts/pi/skills/atlas-slice-review/scripts/classify_findings.mjs +60 -0
  63. package/hosts/pi/skills/atlas-slice-review/scripts/classify_findings.py +9 -41
  64. package/hosts/pi/skills/atlas-sprint-prd-generator/SKILL.md +7 -4
  65. package/hosts/pi/skills/atlas-task-validator/SKILL.md +29 -14
  66. package/hosts/pi/skills/atlas-workflow-orchestrator/SKILL.md +24 -17
  67. package/hosts/zcode/.zcode-plugin/plugin.json +27 -0
  68. package/hosts/zcode/agents/atlas-direct-execute.md +31 -0
  69. package/hosts/zcode/agents/atlas-findings-repair.md +39 -0
  70. package/hosts/zcode/agents/atlas-plan-execute.md +33 -0
  71. package/hosts/zcode/agents/atlas-slice-review.md +27 -0
  72. package/hosts/zcode/agents/atlas-task-validator.md +138 -0
  73. package/hosts/zcode/packages/mcp-server/README.md +29 -0
  74. package/hosts/zcode/packages/mcp-server/VERSION +1 -0
  75. package/hosts/zcode/packages/mcp-server/package.json +15 -0
  76. package/hosts/zcode/packages/mcp-server/server.js +3897 -0
  77. package/hosts/zcode/packages/orchestrator/README.md +270 -0
  78. package/hosts/zcode/packages/orchestrator/commands/workflow.md +37 -0
  79. package/hosts/zcode/packages/orchestrator/defaults/paths.md +21 -0
  80. package/hosts/zcode/packages/orchestrator/references/host-adapters.md +106 -0
  81. package/hosts/zcode/packages/orchestrator/references/qa_s13_matrix.md +141 -0
  82. package/hosts/zcode/packages/orchestrator/references/subagent_dispatch.md +42 -0
  83. package/hosts/zcode/packages/orchestrator/skills/atlas-workflow-orchestrator/SKILL.md +391 -0
  84. package/hosts/zcode/packages/templates/BACKLOG_MESTRE_TEMPLATE.md +855 -0
  85. package/hosts/zcode/packages/templates/BOUNDARY_PRD_PLAN.md +93 -0
  86. package/hosts/zcode/packages/templates/PERGUNTAS_EM_ABERTO_TEMPLATE.md +139 -0
  87. package/hosts/zcode/packages/templates/PLAN_TEMPLATE.md +146 -0
  88. package/hosts/zcode/packages/templates/PRD_TEMPLATE.md +150 -0
  89. package/hosts/zcode/packages/templates/STATE_FILE_SCHEMA.md +56 -0
  90. package/hosts/zcode/skills/_shared/references/stack-profiles.md +36 -0
  91. package/hosts/zcode/skills/_shared/scripts/document_quality.mjs +252 -0
  92. package/hosts/zcode/skills/atlas-backlog-generator/SKILL.md +93 -0
  93. package/hosts/zcode/skills/atlas-backlog-generator/agents/openai.yaml +4 -0
  94. package/hosts/zcode/skills/atlas-direct-execute/SKILL.md +221 -0
  95. package/hosts/zcode/skills/atlas-direct-execute/agents/openai.yaml +7 -0
  96. package/hosts/zcode/skills/atlas-findings-repair/SKILL.md +158 -0
  97. package/hosts/zcode/skills/atlas-findings-repair/agents/openai.yaml +7 -0
  98. package/hosts/zcode/skills/atlas-plan-execute/SKILL.md +175 -0
  99. package/hosts/zcode/skills/atlas-plan-execute/agents/openai.yaml +7 -0
  100. package/hosts/zcode/skills/atlas-plan-execute/references/plan-contract.md +88 -0
  101. package/hosts/zcode/skills/atlas-plan-execute/references/quality-gates.md +60 -0
  102. package/hosts/zcode/skills/atlas-plan-execute/scripts/check_budget_state.py +96 -0
  103. package/hosts/zcode/skills/atlas-plan-execute/scripts/extract_plan_contract.py +191 -0
  104. package/hosts/zcode/skills/atlas-plan-execute/scripts/validate_gate_result.py +56 -0
  105. package/hosts/zcode/skills/atlas-plan-handoff/SKILL.md +183 -0
  106. package/hosts/zcode/skills/atlas-plan-handoff/agents/openai.yaml +7 -0
  107. package/hosts/zcode/skills/atlas-prd-interview/SKILL.md +82 -0
  108. package/hosts/zcode/skills/atlas-prd-interview/agents/openai.yaml +7 -0
  109. package/hosts/zcode/skills/atlas-slice-review/SKILL.md +156 -0
  110. package/hosts/zcode/skills/atlas-slice-review/agents/openai.yaml +4 -0
  111. package/hosts/zcode/skills/atlas-slice-review/references/review-contract.md +58 -0
  112. package/hosts/zcode/skills/atlas-slice-review/references/scenario-lenses.md +57 -0
  113. package/hosts/zcode/skills/atlas-slice-review/scripts/classify_findings.mjs +60 -0
  114. package/hosts/zcode/skills/atlas-slice-review/scripts/classify_findings.py +24 -0
  115. package/hosts/zcode/skills/atlas-slice-review/scripts/extract_review_slice.py +158 -0
  116. package/hosts/zcode/skills/atlas-sprint-prd-generator/SKILL.md +77 -0
  117. package/hosts/zcode/skills/atlas-sprint-prd-generator/agents/openai.yaml +7 -0
  118. package/hosts/zcode/skills/atlas-task-validator/SKILL.md +173 -0
  119. package/hosts/zcode/skills/atlas-task-validator/agents/openai.yaml +7 -0
  120. package/hosts/zcode/skills/atlas-workflow-orchestrator/SKILL.md +391 -0
  121. package/package.json +1 -1
  122. package/plugins/atlas-workflow-orchestrator/.codex/agents/atlas-findings-repair.toml +1 -1
  123. package/plugins/atlas-workflow-orchestrator/.codex/agents/atlas-task-validator.toml +1 -1
  124. package/plugins/atlas-workflow-orchestrator/.codex-plugin/plugin.json +1 -1
  125. package/plugins/atlas-workflow-orchestrator/VERSION +1 -1
  126. package/plugins/atlas-workflow-orchestrator/agents/atlas-findings-repair.md +4 -0
  127. package/plugins/atlas-workflow-orchestrator/agents/atlas-task-validator.md +18 -1
  128. package/plugins/atlas-workflow-orchestrator/orchestrator/README.md +7 -5
  129. package/plugins/atlas-workflow-orchestrator/orchestrator/commands/workflow.md +1 -1
  130. package/plugins/atlas-workflow-orchestrator/orchestrator/references/host-adapters.md +13 -12
  131. package/plugins/atlas-workflow-orchestrator/orchestrator/skills/atlas-workflow-orchestrator/SKILL.md +24 -17
  132. package/plugins/atlas-workflow-orchestrator/packages/mcp-server/README.md +1 -1
  133. package/plugins/atlas-workflow-orchestrator/packages/mcp-server/package.json +1 -1
  134. package/plugins/atlas-workflow-orchestrator/packages/mcp-server/server.js +514 -20
  135. package/plugins/atlas-workflow-orchestrator/packages/skills/_shared/references/stack-profiles.md +36 -0
  136. package/plugins/atlas-workflow-orchestrator/packages/skills/_shared/scripts/document_quality.mjs +252 -0
  137. package/plugins/atlas-workflow-orchestrator/packages/skills/atlas-backlog-generator/SKILL.md +7 -2
  138. package/plugins/atlas-workflow-orchestrator/packages/skills/atlas-direct-execute/SKILL.md +6 -2
  139. package/plugins/atlas-workflow-orchestrator/packages/skills/atlas-findings-repair/SKILL.md +11 -1
  140. package/plugins/atlas-workflow-orchestrator/packages/skills/atlas-plan-execute/SKILL.md +16 -2
  141. package/plugins/atlas-workflow-orchestrator/packages/skills/atlas-plan-handoff/SKILL.md +6 -4
  142. package/plugins/atlas-workflow-orchestrator/packages/skills/atlas-prd-interview/SKILL.md +7 -2
  143. package/plugins/atlas-workflow-orchestrator/packages/skills/atlas-slice-review/SKILL.md +37 -2
  144. package/plugins/atlas-workflow-orchestrator/packages/skills/atlas-slice-review/references/scenario-lenses.md +8 -0
  145. package/plugins/atlas-workflow-orchestrator/packages/skills/atlas-slice-review/scripts/classify_findings.mjs +60 -0
  146. package/plugins/atlas-workflow-orchestrator/packages/skills/atlas-slice-review/scripts/classify_findings.py +9 -41
  147. package/plugins/atlas-workflow-orchestrator/packages/skills/atlas-sprint-prd-generator/SKILL.md +7 -4
  148. package/plugins/atlas-workflow-orchestrator/packages/skills/atlas-task-validator/SKILL.md +29 -14
  149. package/plugins/atlas-workflow-orchestrator/packages/templates/BACKLOG_MESTRE_TEMPLATE.md +14 -3
  150. package/plugins/atlas-workflow-orchestrator/packages/templates/PRD_TEMPLATE.md +2 -1
  151. package/plugins/atlas-workflow-orchestrator/packages/templates/STATE_FILE_SCHEMA.md +25 -1
  152. package/plugins/atlas-workflow-orchestrator/skills/_shared/references/stack-profiles.md +36 -0
  153. package/plugins/atlas-workflow-orchestrator/skills/_shared/scripts/document_quality.mjs +252 -0
  154. package/plugins/atlas-workflow-orchestrator/skills/atlas-backlog-generator/SKILL.md +7 -2
  155. package/plugins/atlas-workflow-orchestrator/skills/atlas-direct-execute/SKILL.md +6 -2
  156. package/plugins/atlas-workflow-orchestrator/skills/atlas-findings-repair/SKILL.md +11 -1
  157. package/plugins/atlas-workflow-orchestrator/skills/atlas-plan-execute/SKILL.md +16 -2
  158. package/plugins/atlas-workflow-orchestrator/skills/atlas-plan-handoff/SKILL.md +6 -4
  159. package/plugins/atlas-workflow-orchestrator/skills/atlas-prd-interview/SKILL.md +7 -2
  160. package/plugins/atlas-workflow-orchestrator/skills/atlas-slice-review/SKILL.md +37 -2
  161. package/plugins/atlas-workflow-orchestrator/skills/atlas-slice-review/references/scenario-lenses.md +8 -0
  162. package/plugins/atlas-workflow-orchestrator/skills/atlas-slice-review/scripts/classify_findings.mjs +60 -0
  163. package/plugins/atlas-workflow-orchestrator/skills/atlas-slice-review/scripts/classify_findings.py +9 -41
  164. package/plugins/atlas-workflow-orchestrator/skills/atlas-sprint-prd-generator/SKILL.md +7 -4
  165. package/plugins/atlas-workflow-orchestrator/skills/atlas-task-validator/SKILL.md +29 -14
  166. package/plugins/atlas-workflow-orchestrator/skills/atlas-workflow-orchestrator/SKILL.md +24 -17
  167. package/plugins/atlas-workflow-orchestrator/templates/BACKLOG_MESTRE_TEMPLATE.md +14 -3
  168. package/plugins/atlas-workflow-orchestrator/templates/PRD_TEMPLATE.md +2 -1
  169. package/plugins/atlas-workflow-orchestrator/templates/STATE_FILE_SCHEMA.md +25 -1
@@ -0,0 +1,36 @@
1
+ # Baseline universal e perfis de stack
2
+
3
+ Ativação é determinística: inspecione manifests no boundary e comandos realmente declarados no repo/plano. Use `detectStackProfiles(project_root, declared_commands, boundary_paths)`; consuma o array `boundaries` e não deduza stack por extensão isolada.
4
+
5
+ ## Baseline universal — sempre ativo
6
+
7
+ - segurança, autenticação/autorização e dados sensíveis;
8
+ - boundary real, contratos, schemas e consumidores afetados;
9
+ - erros, falhas parciais, concorrência, idempotência e reentrada;
10
+ - setup/cleanup, recursos, estado stale e persistência;
11
+ - integridade de dados, nulos, enums, validação de input;
12
+ - checks declarados pelo repo/plano, sem inventar ferramenta.
13
+
14
+ ## Flutter/Dart — `pubspec.yaml` ou comando `flutter`/`dart`
15
+
16
+ - lifecycle de Widget/Controller/Store, dispose/reset e async stale;
17
+ - rotas literais antes de parametrizadas quando o roteador exigir;
18
+ - args obrigatórios, null-safety, casts defensivos, coleções `?? []`;
19
+ - l10n em todos os locales e geração limpa;
20
+ - `flutter analyze`/`flutter test` somente quando declarados/aplicáveis;
21
+ - GetX apenas quando dependência/import/regras reais do repo confirmarem GetX.
22
+
23
+ ## Node/TypeScript — `package.json`, `tsconfig.json` ou comando Node real
24
+
25
+ - lifecycle de processos/handles, abort/cleanup e promises rejeitadas;
26
+ - validação runtime nas fronteiras JSON/HTTP/MCP;
27
+ - ESM/CJS, exports, tipos e scripts realmente declarados;
28
+ - `node --test`, test runner, lint/typecheck apenas se presentes no repo/plano.
29
+
30
+ ## Python — `pyproject.toml`, `requirements.txt`, `setup.py` ou comando Python real
31
+
32
+ - context managers, cleanup, exceções e async/cancelamento;
33
+ - parsing/typing nas fronteiras e mutabilidade de defaults;
34
+ - `pytest`, `ruff`, `mypy` ou equivalente apenas se declarados.
35
+
36
+ Perfis podem coexistir em monorepo. Regra de perfil nunca vira finding fora do boundary onde seu sinal foi ativado.
@@ -0,0 +1,252 @@
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 = ['package.json', 'tsconfig.json', 'pubspec.yaml', 'pyproject.toml', 'requirements.txt', 'setup.py'];
14
+
15
+ function boundaryRoot(projectRoot, boundary) {
16
+ const project = path.resolve(projectRoot);
17
+ let current = path.resolve(project, boundary ?? '.');
18
+ const relative = path.relative(project, current);
19
+ if (relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
20
+ throw new Error(`BOUNDARY_OUTSIDE_PROJECT:${boundary}`);
21
+ }
22
+ if (fs.existsSync(current) && fs.statSync(current).isFile()) current = path.dirname(current);
23
+ while (current === project || current.startsWith(`${project}${path.sep}`)) {
24
+ if (STACK_MANIFESTS.some((name) => fs.existsSync(path.join(current, name)))) return current;
25
+ if (current === project) break;
26
+ current = path.dirname(current);
27
+ }
28
+ return path.resolve(project, boundary ?? '.');
29
+ }
30
+
31
+ function containsGetxImport(root) {
32
+ const queue = ['lib', 'test'].map((name) => path.join(root, name)).filter((dir) => fs.existsSync(dir));
33
+ let inspected = 0;
34
+ while (queue.length > 0 && inspected < 5000) {
35
+ const current = queue.pop();
36
+ for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
37
+ const target = path.join(current, entry.name);
38
+ if (entry.isDirectory()) queue.push(target);
39
+ else if (entry.isFile() && entry.name.endsWith('.dart')) {
40
+ inspected += 1;
41
+ if (/package:get\/get(?:_core)?\.dart/.test(fs.readFileSync(target, 'utf8'))) return true;
42
+ }
43
+ }
44
+ }
45
+ return false;
46
+ }
47
+
48
+ function detectBoundaryProfile(root, declaredCommands) {
49
+ const exists = (name) => fs.existsSync(path.join(root, name));
50
+ const commands = declaredCommands.filter((v) => typeof v === 'string');
51
+ let packageJson = null;
52
+ if (exists('package.json')) {
53
+ try { packageJson = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')); } catch {}
54
+ }
55
+ let pubspec = '';
56
+ if (exists('pubspec.yaml')) {
57
+ try { pubspec = fs.readFileSync(path.join(root, 'pubspec.yaml'), 'utf8'); } catch {}
58
+ }
59
+ const packageCommands = Object.values(packageJson?.scripts ?? {}).filter((v) => typeof v === 'string');
60
+ const allCommands = [...commands, ...packageCommands];
61
+ const hasCommand = (re) => allCommands.some((command) => re.test(command));
62
+ return {
63
+ universal: true,
64
+ flutter_dart: exists('pubspec.yaml') || hasCommand(/\b(flutter|dart)\b/),
65
+ node_typescript: exists('package.json') || exists('tsconfig.json') || hasCommand(/\b(node|npm|pnpm|yarn|bun|tsc)\b/),
66
+ python: exists('pyproject.toml') || exists('requirements.txt') || exists('setup.py') || hasCommand(/\b(python3?|pytest|ruff|mypy)\b/),
67
+ getx: /^\s{0,4}get\s*:/m.test(pubspec) || containsGetxImport(root),
68
+ };
69
+ }
70
+
71
+ export function detectStackProfiles(root, declaredCommands = [], boundaryPaths = ['.']) {
72
+ const project = path.resolve(root);
73
+ const requested = boundaryPaths.length > 0 ? boundaryPaths : ['.'];
74
+ const boundaries = [...new Set(requested.map((boundary) => boundaryRoot(project, boundary)))].map((dir) => ({
75
+ boundary: path.relative(project, dir).replaceAll('\\', '/') || '.',
76
+ ...detectBoundaryProfile(dir, declaredCommands),
77
+ }));
78
+ return {
79
+ universal: true,
80
+ flutter_dart: boundaries.some((profile) => profile.flutter_dart),
81
+ node_typescript: boundaries.some((profile) => profile.node_typescript),
82
+ python: boundaries.some((profile) => profile.python),
83
+ getx: boundaries.some((profile) => profile.getx),
84
+ boundaries,
85
+ };
86
+ }
87
+
88
+ function parseTable(markdown, heading) {
89
+ const start = markdown.indexOf(heading);
90
+ if (start < 0) return [];
91
+ const tail = markdown.slice(start + heading.length);
92
+ const end = tail.search(/\n##?\s/);
93
+ return (end < 0 ? tail : tail.slice(0, end)).split('\n')
94
+ .filter((line) => /^\|.*\|\s*$/.test(line))
95
+ .map((line) => line.split('|').slice(1, -1).map((cell) => cell.trim()))
96
+ .filter((cells) => cells.length && !cells.every((cell) => /^-+$/.test(cell)));
97
+ }
98
+
99
+ export function parseSprintRows(markdown) {
100
+ const rows = parseTable(markdown, '## 7. Registro de sprints');
101
+ const header = rows.findIndex((row) => row[0] === 'ID');
102
+ return rows.slice(header + 1).filter((row) => /^S\d{2}[a-z]?$/.test(row[0])).map((row) => ({
103
+ id: row[0], name: row[1], phase: row[2], objective: row[3], moscow: row[4], gain: row[5].toLowerCase(),
104
+ effort: row[6].toLowerCase(), priority: row[7], prd: row[8], dependencies: row[9], state: row[10], gate: row[11], raw: row,
105
+ }));
106
+ }
107
+
108
+ export function parseDecisionRows(markdown) {
109
+ const rows = parseTable(markdown, '### Decisões bloqueantes');
110
+ const header = rows.findIndex((row) => row[0] === 'ID');
111
+ return rows.slice(header + 1).filter((row) => /^D\d+$/.test(row[0])).map((row) => ({
112
+ id: row[0], decision: row[1], blocks: row[2], owner: row[3], status: row[4], raw: row,
113
+ }));
114
+ }
115
+
116
+ function dependencyIds(value) {
117
+ if (!value || value === '—') return [];
118
+ return [...value.matchAll(/S\d{2}[a-z]?/g)].map((match) => match[0]);
119
+ }
120
+
121
+ function findCycle(rows) {
122
+ const graph = new Map(rows.map((row) => [row.id, dependencyIds(row.dependencies)]));
123
+ const visiting = new Set(); const visited = new Set();
124
+ const walk = (id, chain = []) => {
125
+ if (visiting.has(id)) return [...chain.slice(chain.indexOf(id)), id];
126
+ if (visited.has(id)) return null;
127
+ visiting.add(id);
128
+ for (const dep of graph.get(id) ?? []) {
129
+ const cycle = walk(dep, [...chain, id]);
130
+ if (cycle) return cycle;
131
+ }
132
+ visiting.delete(id); visited.add(id); return null;
133
+ };
134
+ for (const id of graph.keys()) { const cycle = walk(id); if (cycle) return cycle; }
135
+ return null;
136
+ }
137
+
138
+ function changeLogBody(markdown) {
139
+ const match = /^##\s+(?:Registro de alterações|Histórico de alterações)\s*$/im.exec(markdown);
140
+ if (!match) return null;
141
+ const tail = markdown.slice(match.index + match[0].length);
142
+ const end = tail.search(/\n##\s+/);
143
+ return (end < 0 ? tail : tail.slice(0, end)).trim();
144
+ }
145
+
146
+ export function validateBacklogUpdate(before, after, { authorizedIds = [] } = {}) {
147
+ const errors = [];
148
+ const authorized = new Set(authorizedIds);
149
+ const oldRows = parseSprintRows(before); const newRows = parseSprintRows(after);
150
+ const oldById = new Map(oldRows.map((row) => [row.id, row]));
151
+ const newById = new Map(newRows.map((row) => [row.id, row]));
152
+ if (oldById.size !== oldRows.length) errors.push('DUPLICATE_SPRINT_ID_BEFORE');
153
+ if (newById.size !== newRows.length) errors.push('DUPLICATE_SPRINT_ID_AFTER');
154
+ for (const [id, oldRow] of oldById) {
155
+ const next = newById.get(id);
156
+ if (!next) errors.push(`SPRINT_REMOVED:${id}`);
157
+ else if (JSON.stringify(oldRow.raw) !== JSON.stringify(next.raw) && !authorized.has(id)) {
158
+ errors.push(oldRow.state === 'done' ? `DONE_SPRINT_CHANGED:${id}` : `UNAUTHORIZED_SPRINT_CHANGED:${id}`);
159
+ }
160
+ }
161
+ const oldDecisions = new Map(parseDecisionRows(before).map((row) => [row.id, row]));
162
+ const newDecisions = new Map(parseDecisionRows(after).map((row) => [row.id, row]));
163
+ for (const [id, row] of oldDecisions) {
164
+ const next = newDecisions.get(id);
165
+ if (!next) errors.push(`DECISION_REMOVED:${id}`);
166
+ else if (/^(decidido|fechado|aprovado)$/i.test(row.status)
167
+ && JSON.stringify(row.raw) !== JSON.stringify(next.raw) && !authorized.has(id)) errors.push(`CLOSED_DECISION_CHANGED:${id}`);
168
+ }
169
+ for (const row of newRows) {
170
+ for (const [field, values] of Object.entries(VALID)) if (!values.has(row[field])) errors.push(`INVALID_ENUM:${row.id}:${field}:${row[field]}`);
171
+ for (const dependency of dependencyIds(row.dependencies)) {
172
+ if (!newById.has(dependency)) errors.push(`DEPENDENCY_NOT_FOUND:${row.id}:${dependency}`);
173
+ }
174
+ }
175
+ const cycle = findCycle(newRows); if (cycle) errors.push(`DEPENDENCY_CYCLE:${cycle.join('>')}`);
176
+ if (/\[(?:NOME_|RESULTADO_|observação|decisão|slug|pendente\])/i.test(after)) errors.push('UNRESOLVED_PLACEHOLDER');
177
+ if (before !== after) {
178
+ const oldLog = changeLogBody(before);
179
+ const newLog = changeLogBody(after);
180
+ if (newLog === null) errors.push('CHANGELOG_REQUIRED');
181
+ else if (oldLog !== null && !newLog.startsWith(oldLog)) errors.push('CHANGELOG_REWRITTEN');
182
+ else if (newLog === oldLog) errors.push('CHANGELOG_ENTRY_REQUIRED');
183
+ }
184
+ return { valid: errors.length === 0, errors };
185
+ }
186
+
187
+ export function resolveSprintAuthority({ sprintId, explicitPath, canonicalPath, candidates }) {
188
+ const normalized = candidates.map((candidate) => ({ ...candidate, path: path.resolve(candidate.path) }));
189
+ const byPath = (target) => normalized.find((candidate) => candidate.path === path.resolve(target));
190
+ if (explicitPath) {
191
+ const match = byPath(explicitPath); if (!match?.sprints.includes(sprintId)) throw new Error(`SPRINT_NOT_FOUND_IN_EXPLICIT_PATH:${sprintId}`); return match;
192
+ }
193
+ if (canonicalPath) {
194
+ const match = byPath(canonicalPath); if (!match?.sprints.includes(sprintId)) throw new Error(`SPRINT_NOT_FOUND_IN_CANONICAL_BACKLOG:${sprintId}`); return match;
195
+ }
196
+ const matches = normalized.filter((candidate) => candidate.sprints.includes(sprintId));
197
+ if (matches.length !== 1) throw new Error(matches.length ? `AMBIGUOUS_BACKLOG_AUTHORITY:${matches.map((m) => m.path).join(',')}` : `SPRINT_NOT_FOUND:${sprintId}`);
198
+ return matches[0];
199
+ }
200
+
201
+ export function closedDecisionIds(prd) {
202
+ return new Set([...prd.matchAll(/^\|\s*(D\d+)\s*\|\s*(?!<|\[)(.+?)\s*\|\s*$/gm)].map((match) => match[1]));
203
+ }
204
+
205
+ export function pendingInterviewQuestions(prd, questions) {
206
+ const closed = closedDecisionIds(prd);
207
+ return questions.filter((question) => !closed.has(question.decision_id));
208
+ }
209
+
210
+ export function applyInterviewRound(prd, answers, date = new Date().toISOString().slice(0, 10)) {
211
+ const ids = new Set();
212
+ for (const answer of answers) {
213
+ if (!answer || typeof answer.decision_id !== 'string' || !/^D\d+$/.test(answer.decision_id)) throw new Error('INVALID_DECISION_ID');
214
+ if (ids.has(answer.decision_id)) throw new Error(`DUPLICATE_DECISION_ID:${answer.decision_id}`);
215
+ if (typeof answer.value !== 'string' || !answer.value.trim()) throw new Error(`EMPTY_DECISION_VALUE:${answer.decision_id}`);
216
+ ids.add(answer.decision_id);
217
+ }
218
+ let updated = prd;
219
+ for (const answer of answers) {
220
+ const row = new RegExp(`^\\|\\s*${answer.decision_id}\\s*\\|.*$`, 'm');
221
+ const replacement = `| ${answer.decision_id} | ${answer.value} |`;
222
+ updated = row.test(updated) ? updated.replace(row, replacement) : updated.replace(/(\| ID \| Decisão \|\n\|[-| ]+\|)/, `$1\n${replacement}`);
223
+ }
224
+ const log = `${date} — entrevista: ${answers.map((answer) => answer.decision_id).join(', ')} persistida(s)`;
225
+ updated = /\*\*Histórico:\*\*/.test(updated)
226
+ ? updated.replace(/(\*\*Histórico:\*\*[^\n]*)/, `$1 · ${log}`)
227
+ : `${updated.trimEnd()}\n\n**Histórico:** ${log}\n`;
228
+ return updated;
229
+ }
230
+
231
+ export function persistInterviewRound(prdPath, answers, date = new Date().toISOString().slice(0, 10)) {
232
+ const absolute = path.resolve(prdPath);
233
+ const temporary = path.join(path.dirname(absolute), `.${path.basename(absolute)}.${process.pid}.${Date.now()}.tmp`);
234
+ try {
235
+ const current = fs.readFileSync(absolute, 'utf8');
236
+ const updated = applyInterviewRound(current, answers, date);
237
+ const materialized = closedDecisionIds(updated);
238
+ const missingBeforeWrite = answers.filter((answer) => !materialized.has(answer.decision_id));
239
+ if (missingBeforeWrite.length > 0) {
240
+ throw new Error(`DECISION_NOT_MATERIALIZED:${missingBeforeWrite.map((answer) => answer.decision_id).join(',')}`);
241
+ }
242
+ const mode = fs.statSync(absolute).mode;
243
+ fs.writeFileSync(temporary, updated, { encoding: 'utf8', mode });
244
+ fs.renameSync(temporary, absolute);
245
+ const readback = fs.readFileSync(absolute, 'utf8');
246
+ if (readback !== updated) throw new Error('READBACK_DIVERGENT');
247
+ return readback;
248
+ } catch (error) {
249
+ try { fs.rmSync(temporary, { force: true }); } catch {}
250
+ throw new Error(`INTERVIEW_PERSISTENCE_FAILED:${error.message}`);
251
+ }
252
+ }
@@ -33,8 +33,10 @@ Se faltar informação não bloqueante, gere o backlog com premissas marcadas e
33
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
34
  7. **Priorizar:** para cada sprint, preencha MoSCoW, ganho, esforço e prioridade usando a regra da seção 8.3 do template.
35
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. **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`.
37
- 10. **Validar antes de finalizar:** releia o arquivo salvo e confirme que não restaram placeholders acidentais, exceto campos conscientemente pendentes e marcados.
36
+ 9. **Atualização não destrutiva:** se o arquivo 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.
38
40
 
39
41
  ---
40
42
 
@@ -76,6 +78,7 @@ O backlog final deve:
76
78
  - Ter catálogo de fases preservado e adaptado apenas quando necessário.
77
79
  - Ter riscos, decisões e próxima sprint executável preenchidos.
78
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.
79
82
 
80
83
  ---
81
84
 
@@ -86,3 +89,5 @@ O backlog final deve:
86
89
  - Não inventar endpoints, tabelas, schemas, fornecedores, métricas ou responsabilidades como fatos. Quando forem hipóteses, marcar como premissa.
87
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.
88
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.
@@ -149,6 +149,8 @@ For each task, keep a tiny task contract:
149
149
 
150
150
  Do not widen scope for opportunistic cleanup.
151
151
 
152
+ **Minimalism rung (per task, before writing):** prefer the minimal viable implementation that satisfies the obligation — reuse existing repo code/symbol before introducing a new abstraction; use a stdlib/native platform feature before a new dependency; avoid indirection, factory, wrapper, extra layer, config option, or extra file not required by an obligation or invariant. This rung constrains only new abstraction/indirection/file/dependency. It never reduces trust-boundary validation, error handling, data-loss handling, invariants, scenario/test coverage, or negative paths. When minimal and safe conflict, choose safe.
153
+
152
154
  Before the first concrete task, emit `task_started`. After the first workspace mutation, emit `first_write`.
153
155
 
154
156
  ### 4. Gate each task
@@ -175,6 +177,8 @@ After tasks and local gates pass, write `.atlas/state/<run_id>/<slice>.json` fol
175
177
 
176
178
  For direct execution, the state file is still the only validator input. Use the user-provided PRD/spec path as `plan_path` when no handoff plan exists, and include direct-contract anchors in `boundary_refs` such as `direct.O1`, `direct.invariant.permissions`, or `direct.risk.partial_failure`.
177
179
 
180
+ Persist the full direct contract using the additive state extension: `base_sha`, `head_sha`, `contract_kind: direct`, non-empty `obligations`, `invariants`, `scenario_probes`, `risk_probes`, `validation_map`, `task_evidence`, empty `repair_evidence`, `worktree_baseline` and `worktree_final`. Capture baseline before the first mutation and final immediately before handoff; `files_changed`/evidence must equal `base_sha...head_sha` + snapshot delta. Capture base from an explicit task/spec anchor or execution-start `HEAD`; never infer it from branch name. Recompute `head_sha` and `diff_stat` immediately before handoff. A direct state without obligations is invalid and must block.
181
+
178
182
  The state file is the only validator input. Validation is always **sibling**, on every host: this executor **never** dispatches `atlas-task-validator` itself and never validates its own work in the same context. After tasks and local gates pass and the state file is written, this executor **stops mutation** and returns `validator_handoff_required` with the `state_path`. The orchestrator then dispatches `atlas-task-validator` as the next isolated sibling phase, locks it via `atlas_lock_validator`, and — if the verdict is `fail` — dispatches `atlas-findings-repair` (not this executor) before the **2nd and last** validator.
179
183
 
180
184
  After writing the state file and before returning, emit `state_path_created` with the same `state_path`.
@@ -190,7 +194,7 @@ The verdict is consumed by the **orchestrator**, not by this executor:
190
194
 
191
195
  This executor only re-engages if the orchestrator explicitly re-dispatches it for a new slice. It must not "fix" observations and reopen a closed slice; real follow-up from an observation goes to the final report or backlog, not into an extra in-slice change.
192
196
 
193
- If isolated subagents are unavailable in the current environment, do not pretend the slice is validator-closed. Run a local self-check against the same contract, report `validator not run`, and mark residual risk explicitly.
197
+ If isolated subagents or MCP are unavailable, return `blocked` with the missing prerequisite and next safe action. Never replace cold validation with a local self-check or report `validator not run` as an accepted pipeline outcome.
194
198
 
195
199
  ## Stop Conditions
196
200
 
@@ -211,7 +215,7 @@ Keep final report short:
211
215
  - changed scope
212
216
  - files touched
213
217
  - validations run
214
- - validator verdict/cycles
218
+ - `validator_handoff_required` + `state_path`
215
219
  - blockers or residual risks
216
220
 
217
221
  Do not include the full internal contract unless the user asks.
@@ -49,6 +49,7 @@ Leia `atlas_run_state` como fonte primária do estado da run. O `state_path` con
49
49
  5. **Não despachar validator, review ou qualquer subagente.** O orquestrador faz isso.
50
50
  6. **Não iniciar terceiro ciclo.** Esta skill existe só entre validator 1 e validator 2.
51
51
  7. **Não trocar o `state_path`.** Atualize o arquivo original em lugar; redirecionar o boundary invalida a correlação do repair.
52
+ 8. **Não inventar correlação.** IDs devem existir no packet recebido, sem duplicatas; todo arquivo tocado pertence a pelo menos um `repair_evidence` recebido e nenhum arquivo extra é permitido.
52
53
 
53
54
  ## Fluxo
54
55
 
@@ -67,6 +68,8 @@ Leia do plano apenas o mínimo necessário:
67
68
  - Section 6 — contratos técnicos
68
69
  - Section 8 — checklist
69
70
 
71
+ Capture também `base_sha`, `head_sha`, `task_evidence`, `repair_evidence`, `worktree_baseline` e `worktree_final` do state.
72
+
70
73
  ### 2. Ler os findings recebidos
71
74
 
72
75
  Trabalhe somente com findings de severidade:
@@ -75,6 +78,8 @@ Trabalhe somente com findings de severidade:
75
78
  - `P1`
76
79
  - `P2`
77
80
 
81
+ Cada finding novo deve ter `id`, `failure_mode`, `evidence`, `recommendation` e `fix_validation`. `msg` é compatibilidade deprecated e não substitui esses campos.
82
+
78
83
  Se o pacote vier vazio, inconsistente ou sem finding reparável, pare em `blocked`.
79
84
 
80
85
  ### 3. Montar contrato mínimo de reparo
@@ -115,7 +120,11 @@ Se o finding persistir por falta de decisão de produto, dependência externa ou
115
120
 
116
121
  Ao terminar:
117
122
 
118
- - atualize o conteúdo do `state_path` original se a evidência do boundary mudou
123
+ - atualize `files_changed` com todo arquivo tocado, inclusive novo/adjacente
124
+ - recompute `head_sha` (`git rev-parse HEAD`) e `diff_stat`; preserve `base_sha`
125
+ - preserve `worktree_baseline` e recapture `worktree_final` após o repair; derive o boundary completo do delta entre snapshots
126
+ - acrescente `repair_evidence[]` no shape `{finding_id, files_touched, checks_run, status}`
127
+ - garanta que cada `repair_evidence.files_touched` esteja em `files_changed`
119
128
  - mantenha a mesma slice
120
129
  - não invente novo run state paralelo
121
130
 
@@ -128,6 +137,7 @@ Retorne saída curta e estruturada com:
128
137
  - `state_path`
129
138
  - `files_touched`
130
139
  - `checks_run`
140
+ - `repairs`: array `{finding_id, files_touched, checks_run, status: resolved|blocked}`
131
141
  - `residual_risk` (se houver)
132
142
 
133
143
  O orquestrador chamará `atlas_lock_validator(action=repair_complete, repair_run_id=..., state_path=<mesmo path original>)` e só então poderá despachar o validator final.
@@ -130,16 +130,30 @@ Create `.atlas/state/<run_id>/<slice>.json` following `packages/templates/STATE_
130
130
  {
131
131
  "run_id": "<run_id>",
132
132
  "slice": "<slice id>",
133
+ "base_sha": "<base commit explícito do plano/handoff>",
134
+ "head_sha": "<git rev-parse HEAD ao fechar a execução>",
135
+ "contract_kind": "plan",
133
136
  "tasks": ["T01"],
134
137
  "files_changed": ["relative/path.ext"],
135
138
  "diff_stat": "N files, +X -Y",
136
139
  "plan_path": ".atlas/plans/<id>.plan.md",
137
140
  "boundary_refs": ["§2.I1", "§6.1", "§8"],
141
+ "obligations": [],
142
+ "invariants": [{"id": "I1", "requirement": "<invariante>", "expected_evidence": ["<path/check>"]}],
143
+ "scenario_probes": [{"id": "S1", "scenario": "<cenário>", "expected": "<resultado>"}],
144
+ "risk_probes": [{"id": "R1", "risk": "<risco>", "probe": "<pergunta verificável>"}],
145
+ "validation_map": [{"obligation_ids": [], "checks": ["<comando>"], "status": "passed"}],
146
+ "task_evidence": [{"task": "T01", "files": ["relative/path.ext"], "checks": ["<comando>"], "result": "passed"}],
147
+ "repair_evidence": [],
148
+ "worktree_baseline": [{"path": "relative/preexisting.ext", "status": "M", "sha256": "<64 hex>"}],
149
+ "worktree_final": [{"path": "relative/preexisting.ext", "status": "M", "sha256": "<64 hex>"}],
138
150
  "executed_at": "ISO8601",
139
151
  "executor_skill": "atlas-plan-execute"
140
152
  }
141
153
  ```
142
154
 
155
+ Capture `base_sha` da referência explícita do plano/handoff; nunca infira pelo nome da branch. Antes da primeira mutação, capture `worktree_baseline`; imediatamente antes do handoff, capture `worktree_final`. `files_changed` e `task_evidence` representam exatamente `base_sha...head_sha` + delta entre snapshots. Dirty preexistente byte/status-idêntico fica fora; qualquer alteração posterior entra.
156
+
143
157
  Validation is always **sibling**, on every host. The validator is registered as a real subagent on every host, but this executor **never** dispatches it and never validates its own work. After tasks and local gates pass and the state file is written, this executor **stops mutation** and returns `validator_handoff_required` with the `state_path`. The orchestrator dispatches `atlas-task-validator` as the next isolated sibling phase, locks it via `atlas_lock_validator`, and — if the verdict is `fail` — dispatches `atlas-findings-repair` (not this executor) before the **2nd and last** validator.
144
158
 
145
159
  After writing the state file and before returning, emit `state_path_created` with the same `state_path`.
@@ -157,5 +171,5 @@ This executor does not parse the validator output — the **orchestrator** does,
157
171
 
158
172
  Never decide by substring matching prose. Once the slice is closed, do not edit code, tests, or boundary files just to satisfy an observation; that reopens the slice and forces an avoidable re-validation. Real follow-up from an observation goes to the final report or a backlog item, not into an extra in-slice change.
159
173
 
160
- ### 10. Report final outcome
161
- At the end of execution, report completed tasks, validations run, validator outcome, and any residual gaps.
174
+ ### 10. Report executor handoff
175
+ Report only completed tasks, local validations, files changed, and `validator_handoff_required` with `state_path`. Validator verdict/cycles and final residuals belong exclusively to the orchestrator's final report.
@@ -47,8 +47,8 @@ No workflow `full`, `atlas-plan-handoff` é autoria documental do agente princip
47
47
 
48
48
  ## Fluxo obrigatório
49
49
 
50
- 1. **Classificação da tarefa:** feature, ui, contract, navigation, shared, security, diagnostic, refactoring, testing. Leia `project-rules/index/<tipo>.md` e regras em `project-rules/rules/` (ou equivalente do repo ativo).
51
- 2. **Grounding no código:** confirme padrões, contratos e comandos locais (`flutter analyze` / `flutter test` com path do package) antes de inferir.
50
+ 1. **Classificação da tarefa:** feature, ui, contract, navigation, shared, security, diagnostic, refactoring, testing. Leia instruções reais aplicáveis do repo; `project-rules/` é apenas um formato possível, nunca requisito universal.
51
+ 2. **Grounding no código:** confirme padrões, contratos, manifests e comandos reais antes de inferir. Resolva baseline/perfis via `../_shared/references/stack-profiles.md` + `detectStackProfiles(project_root, declared_commands, boundary_paths)`; não presuma Flutter nem aplique perfil fora do package correspondente.
52
52
  3. **Decisões estáveis:** sanar bloqueios com perguntas ao usuário; registrar no plano (não recopiar tabela D* do PRD — referenciar `PRD §3`).
53
53
  4. **Escrita:** artefato markdown no path canônico `.atlas/plans/`. Teto orientativo ~250–350 linhas (até ~450 com slices).
54
54
 
@@ -112,7 +112,9 @@ Tarefas `#### T01.` … `#### TNN.` com schema de `BOUNDARY_PRD_PLAN.md` canôni
112
112
  - **Quality gates** (opcional em tasks críticas)
113
113
  - **Casos mínimos** (somente em tasks de teste)
114
114
 
115
- Última task típica: **Validação final** (`flutter analyze`, `flutter test`, passos manuais alinhados a **PRD §4–6**).
115
+ **Regra de minimalismo estrutural (autoria de task):** ao redigir `Mudança esperada`, prefira a forma mínima viável que cumpre o `Critério de done` reusar módulo/símbolo já existente no repo antes de introduzir nova abstração; usar stdlib/feature nativa antes de dependência nova; evitar indireção, factory, wrapper, camada ou opção de config não exigida por PRD/invariante. A regra recai **somente** sobre abstração/indireção/arquivo/dependência nova. **Nunca** reduz: validação de trust-boundary, error-handling, data-loss, invariantes §2, cobertura de cenário/teste e negative paths. Em dúvida entre enxuto e seguro, escolha seguro.
116
+
117
+ Última task típica: **Validação final** (checks reais da stack ativa e passos manuais alinhados a **PRD §4–6**). Flutter usa `flutter analyze/test`; Node e Python usam somente scripts/ferramentas declarados no repo/plano.
116
118
 
117
119
  ### 6. Contratos técnicos (só ambiguidade PRD → código)
118
120
 
@@ -126,7 +128,7 @@ Tarefas `#### T01.` … `#### TNN.` com schema de `BOUNDARY_PRD_PLAN.md` canôni
126
128
 
127
129
  - Critérios derivados de **PRD §6** + invariantes **§2** deste plano.
128
130
  - Título recomendado: `## 8. Validação e checklist (validator)`.
129
- - Comandos globais de analyze/test do package.
131
+ - Comandos globais aplicáveis ao package, derivados de manifests/scripts reais; nunca inventar `flutter`, `npm` ou `pytest`.
130
132
 
131
133
  ---
132
134
 
@@ -41,8 +41,11 @@ Ataque principalmente as seguintes seções do template de PRD:
41
41
  * **§5 Contrato funcional e invariantes:** `❌` se campos críticos não possuírem regras de formato (ex: decimais) ou se a regra de negócio for ambígua/impossível de verificar na codebase.
42
42
  * **§6 Critérios de aceite (negócio):** `❌` se o critério for subjetivo, não observável ou não testável.
43
43
 
44
- 3. **Perguntas por Rodada (AskUserQuestion):** Formule rodadas de no máximo 4 perguntas concisas via ferramenta nativa `AskUserQuestion`, com exatamente 3 opções e indicando a recomendada. **Pare o turno e aguarde a resposta.**
45
- 4. **Veredito Final:** emita o veredito de `Pronto para planejamento` quando zerar todos os `❌`.
44
+ 3. **Resolver mecanismo estruturado:** chame `atlas_capabilities`, leia `question_prompt` e use seu `mechanism`/shape. Nunca hardcode nome de ferramenta de host. Se o descriptor estiver ausente ou indisponível, bloqueie a rodada; não degrade para pergunta livre sem correlação.
45
+ 4. **Perguntas por rodada:** formule no máximo 4 perguntas concisas, exatamente 3 opções, recomendada explícita e `decision_id` D* estável. Antes de perguntar, use `pendingInterviewQuestions` de `../_shared/scripts/document_quality.mjs` para excluir decisões fechadas.
46
+ 5. **Persistência imediata:** ao receber respostas, grave-as no mesmo PRD antes de qualquer nova pergunta, preservando IDs/anchors e acrescentando histórico. Use `persistInterviewRound(prd_path, answers)`, que escreve via arquivo temporário + rename e valida readback; falha bloqueia. Nunca acumule respostas apenas no chat.
47
+ 6. **Reindexação:** releia o PRD salvo, reexecute o índice §3–§6 e recalcule perguntas pendentes. Decisão fechada não pode reaparecer em rodada posterior.
48
+ 7. **Veredito Final:** só emita `Pronto para planejamento` quando zerar todos os `❌`; no workflow, devolva controle ao orquestrador para reexecutar artifact/scan/TC.
46
49
 
47
50
  ---
48
51
 
@@ -55,6 +58,8 @@ Ataque principalmente as seguintes seções do template de PRD:
55
58
  §6 Aceite: ✅/⚠️/❌
56
59
  ```
57
60
 
61
+ O índice é materializado novamente após cada persistência; não reutilize índice anterior à resposta.
62
+
58
63
  ---
59
64
 
60
65
  ## Uso standalone vs protocolo interno no workflow (PRD D10/D11)
@@ -54,7 +54,7 @@ Base the review on three inputs:
54
54
 
55
55
  ### 1. Build the slice boundary first
56
56
  Before reviewing code, identify:
57
- * diff physical boundary (`git diff --name-only main...HEAD`).
57
+ * boundary físico do diff a partir do state/task ids; use a base configurada ou upstream e inclua mudanças não commitadas pertencentes à slice.
58
58
  * Section 2 - Invariants of Execution (contract).
59
59
  * Section 6 - Technical Contracts (signatures and shapes).
60
60
  * Section 8 - Validation and Checklist (QA criteria).
@@ -81,10 +81,43 @@ Ask what the implementation forgot:
81
81
  * **View & rendering:** inputs empty, null, partial, out of order, UI permission conditional.
82
82
  * **Contracts:** shape drift, enums, mappers, RLS server-side, i18n parity.
83
83
 
84
+ Aplique estes probes determinísticos a cada símbolo ou hunk alterado relevante:
85
+ * **Linha a linha:** leia cada hunk alterado e a função completa que o contém; construa entradas, estados, timings ou plataformas concretas capazes de provocar falha.
86
+ * **Comportamento removido:** para cada guard, validação, cleanup, error path ou teste removido/substituído, identifique o invariante protegido e prove onde o novo código o restabelece.
87
+ * **Rastreamento cross-file:** inspecione callers e callees quando assinaturas, shapes de retorno, erros, timing, ordem ou pré-condições mudarem.
88
+ * **Altitude:** confirme que a mudança corrige o componente proprietário do invariante, sem empilhar um caso especial local sobre um defeito compartilhado.
89
+ * **Regras aplicáveis:** inspecione arquivos de instruções do repo que governam os arquivos alterados. Reporte apenas violações exatas, com path da regra, texto da regra, linha violadora e impacto concreto.
90
+
91
+ Reuse, simplificação e eficiência só viram findings quando o diff atual cria custo comportamental, operacional ou de manutenção concreto. Não reporte preferências de estilo.
92
+
84
93
  ### 4. Distinguish current-diff findings from pre-existing issues
85
94
  Prefer findings attributable to the executed slice. Mark pre-existing issues as observations or separate notes to keep signals clean and actionable.
86
95
 
87
- ### 5. Output Expectations
96
+ ### 5. Verifique candidatos antes de reportar
97
+
98
+ Elimine duplicatas que descrevam o mesmo defeito no mesmo local. Classifique cada candidato restante como:
99
+ * `CONFIRMED` — evidência e cenário de falha alcançável sustentam o defeito.
100
+ * `REFUTED` — código, tipo, invariante ou guard prova que o candidato é falso ou já está tratado.
101
+ * `NEEDS_EVIDENCE` — o cenário é relevante, mas a evidência disponível não estabelece o defeito.
102
+
103
+ Apenas `CONFIRMED` vira finding. Descarte `REFUTED`. Mova `NEEDS_EVIDENCE` para `Perguntas Abertas ou Suposições`, sem apresentá-lo como defeito. Nunca mantenha um candidato apenas por ser plausível.
104
+
105
+ Antes de renderizar a saída, materialize os findings confirmados como JSON e execute o gate canônico Node `node scripts/classify_findings.mjs <findings.json>`. Cada item deve conter `severity`, `task_id`, `title`, `file`, `line`, `failure_mode`, `evidence`, `recommendation` e `fix_validation`. Saída não-zero bloqueia o relatório até o payload ser corrigido; é proibido ignorar o gate ou substituir campos ausentes por texto vazio. Array vazio é válido quando não há findings confirmados.
106
+
107
+ Node é o único requisito runtime deste gate e funciona em Linux/macOS/Windows. `scripts/classify_findings.py` permanece por uma release somente como wrapper compatível que delega ao Node; não é fonte canônica nem torna Python obrigatório.
108
+
109
+ ### 6. Recomende uma correção de causa raiz
110
+
111
+ Todo finding deve incluir exatamente uma recomendação principal de correção e uma validação que comprove a correção. A recomendação deve:
112
+ * atacar a causa raiz no componente proprietário do invariante violado;
113
+ * ser cirúrgica e permanecer no boundary revisado, salvo quando a evidência provar que o proprietário está fora dele;
114
+ * preservar contratos do plano, arquitetura e comportamento existente não implicado pelo finding;
115
+ * nomear concretamente componente, condição e comportamento esperado;
116
+ * ser a melhor correção sustentada pela evidência disponível, nunca uma alegação sem suporte de superioridade absoluta.
117
+
118
+ Não ofereça alternativas A/B. Não forneça patch completo nem altere código. Se a evidência for insuficiente para recomendar uma correção com segurança, classifique o candidato como `NEEDS_EVIDENCE` em vez de emitir finding.
119
+
120
+ ### 7. Output Expectations
88
121
 
89
122
  Return exactly this structure:
90
123
 
@@ -97,6 +130,8 @@ Return exactly this structure:
97
130
  - **Arquivo:** `relative/path.ext:line`
98
131
  - **Modo de falha:** [o que quebra e como]
99
132
  - **Evidência:** [o que suporta o finding]
133
+ - **Correção recomendada:** [uma correção cirúrgica na causa raiz]
134
+ - **Validação da correção:** [teste/check específico que comprova a resolução]
100
135
 
101
136
  ### P1 - <short title>
102
137
  [same shape]
@@ -34,6 +34,14 @@ Use these lenses to find hidden bugs in the executed slice. Apply only the relev
34
34
  - Is retry or re-entry behavior still coherent after this slice?
35
35
  - Did generated files, localization keys, imports, routes, RPC signatures, or schemas match the verified repo convention?
36
36
 
37
+ ## Mecânica da mudança
38
+
39
+ - Qual invariante cada guard, validação, cleanup, error path ou teste removido/substituído protegia, e onde ele foi restabelecido?
40
+ - Callers e callees alterados ainda concordam sobre pré-condições, shapes de retorno, erros, timing e ordem?
41
+ - A mudança corrige o componente proprietário do invariante ou adiciona um caso especial local frágil?
42
+ - Algum novo problema de reuse, simplificação ou eficiência tem custo comportamental, operacional ou de manutenção concreto?
43
+ - As instruções aplicáveis do repo expõem uma violação exata, atribuível a uma linha e com impacto concreto?
44
+
37
45
  ## Security and safety
38
46
 
39
47
  - Did the slice weaken permission, ownership, or visibility checks?