spec-first-copilot 0.7.0-beta.1 → 0.7.0

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 (55) hide show
  1. package/README.md +252 -167
  2. package/bin/cli.js +70 -70
  3. package/lib/init.js +92 -92
  4. package/lib/update.js +132 -132
  5. package/package.json +1 -1
  6. package/templates/.ai/memory/napkin.md +68 -68
  7. package/templates/.github/CHANGELOG.md +560 -533
  8. package/templates/.github/adapters/SETUP.md +314 -314
  9. package/templates/.github/adapters/confluence.md +295 -295
  10. package/templates/.github/adapters/errors.md +234 -234
  11. package/templates/.github/adapters/filesystem.md +353 -353
  12. package/templates/.github/adapters/interface.md +301 -301
  13. package/templates/.github/adapters/naming.md +241 -241
  14. package/templates/.github/adapters/registry.md +244 -244
  15. package/templates/.github/agents/backend-coder.md +215 -215
  16. package/templates/.github/agents/db-coder.md +165 -165
  17. package/templates/.github/agents/doc-writer.md +66 -66
  18. package/templates/.github/agents/frontend-coder.md +222 -222
  19. package/templates/.github/agents/infra-coder.md +341 -341
  20. package/templates/.github/agents/reviewer.md +99 -99
  21. package/templates/.github/agents/security-reviewer.md +153 -153
  22. package/templates/.github/copilot-instructions.md +272 -272
  23. package/templates/.github/instructions/docs.instructions.md +147 -145
  24. package/templates/.github/instructions/sensitive-files.instructions.md +32 -32
  25. package/templates/.github/rules.md +229 -229
  26. package/templates/.github/scripts/bootstrap-confluence.js +289 -289
  27. package/templates/.github/skills/sf-design/SKILL.md +161 -161
  28. package/templates/.github/skills/sf-dev/SKILL.md +204 -204
  29. package/templates/.github/skills/sf-discovery/SKILL.md +415 -415
  30. package/templates/.github/skills/sf-extract/SKILL.md +225 -225
  31. package/templates/.github/skills/sf-load/SKILL.md +296 -296
  32. package/templates/.github/skills/sf-mcp/SKILL.md +386 -386
  33. package/templates/.github/skills/sf-merge-docs/SKILL.md +152 -152
  34. package/templates/.github/skills/sf-plan/SKILL.md +152 -152
  35. package/templates/.github/skills/sf-publish/SKILL.md +144 -144
  36. package/templates/.github/skills/sf-session-finish/SKILL.md +93 -93
  37. package/templates/.github/skills/sf-start/SKILL.md +192 -192
  38. package/templates/.github/templates/estrutura/apiContracts.template.md +160 -159
  39. package/templates/.github/templates/estrutura/architecture.template.md +169 -168
  40. package/templates/.github/templates/estrutura/conventions.template.md +214 -212
  41. package/templates/.github/templates/estrutura/decisions.template.md +107 -107
  42. package/templates/.github/templates/estrutura/domain.template.md +161 -160
  43. package/templates/.github/templates/feature/PRD.template.md +279 -279
  44. package/templates/.github/templates/feature/Progresso.template.md +141 -141
  45. package/templates/.github/templates/feature/TRD.template.md +358 -358
  46. package/templates/.github/templates/feature/context.template.md +89 -89
  47. package/templates/.github/templates/feature/extract-log.template.md +49 -49
  48. package/templates/.github/templates/feature/projetos.template.yaml +79 -79
  49. package/templates/.github/templates/global/progresso_global.template.md +59 -57
  50. package/templates/.github/templates/specs/brief.template.md +66 -66
  51. package/templates/.github/templates/specs/contracts.template.md +147 -147
  52. package/templates/.github/templates/specs/scenarios.template.md +125 -125
  53. package/templates/.github/templates/specs/tasks.template.md +65 -65
  54. package/templates/_gitignore +35 -35
  55. package/templates/sfw.config.yml.example +147 -147
package/lib/init.js CHANGED
@@ -1,92 +1,92 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
-
4
- const PLACEHOLDER = '{{PROJECT_NAME}}';
5
-
6
- function copyDir(src, dest, replacements) {
7
- fs.mkdirSync(dest, { recursive: true });
8
- for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
9
- const srcPath = path.join(src, entry.name);
10
- // Rename _gitignore → .gitignore (npm strips .gitignore from packages)
11
- const destName = entry.name === '_gitignore' ? '.gitignore' : entry.name;
12
- const destPath = path.join(dest, destName);
13
- if (entry.isDirectory()) {
14
- copyDir(srcPath, destPath, replacements);
15
- } else {
16
- copyFile(srcPath, destPath, replacements);
17
- }
18
- }
19
- }
20
-
21
- function copyFile(src, dest, replacements) {
22
- // Skip if file already exists (preserve user's files)
23
- if (fs.existsSync(dest)) return;
24
-
25
- const textExtensions = ['.md', '.yaml', '.yml', '.json', '.js', '.ts', '.gitignore', '.gitkeep', ''];
26
- const ext = path.extname(src).toLowerCase();
27
- const basename = path.basename(src);
28
- const isText = textExtensions.includes(ext) || basename.startsWith('.') || basename === '_gitignore';
29
-
30
- if (isText) {
31
- let content = fs.readFileSync(src, 'utf-8');
32
- for (const [placeholder, value] of Object.entries(replacements)) {
33
- content = content.split(placeholder).join(value);
34
- }
35
- fs.writeFileSync(dest, content, 'utf-8');
36
- } else {
37
- fs.copyFileSync(src, dest);
38
- }
39
- }
40
-
41
- function init({ name, templatesDir, targetDir }) {
42
- const dest = targetDir || path.resolve(process.cwd(), name);
43
-
44
- const existed = fs.existsSync(dest);
45
- console.log(`\n${existed ? 'Inicializando' : 'Criando'} projeto "${name}" em ${dest}\n`);
46
-
47
- const replacements = { [PLACEHOLDER]: name };
48
- copyDir(templatesDir, dest, replacements);
49
- patchProjectName(dest, name);
50
-
51
- console.log(`Pronto! Projeto "${name}" criado.`);
52
- console.log('\nPróximos passos:');
53
- console.log(` 1. cd ${name}`);
54
- console.log(' 2. Crie uma pasta em workspace/Input/ com os insumos do seu projeto');
55
- console.log(' (ex: workspace/Input/meu_app/requisitos.md)');
56
- console.log('');
57
- console.log(' 3. (Opcional) Configure o backend de input/output:');
58
- console.log(' cp sfw.config.yml.example sfw.config.yml');
59
- console.log(' Sem isso, o framework roda 100% local (sem Confluence/etc.)');
60
- console.log('');
61
- console.log(' 4. (Opcional, se vai usar Confluence) Prepare o ambiente ANTES de abrir o VS Code:');
62
- console.log(' node .github/scripts/bootstrap-confluence.js');
63
- console.log(' Valida uvx, coleta credenciais, gera .mcp.json e pré-cacheia o MCP.');
64
- console.log(' Evita o ciclo "abrir Copilot → faltar dep → reiniciar VS Code".');
65
- console.log('');
66
- console.log(' 5. Abra o VS Code com Copilot Chat e rode:');
67
- console.log(' /sf-start <nome-da-pasta-em-Input>');
68
- console.log(' O framework detecta first-run automaticamente (cria docs/) vs feature');
69
- console.log(' (atualiza docs/ via merge).');
70
- console.log('');
71
- }
72
-
73
- function patchProjectName(dest, name) {
74
- const filesToPatch = [
75
- path.join('.ai', 'memory', 'napkin.md'),
76
- path.join('.github', 'copilot-instructions.md'),
77
- 'CLAUDE.md',
78
- ];
79
-
80
- for (const relPath of filesToPatch) {
81
- const filePath = path.join(dest, relPath);
82
- if (!fs.existsSync(filePath)) continue;
83
-
84
- let content = fs.readFileSync(filePath, 'utf-8');
85
- content = content.replace(/# Napkin Runbook\b/, `# Napkin Runbook — ${name}`);
86
- content = content.replace(/# Copilot Instructions — Projeto\b/, `# Copilot Instructions — ${name}`);
87
- content = content.replace(/# CLAUDE\.md — Projeto\b/, `# CLAUDE.md — ${name}`);
88
- fs.writeFileSync(filePath, content, 'utf-8');
89
- }
90
- }
91
-
92
- module.exports = { init };
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ const PLACEHOLDER = '{{PROJECT_NAME}}';
5
+
6
+ function copyDir(src, dest, replacements) {
7
+ fs.mkdirSync(dest, { recursive: true });
8
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
9
+ const srcPath = path.join(src, entry.name);
10
+ // Rename _gitignore → .gitignore (npm strips .gitignore from packages)
11
+ const destName = entry.name === '_gitignore' ? '.gitignore' : entry.name;
12
+ const destPath = path.join(dest, destName);
13
+ if (entry.isDirectory()) {
14
+ copyDir(srcPath, destPath, replacements);
15
+ } else {
16
+ copyFile(srcPath, destPath, replacements);
17
+ }
18
+ }
19
+ }
20
+
21
+ function copyFile(src, dest, replacements) {
22
+ // Skip if file already exists (preserve user's files)
23
+ if (fs.existsSync(dest)) return;
24
+
25
+ const textExtensions = ['.md', '.yaml', '.yml', '.json', '.js', '.ts', '.gitignore', '.gitkeep', ''];
26
+ const ext = path.extname(src).toLowerCase();
27
+ const basename = path.basename(src);
28
+ const isText = textExtensions.includes(ext) || basename.startsWith('.') || basename === '_gitignore';
29
+
30
+ if (isText) {
31
+ let content = fs.readFileSync(src, 'utf-8');
32
+ for (const [placeholder, value] of Object.entries(replacements)) {
33
+ content = content.split(placeholder).join(value);
34
+ }
35
+ fs.writeFileSync(dest, content, 'utf-8');
36
+ } else {
37
+ fs.copyFileSync(src, dest);
38
+ }
39
+ }
40
+
41
+ function init({ name, templatesDir, targetDir }) {
42
+ const dest = targetDir || path.resolve(process.cwd(), name);
43
+
44
+ const existed = fs.existsSync(dest);
45
+ console.log(`\n${existed ? 'Inicializando' : 'Criando'} projeto "${name}" em ${dest}\n`);
46
+
47
+ const replacements = { [PLACEHOLDER]: name };
48
+ copyDir(templatesDir, dest, replacements);
49
+ patchProjectName(dest, name);
50
+
51
+ console.log(`Pronto! Projeto "${name}" criado.`);
52
+ console.log('\nPróximos passos:');
53
+ console.log(` 1. cd ${name}`);
54
+ console.log(' 2. Crie uma pasta em workspace/Input/ com os insumos do seu projeto');
55
+ console.log(' (ex: workspace/Input/meu_app/requisitos.md)');
56
+ console.log('');
57
+ console.log(' 3. (Opcional) Configure o backend de input/output:');
58
+ console.log(' cp sfw.config.yml.example sfw.config.yml');
59
+ console.log(' Sem isso, o framework roda 100% local (sem Confluence/etc.)');
60
+ console.log('');
61
+ console.log(' 4. (Opcional, se vai usar Confluence) Prepare o ambiente ANTES de abrir o VS Code:');
62
+ console.log(' node .github/scripts/bootstrap-confluence.js');
63
+ console.log(' Valida uvx, coleta credenciais, gera .mcp.json e pré-cacheia o MCP.');
64
+ console.log(' Evita o ciclo "abrir Copilot → faltar dep → reiniciar VS Code".');
65
+ console.log('');
66
+ console.log(' 5. Abra o VS Code com Copilot Chat e rode:');
67
+ console.log(' /sf-start <nome-da-pasta-em-Input>');
68
+ console.log(' O framework detecta first-run automaticamente (cria docs/) vs feature');
69
+ console.log(' (atualiza docs/ via merge).');
70
+ console.log('');
71
+ }
72
+
73
+ function patchProjectName(dest, name) {
74
+ const filesToPatch = [
75
+ path.join('.ai', 'memory', 'napkin.md'),
76
+ path.join('.github', 'copilot-instructions.md'),
77
+ 'CLAUDE.md',
78
+ ];
79
+
80
+ for (const relPath of filesToPatch) {
81
+ const filePath = path.join(dest, relPath);
82
+ if (!fs.existsSync(filePath)) continue;
83
+
84
+ let content = fs.readFileSync(filePath, 'utf-8');
85
+ content = content.replace(/# Napkin Runbook\b/, `# Napkin Runbook — ${name}`);
86
+ content = content.replace(/# Copilot Instructions — Projeto\b/, `# Copilot Instructions — ${name}`);
87
+ content = content.replace(/# CLAUDE\.md — Projeto\b/, `# CLAUDE.md — ${name}`);
88
+ fs.writeFileSync(filePath, content, 'utf-8');
89
+ }
90
+ }
91
+
92
+ module.exports = { init };
package/lib/update.js CHANGED
@@ -1,132 +1,132 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
-
4
- const FRAMEWORK_DIRS = [
5
- path.join('.github', 'adapters'),
6
- path.join('.github', 'skills'),
7
- path.join('.github', 'agents'),
8
- path.join('.github', 'templates'),
9
- path.join('.github', 'instructions'),
10
- ];
11
-
12
- const FRAMEWORK_FILES = [
13
- path.join('.github', 'rules.md'),
14
- path.join('.github', 'copilot-instructions.md'),
15
- path.join('.github', 'CHANGELOG.md'),
16
- 'sfw.config.yml.example',
17
- ];
18
-
19
- function update({ templatesDir, targetDir, force }) {
20
- const dest = targetDir || process.cwd();
21
-
22
- if (!fs.existsSync(dest)) {
23
- console.error(`Erro: diretório "${dest}" não existe.`);
24
- console.error('Rode "spec-first-copilot init --name=<nome>" primeiro pra criar o projeto.');
25
- process.exit(1);
26
- }
27
-
28
- const githubDir = path.join(dest, '.github');
29
- if (!fs.existsSync(githubDir)) {
30
- console.error('Erro: não é um projeto spec-first (pasta .github/ não encontrada).');
31
- console.error('Rode "spec-first-copilot init --name=<nome>" primeiro.');
32
- process.exit(1);
33
- }
34
-
35
- console.log(`\nAtualizando projeto em ${dest}\n`);
36
-
37
- const stats = { added: [], updated: [], skipped: 0 };
38
- syncDir(templatesDir, dest, force, stats, dest);
39
-
40
- console.log('');
41
- if (stats.added.length > 0) {
42
- console.log(`Adicionados (${stats.added.length}):`);
43
- for (const f of stats.added) console.log(` + ${f}`);
44
- }
45
- if (stats.updated.length > 0) {
46
- console.log(`Atualizados (${stats.updated.length}):`);
47
- for (const f of stats.updated) console.log(` ~ ${f}`);
48
- }
49
- if (stats.added.length === 0 && stats.updated.length === 0) {
50
- console.log('Já está atualizado — nenhum arquivo novo pra adicionar.');
51
- } else {
52
- console.log(`\n${stats.added.length} adicionado(s), ${stats.updated.length} atualizado(s), ${stats.skipped} inalterado(s)`);
53
- }
54
-
55
- console.log('');
56
- }
57
-
58
- function syncDir(src, dest, force, stats, root) {
59
- fs.mkdirSync(dest, { recursive: true });
60
- for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
61
- const srcPath = path.join(src, entry.name);
62
- const destName = entry.name === '_gitignore' ? '.gitignore' : entry.name;
63
- const destPath = path.join(dest, destName);
64
- if (entry.isDirectory()) {
65
- syncDir(srcPath, destPath, force, stats, root);
66
- } else {
67
- syncFile(srcPath, destPath, force, stats, root);
68
- }
69
- }
70
- }
71
-
72
- function syncFile(src, dest, force, stats, root) {
73
- const rel = path.relative(root, dest);
74
- const exists = fs.existsSync(dest);
75
-
76
- if (exists && !force) {
77
- const isFramework = isFrameworkPath(rel);
78
- if (!isFramework) {
79
- stats.skipped++;
80
- return;
81
- }
82
- const srcContent = fs.readFileSync(src, 'utf-8');
83
- const destContent = fs.readFileSync(dest, 'utf-8');
84
- if (srcContent === destContent) {
85
- stats.skipped++;
86
- return;
87
- }
88
- fs.writeFileSync(dest, srcContent, 'utf-8');
89
- stats.updated.push(rel);
90
- return;
91
- }
92
-
93
- if (exists && force) {
94
- const srcContent = fs.readFileSync(src, 'utf-8');
95
- const destContent = fs.readFileSync(dest, 'utf-8');
96
- if (srcContent === destContent) {
97
- stats.skipped++;
98
- return;
99
- }
100
- fs.writeFileSync(dest, srcContent, 'utf-8');
101
- stats.updated.push(rel);
102
- return;
103
- }
104
-
105
- const textExtensions = ['.md', '.yaml', '.yml', '.json', '.js', '.ts', '.gitignore', '.gitkeep', ''];
106
- const ext = path.extname(src).toLowerCase();
107
- const basename = path.basename(src);
108
- const isText = textExtensions.includes(ext) || basename.startsWith('.');
109
-
110
- if (isText) {
111
- const content = fs.readFileSync(src, 'utf-8');
112
- fs.writeFileSync(dest, content, 'utf-8');
113
- } else {
114
- fs.copyFileSync(src, dest);
115
- }
116
- stats.added.push(rel);
117
- }
118
-
119
- function isFrameworkPath(rel) {
120
- const normalized = rel.split(path.sep).join('/');
121
- for (const dir of FRAMEWORK_DIRS) {
122
- const normalizedDir = dir.split(path.sep).join('/');
123
- if (normalized.startsWith(normalizedDir + '/')) return true;
124
- }
125
- for (const file of FRAMEWORK_FILES) {
126
- const normalizedFile = file.split(path.sep).join('/');
127
- if (normalized === normalizedFile) return true;
128
- }
129
- return false;
130
- }
131
-
132
- module.exports = { update };
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ const FRAMEWORK_DIRS = [
5
+ path.join('.github', 'adapters'),
6
+ path.join('.github', 'skills'),
7
+ path.join('.github', 'agents'),
8
+ path.join('.github', 'templates'),
9
+ path.join('.github', 'instructions'),
10
+ ];
11
+
12
+ const FRAMEWORK_FILES = [
13
+ path.join('.github', 'rules.md'),
14
+ path.join('.github', 'copilot-instructions.md'),
15
+ path.join('.github', 'CHANGELOG.md'),
16
+ 'sfw.config.yml.example',
17
+ ];
18
+
19
+ function update({ templatesDir, targetDir, force }) {
20
+ const dest = targetDir || process.cwd();
21
+
22
+ if (!fs.existsSync(dest)) {
23
+ console.error(`Erro: diretório "${dest}" não existe.`);
24
+ console.error('Rode "spec-first-copilot init --name=<nome>" primeiro pra criar o projeto.');
25
+ process.exit(1);
26
+ }
27
+
28
+ const githubDir = path.join(dest, '.github');
29
+ if (!fs.existsSync(githubDir)) {
30
+ console.error('Erro: não é um projeto spec-first (pasta .github/ não encontrada).');
31
+ console.error('Rode "spec-first-copilot init --name=<nome>" primeiro.');
32
+ process.exit(1);
33
+ }
34
+
35
+ console.log(`\nAtualizando projeto em ${dest}\n`);
36
+
37
+ const stats = { added: [], updated: [], skipped: 0 };
38
+ syncDir(templatesDir, dest, force, stats, dest);
39
+
40
+ console.log('');
41
+ if (stats.added.length > 0) {
42
+ console.log(`Adicionados (${stats.added.length}):`);
43
+ for (const f of stats.added) console.log(` + ${f}`);
44
+ }
45
+ if (stats.updated.length > 0) {
46
+ console.log(`Atualizados (${stats.updated.length}):`);
47
+ for (const f of stats.updated) console.log(` ~ ${f}`);
48
+ }
49
+ if (stats.added.length === 0 && stats.updated.length === 0) {
50
+ console.log('Já está atualizado — nenhum arquivo novo pra adicionar.');
51
+ } else {
52
+ console.log(`\n${stats.added.length} adicionado(s), ${stats.updated.length} atualizado(s), ${stats.skipped} inalterado(s)`);
53
+ }
54
+
55
+ console.log('');
56
+ }
57
+
58
+ function syncDir(src, dest, force, stats, root) {
59
+ fs.mkdirSync(dest, { recursive: true });
60
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
61
+ const srcPath = path.join(src, entry.name);
62
+ const destName = entry.name === '_gitignore' ? '.gitignore' : entry.name;
63
+ const destPath = path.join(dest, destName);
64
+ if (entry.isDirectory()) {
65
+ syncDir(srcPath, destPath, force, stats, root);
66
+ } else {
67
+ syncFile(srcPath, destPath, force, stats, root);
68
+ }
69
+ }
70
+ }
71
+
72
+ function syncFile(src, dest, force, stats, root) {
73
+ const rel = path.relative(root, dest);
74
+ const exists = fs.existsSync(dest);
75
+
76
+ if (exists && !force) {
77
+ const isFramework = isFrameworkPath(rel);
78
+ if (!isFramework) {
79
+ stats.skipped++;
80
+ return;
81
+ }
82
+ const srcContent = fs.readFileSync(src, 'utf-8');
83
+ const destContent = fs.readFileSync(dest, 'utf-8');
84
+ if (srcContent === destContent) {
85
+ stats.skipped++;
86
+ return;
87
+ }
88
+ fs.writeFileSync(dest, srcContent, 'utf-8');
89
+ stats.updated.push(rel);
90
+ return;
91
+ }
92
+
93
+ if (exists && force) {
94
+ const srcContent = fs.readFileSync(src, 'utf-8');
95
+ const destContent = fs.readFileSync(dest, 'utf-8');
96
+ if (srcContent === destContent) {
97
+ stats.skipped++;
98
+ return;
99
+ }
100
+ fs.writeFileSync(dest, srcContent, 'utf-8');
101
+ stats.updated.push(rel);
102
+ return;
103
+ }
104
+
105
+ const textExtensions = ['.md', '.yaml', '.yml', '.json', '.js', '.ts', '.gitignore', '.gitkeep', ''];
106
+ const ext = path.extname(src).toLowerCase();
107
+ const basename = path.basename(src);
108
+ const isText = textExtensions.includes(ext) || basename.startsWith('.');
109
+
110
+ if (isText) {
111
+ const content = fs.readFileSync(src, 'utf-8');
112
+ fs.writeFileSync(dest, content, 'utf-8');
113
+ } else {
114
+ fs.copyFileSync(src, dest);
115
+ }
116
+ stats.added.push(rel);
117
+ }
118
+
119
+ function isFrameworkPath(rel) {
120
+ const normalized = rel.split(path.sep).join('/');
121
+ for (const dir of FRAMEWORK_DIRS) {
122
+ const normalizedDir = dir.split(path.sep).join('/');
123
+ if (normalized.startsWith(normalizedDir + '/')) return true;
124
+ }
125
+ for (const file of FRAMEWORK_FILES) {
126
+ const normalizedFile = file.split(path.sep).join('/');
127
+ if (normalized === normalizedFile) return true;
128
+ }
129
+ return false;
130
+ }
131
+
132
+ module.exports = { update };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spec-first-copilot",
3
- "version": "0.7.0-beta.1",
3
+ "version": "0.7.0",
4
4
  "description": "Spec-first workflow kit for GitHub Copilot — AI-driven development with specs, not guesswork",
5
5
  "bin": {
6
6
  "spec-first-copilot": "bin/cli.js"
@@ -1,68 +1,68 @@
1
- # Napkin Runbook
2
-
3
- > Sistema de memória persistente do projeto.
4
- > Lido no início de cada sessão. Curado continuamente. Runbook, não log.
5
-
6
- ## Regras de Curadoria
7
- - Re-priorizar a cada leitura (mais importante primeiro)
8
- - Manter apenas notas recorrentes e de alto valor
9
- - Máximo 10 itens por categoria
10
- - Cada item inclui data + ação concreta ("Fazer:" / "Evitar:")
11
- - Remover itens obsoletos ou de baixo sinal
12
- - Fundir duplicatas
13
- - Adaptar categorias ao projeto conforme ele evolui
14
-
15
- ---
16
-
17
- ## Princípios Invioláveis
18
-
19
- 1. **Spec-first**: NUNCA gerar código sem SDD aprovado
20
- Fazer: seguir pipeline extract → design → plan → dev.
21
-
22
- 2. **Entregáveis contínuos**: toda feature é faseada em entregáveis incrementais.
23
- Cada fase entrega valor ao usuário e pode ir pra produção.
24
- Fazer: nunca "tudo ou nada". Sempre pequeno e constante.
25
-
26
- 3. **Nunca na main**: todo código em branch própria. Merge via PR aprovado pelo usuário.
27
- Fazer: seguir git workflow (5 passos no rules.md).
28
-
29
- 4. **SDD é auto-contido**: o coder lê APENAS SDD + task. Nunca PRD ou PM direto.
30
- Fazer: se falta info no SDD, parar e reportar.
31
-
32
- 5. **Docker dev = infra only**: docker-compose.yml tem APENAS dependências (banco, redis, rabbit).
33
- Apps (API, Worker, Web) rodam direto: `dotnet run`, `npm run dev`.
34
- Evitar: NUNCA colocar apps no docker-compose.yml de dev.
35
-
36
- ## Padrões de Execução
37
-
38
- 1. **Projeto-base = orquestrador, projetos/ = código**
39
- Specs, docs, tasks, progresso ficam aqui. Código fica nos repos em projetos/.
40
- Commits de código nos repos do serviço, NUNCA no projeto-base.
41
-
42
- 2. **Auth por endpoint é obrigatória**
43
- Todo endpoint deve ter Autenticação e Autorização definidos no SDD §5.
44
- Se SDD omitiu → PARAR e reportar. Se público → escrever "público" explicitamente.
45
-
46
- 3. **Temas críticos geram ambiguidades automáticas**
47
- Se os insumos não mencionam: auth, authz, separação de serviços, ambientes,
48
- dados sensíveis → o /extract gera ambiguidades obrigatórias.
49
-
50
- ## Armadilhas do Ambiente
51
-
52
- <!-- Adicionar conforme descoberto durante o projeto -->
53
-
54
- ## Decisões de Design
55
-
56
- <!-- Populado pelo /design e /dev conforme o projeto evolui -->
57
-
58
- ## Regras de Negócio Aprendidas
59
-
60
- <!-- Populado pelo /extract e feedback do usuário -->
61
-
62
- ## Preferências do Usuário
63
-
64
- <!-- Populado conforme interações com o usuário -->
65
-
66
- ## Sessão Atual
67
-
68
- <!-- Atualizado pelo /session-finish ao encerrar cada sessão -->
1
+ # Napkin Runbook
2
+
3
+ > Sistema de memória persistente do projeto.
4
+ > Lido no início de cada sessão. Curado continuamente. Runbook, não log.
5
+
6
+ ## Regras de Curadoria
7
+ - Re-priorizar a cada leitura (mais importante primeiro)
8
+ - Manter apenas notas recorrentes e de alto valor
9
+ - Máximo 10 itens por categoria
10
+ - Cada item inclui data + ação concreta ("Fazer:" / "Evitar:")
11
+ - Remover itens obsoletos ou de baixo sinal
12
+ - Fundir duplicatas
13
+ - Adaptar categorias ao projeto conforme ele evolui
14
+
15
+ ---
16
+
17
+ ## Princípios Invioláveis
18
+
19
+ 1. **Spec-first**: NUNCA gerar código sem PRD e/ou TRD aprovados (+ `specs/{nome}/` gerado)
20
+ Fazer: seguir pipeline discovery → extract → design → plan → dev.
21
+
22
+ 2. **Entregáveis contínuos**: toda feature é faseada em entregáveis incrementais.
23
+ Cada fase entrega valor ao usuário e pode ir pra produção.
24
+ Fazer: nunca "tudo ou nada". Sempre pequeno e constante.
25
+
26
+ 3. **Nunca na main**: todo código em branch própria. Merge via PR aprovado pelo usuário.
27
+ Fazer: seguir git workflow (5 passos no rules.md).
28
+
29
+ 4. **specs/ é auto-contido**: o coder lê APENAS `specs/{nome}/` + task. Nunca PRD/TRD ou PM direto.
30
+ Fazer: se falta info em `specs/`, parar e reportar.
31
+
32
+ 5. **Docker dev = infra only**: docker-compose.yml tem APENAS dependências (banco, redis, rabbit).
33
+ Apps (API, Worker, Web) rodam direto: `dotnet run`, `npm run dev`.
34
+ Evitar: NUNCA colocar apps no docker-compose.yml de dev.
35
+
36
+ ## Padrões de Execução
37
+
38
+ 1. **Projeto-base = orquestrador, projetos/ = código**
39
+ Specs, docs, tasks, progresso ficam aqui. Código fica nos repos em projetos/.
40
+ Commits de código nos repos do serviço, NUNCA no projeto-base.
41
+
42
+ 2. **Auth por endpoint é obrigatória**
43
+ Todo endpoint deve ter Autenticação e Autorização definidos no TRD §2.1 (Backend) + §7 (Segurança).
44
+ Se TRD omitiu → PARAR e reportar. Se público → escrever "público" explicitamente.
45
+
46
+ 3. **Temas críticos geram ambiguidades automáticas**
47
+ Se os insumos não mencionam: auth, authz, separação de serviços, ambientes,
48
+ dados sensíveis → o /extract gera ambiguidades obrigatórias.
49
+
50
+ ## Armadilhas do Ambiente
51
+
52
+ <!-- Adicionar conforme descoberto durante o projeto -->
53
+
54
+ ## Decisões de Design
55
+
56
+ <!-- Populado pelo /design e /dev conforme o projeto evolui -->
57
+
58
+ ## Regras de Negócio Aprendidas
59
+
60
+ <!-- Populado pelo /extract e feedback do usuário -->
61
+
62
+ ## Preferências do Usuário
63
+
64
+ <!-- Populado conforme interações com o usuário -->
65
+
66
+ ## Sessão Atual
67
+
68
+ <!-- Atualizado pelo /session-finish ao encerrar cada sessão -->