sinapse-ai 1.25.0 → 1.25.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/.claude/settings.json +170 -0
  2. package/.codex/catalog.json +1 -1
  3. package/.codex/scripts/sinapse-codex.js +0 -5
  4. package/.codex/scripts/sync-codex-native.js +4 -37
  5. package/.codex/scripts/validate-codex-native.js +13 -23
  6. package/.codex/tasks/onboard-sinapse-codex.md +1 -1
  7. package/.sinapse-ai/data/entity-registry.yaml +44 -18
  8. package/.sinapse-ai/install-manifest.yaml +3 -3
  9. package/CHANGELOG.md +8 -14
  10. package/README.en.md +38 -30
  11. package/README.md +33 -25
  12. package/bin/cli.js +8 -1
  13. package/bin/commands/help.js +3 -2
  14. package/bin/commands/install.js +25 -14
  15. package/bin/commands/status.js +4 -3
  16. package/bin/commands/uninstall.js +216 -51
  17. package/bin/commands/update.js +8 -4
  18. package/bin/lib/global-provider-adapters.js +73 -10
  19. package/bin/lib/provider-parity.js +22 -0
  20. package/bin/postinstall.js +11 -11
  21. package/docs/framework/codex-parity/codex-integration-process.md +6 -7
  22. package/docs/framework/codex-parity/codex-native-runtime-v2.md +8 -8
  23. package/docs/framework/codex-parity/codex-parity-program.md +4 -4
  24. package/docs/framework/codex-parity/codex-total-parity-orchestration-plan.md +1 -1
  25. package/docs/getting-started.md +13 -5
  26. package/docs/guides/codex-config.md +35 -51
  27. package/docs/guides/development-setup.md +1 -2
  28. package/docs/guides/ide-integration.md +28 -29
  29. package/docs/guides/project-status-feature.md +35 -55
  30. package/docs/guides/quality-gates.md +2 -2
  31. package/docs/guides/user-guide.md +21 -10
  32. package/docs/ide-integration.md +2 -2
  33. package/docs/installation/README.md +4 -4
  34. package/docs/installation/faq.md +84 -70
  35. package/docs/installation/linux.md +11 -14
  36. package/docs/installation/macos.md +10 -4
  37. package/docs/installation/troubleshooting.md +11 -5
  38. package/docs/installation/v4-quick-start.md +14 -14
  39. package/docs/installation/windows.md +11 -15
  40. package/docs/pt/contributing.md +1 -1
  41. package/docs/pt/guides/development-setup.md +1 -2
  42. package/docs/pt/guides/project-status-feature.md +35 -55
  43. package/docs/pt/guides/quality-gates.md +2 -2
  44. package/docs/pt/guides/user-guide.md +21 -11
  45. package/docs/pt/ide-integration.md +6 -6
  46. package/docs/pt/installation/README.md +2 -4
  47. package/docs/pt/installation/faq.md +54 -89
  48. package/docs/pt/installation/linux.md +9 -27
  49. package/docs/pt/installation/macos.md +8 -12
  50. package/docs/pt/installation/troubleshooting.md +11 -10
  51. package/docs/pt/installation/v4-quick-start.md +17 -18
  52. package/docs/pt/installation/windows.md +8 -21
  53. package/docs/pt/platforms/claude-code.md +4 -4
  54. package/docs/sinapse-agent-flows/analyst-system.md +1 -1
  55. package/docs/sinapse-agent-flows/architect-system.md +1 -1
  56. package/docs/sinapse-agent-flows/data-engineer-system.md +1 -1
  57. package/docs/sinapse-agent-flows/dev-system.md +1 -1
  58. package/docs/sinapse-agent-flows/pm-system.md +1 -1
  59. package/docs/sinapse-agent-flows/qa-system.md +1 -1
  60. package/docs/sinapse-agent-flows/sm-system.md +1 -1
  61. package/docs/sinapse-agent-flows/snps-orqx-system.md +1 -1
  62. package/docs/sinapse-agent-flows/squad-creator-system.md +2 -2
  63. package/docs/sinapse-agent-flows/ux-design-expert-system.md +1 -1
  64. package/docs/troubleshooting.md +9 -7
  65. package/package.json +21 -16
  66. package/packages/installer/src/config/ide-configs.js +1 -1
  67. package/packages/installer/src/installer/sinapse-ai-installer.js +282 -10
  68. package/packages/installer/src/migrations/legacy-claude-agent-command-hashes.json +36 -0
  69. package/packages/installer/src/migrations/legacy-codex-native-skill-hashes.json +42 -0
  70. package/packages/installer/src/updater/index.js +24 -7
  71. package/packages/installer/src/wizard/ide-config-generator.js +4 -0
  72. package/packages/installer/src/wizard/index.js +6 -2
  73. package/scripts/validate-article-vii.js +305 -20
  74. package/scripts/validate-codex-command-registry.js +101 -0
  75. package/scripts/validate-install-docs.js +87 -3
  76. package/scripts/validate-provider-adapters.js +117 -1
  77. package/squads/claude-code-mastery/agents/skill-craftsman.md +2 -2
  78. package/squads/claude-code-mastery/knowledge-base/skill-creation-patterns.md +1 -1
  79. package/squads/squad-design/agents/ux-designer.md +1 -1
  80. package/.codex/skills/sinapse/SKILL.md +0 -10
  81. package/.codex/skills/sinapse-agent/SKILL.md +0 -22
  82. package/.codex/skills/sinapse-analyst/SKILL.md +0 -29
  83. package/.codex/skills/sinapse-animations/SKILL.md +0 -28
  84. package/.codex/skills/sinapse-architect/SKILL.md +0 -31
  85. package/.codex/skills/sinapse-brand/SKILL.md +0 -28
  86. package/.codex/skills/sinapse-claude/SKILL.md +0 -28
  87. package/.codex/skills/sinapse-cloning/SKILL.md +0 -28
  88. package/.codex/skills/sinapse-commercial/SKILL.md +0 -28
  89. package/.codex/skills/sinapse-content/SKILL.md +0 -28
  90. package/.codex/skills/sinapse-copy/SKILL.md +0 -28
  91. package/.codex/skills/sinapse-council/SKILL.md +0 -28
  92. package/.codex/skills/sinapse-courses/SKILL.md +0 -28
  93. package/.codex/skills/sinapse-cyber/SKILL.md +0 -28
  94. package/.codex/skills/sinapse-data-engineer/SKILL.md +0 -31
  95. package/.codex/skills/sinapse-design/SKILL.md +0 -28
  96. package/.codex/skills/sinapse-dev/SKILL.md +0 -31
  97. package/.codex/skills/sinapse-devops/SKILL.md +0 -31
  98. package/.codex/skills/sinapse-finance/SKILL.md +0 -28
  99. package/.codex/skills/sinapse-growth/SKILL.md +0 -28
  100. package/.codex/skills/sinapse-loop/SKILL.md +0 -35
  101. package/.codex/skills/sinapse-orqx/SKILL.md +0 -44
  102. package/.codex/skills/sinapse-paidmedia/SKILL.md +0 -28
  103. package/.codex/skills/sinapse-pm/SKILL.md +0 -31
  104. package/.codex/skills/sinapse-po/SKILL.md +0 -31
  105. package/.codex/skills/sinapse-product/SKILL.md +0 -28
  106. package/.codex/skills/sinapse-qa/SKILL.md +0 -31
  107. package/.codex/skills/sinapse-research/SKILL.md +0 -28
  108. package/.codex/skills/sinapse-sm/SKILL.md +0 -27
  109. package/.codex/skills/sinapse-spec-driven/SKILL.md +0 -35
  110. package/.codex/skills/sinapse-squad-creator/SKILL.md +0 -31
  111. package/.codex/skills/sinapse-storytelling/SKILL.md +0 -28
  112. package/.codex/skills/sinapse-swarm/SKILL.md +0 -28
  113. package/.codex/skills/sinapse-ux-design-expert/SKILL.md +0 -24
  114. package/.codex/skills/snps/SKILL.md +0 -10
  115. package/.codex/skills/snps-orqx/SKILL.md +0 -10
  116. /package/{.codex → .agents}/skills/react-bits-frontend/SKILL.md +0 -0
  117. /package/{.codex → .agents}/skills/react-bits-frontend/agents/openai.yaml +0 -0
  118. /package/{.codex → .agents}/skills/react-bits-frontend/references/corpus-map.md +0 -0
@@ -5,8 +5,9 @@
5
5
  * @story GA-1.5 (Article gates VII/VIII/XI automated)
6
6
  *
7
7
  * Verifica que TODOS os documentos publicos referem aos MESMOS numeros de
8
- * squads/agentes/orqx/tasks que `.sinapse-ai/constitution.md` (canonico,
9
- * gerado por `npm run sync:counts`).
8
+ * squads/agentes/orqx/tasks medidos pelas fontes canonicas em disco. Contagens
9
+ * gerais reutilizam `sync-counts`; o detalhamento resolvivel usa o ativador
10
+ * parametrico do Codex.
10
11
  *
11
12
  * Documentos verificados:
12
13
  * - README.md
@@ -27,11 +28,14 @@
27
28
 
28
29
  const fs = require('fs');
29
30
  const path = require('path');
31
+ const { execFileSync } = require('child_process');
30
32
 
31
33
  const { collectCounts } = require('./sync-counts.js');
34
+ const { validateProviderAdapters } = require('./validate-provider-adapters.js');
32
35
 
33
36
  const ROOT = path.resolve(__dirname, '..');
34
37
  const CONSTITUTION = path.join(ROOT, '.sinapse-ai', 'constitution.md');
38
+ const CODEX_RESOLVER = path.join(ROOT, '.codex', 'scripts', 'resolve-codex-agent.js');
35
39
 
36
40
  /**
37
41
  * Count Constitution articles + NON-NEGOTIABLE ones from the canonical source.
@@ -56,37 +60,189 @@ function collectConstitutionCounts() {
56
60
  }
57
61
 
58
62
  const TARGETS = [
59
- { file: 'README.md', label: 'README PT' },
60
- { file: 'README.en.md', label: 'README EN' },
63
+ { file: 'README.md', label: 'README PT', requireTaskBreakdown: true },
64
+ { file: 'README.en.md', label: 'README EN', requireTaskBreakdown: true },
61
65
  { file: 'AGENTS.md', label: 'AGENTS guide' },
66
+ { file: 'docs/getting-started.md', label: 'Getting started' },
67
+ { file: 'docs/troubleshooting.md', label: 'Troubleshooting' },
68
+ { file: 'docs/guides/user-guide.md', label: 'User guide EN', requireAgentTotal: true },
69
+ { file: 'docs/pt/guides/user-guide.md', label: 'User guide PT', requireAgentTotal: true },
70
+ { file: 'docs/guides/codex-config.md', label: 'Codex configuration guide' },
71
+ { file: 'docs/guides/ide-integration.md', label: 'IDE integration guide', requireProviderBreakdown: true },
72
+ { file: 'docs/guides/project-status-feature.md', label: 'Project status guide EN', requireAgentTotal: true },
73
+ { file: 'docs/pt/guides/project-status-feature.md', label: 'Project status guide PT', requireAgentTotal: true },
74
+ { file: 'docs/installation/README.md', label: 'Installation index' },
75
+ { file: 'docs/installation/faq.md', label: 'Installation FAQ' },
76
+ { file: 'docs/installation/troubleshooting.md', label: 'Installation troubleshooting' },
77
+ { file: 'docs/installation/v4-quick-start.md', label: 'Installation quick start' },
78
+ { file: 'docs/pt/installation/README.md', label: 'Installation index PT' },
79
+ { file: 'docs/pt/installation/faq.md', label: 'Installation FAQ PT' },
80
+ { file: 'docs/pt/installation/troubleshooting.md', label: 'Installation troubleshooting PT' },
81
+ { file: 'docs/pt/installation/v4-quick-start.md', label: 'Installation quick start PT' },
62
82
  { file: 'package.json', label: 'package.json description', jsonField: 'description' },
63
83
  { file: 'packages/installer/src/wizard/feedback.js', label: 'wizard feedback' },
64
84
  ];
65
85
 
86
+ function collectRuntimeTaskCounts(options = {}) {
87
+ const execute = options.execFileSync || execFileSync;
88
+ const resolverPath = options.resolverPath || CODEX_RESOLVER;
89
+ const timeout = options.timeoutMs ?? 10_000;
90
+ if (!Number.isInteger(timeout) || timeout <= 0) {
91
+ throw new Error('parametric resolver timeout must be a positive integer');
92
+ }
93
+ let raw;
94
+ try {
95
+ raw = execute(process.execPath, [resolverPath, '--stats'], {
96
+ cwd: ROOT,
97
+ encoding: 'utf8',
98
+ timeout,
99
+ });
100
+ } catch (error) {
101
+ throw new Error(`cannot execute the parametric resolver: ${error.message}`);
102
+ }
103
+
104
+ let stats;
105
+ try {
106
+ stats = JSON.parse(raw);
107
+ } catch (error) {
108
+ throw new Error(`parametric resolver returned malformed JSON: ${error.message}`);
109
+ }
110
+
111
+ const fields = {
112
+ squadTasks: stats.squadTaskFiles,
113
+ developmentTasks: stats.devTaskFiles,
114
+ totalTasks: stats.totalTaskFiles,
115
+ resolvableTasks: stats.resolvableTaskPointers,
116
+ };
117
+ for (const [name, value] of Object.entries(fields)) {
118
+ if (!Number.isInteger(value) || value <= 0) {
119
+ throw new Error(`parametric resolver field ${name} must be a positive integer`);
120
+ }
121
+ }
122
+ if (fields.totalTasks !== fields.squadTasks + fields.developmentTasks) {
123
+ throw new Error('parametric resolver totalTaskFiles does not equal squadTaskFiles + devTaskFiles');
124
+ }
125
+ if (fields.resolvableTasks > fields.totalTasks) {
126
+ throw new Error('parametric resolver resolvableTaskPointers exceeds totalTaskFiles');
127
+ }
128
+ if (
129
+ options.expectedSquadTasks !== undefined &&
130
+ fields.squadTasks !== options.expectedSquadTasks
131
+ ) {
132
+ throw new Error(
133
+ `parametric resolver squadTaskFiles ${fields.squadTasks} does not match ` +
134
+ `sync-counts tasks ${options.expectedSquadTasks}`,
135
+ );
136
+ }
137
+ return fields;
138
+ }
139
+
140
+ function countHookRegistrations(settings, label) {
141
+ if (!settings || typeof settings !== 'object' || Array.isArray(settings)) {
142
+ throw new Error(`${label} hook settings must be an object`);
143
+ }
144
+ const hooks = settings.hooks;
145
+ if (!hooks || typeof hooks !== 'object' || Array.isArray(hooks)) {
146
+ throw new Error(`${label} hook settings are missing hooks`);
147
+ }
148
+ let count = 0;
149
+ for (const groups of Object.values(hooks)) {
150
+ if (!Array.isArray(groups)) throw new Error(`${label} hook groups must be arrays`);
151
+ for (const group of groups) {
152
+ if (!group || !Array.isArray(group.hooks)) {
153
+ throw new Error(`${label} hook registration is malformed`);
154
+ }
155
+ count += group.hooks.length;
156
+ }
157
+ }
158
+ if (count === 0) throw new Error(`${label} hook settings contain no registrations`);
159
+ return count;
160
+ }
161
+
162
+ function collectProviderSurfaceCounts(options = {}) {
163
+ const validateProviders = options.validateProviderAdapters || validateProviderAdapters;
164
+ const readJson = options.readJson || ((filePath) => JSON.parse(fs.readFileSync(filePath, 'utf8')));
165
+ const result = validateProviders(ROOT);
166
+ if (!result || result.ok !== true || !result.metrics) {
167
+ const detail = result && Array.isArray(result.errors) ? result.errors.join('; ') : 'invalid result';
168
+ throw new Error(`provider adapter validation failed: ${detail}`);
169
+ }
170
+ const claudeSkills = result.metrics.claudeSkills;
171
+ const codexSkills = result.metrics.codexSkills;
172
+ for (const [name, value] of Object.entries({ claudeSkills, codexSkills })) {
173
+ if (!Number.isInteger(value) || value <= 0) {
174
+ throw new Error(`provider metric ${name} must be a positive integer`);
175
+ }
176
+ }
177
+ const claudeHooks = countHookRegistrations(
178
+ readJson(path.join(ROOT, '.claude', 'settings.json')),
179
+ 'Claude',
180
+ );
181
+ const codexSettings = readJson(path.join(ROOT, '.codex', 'hooks.json'));
182
+ if (
183
+ !codexSettings ||
184
+ typeof codexSettings !== 'object' ||
185
+ !codexSettings.hooks ||
186
+ typeof codexSettings.hooks !== 'object' ||
187
+ Array.isArray(codexSettings.hooks) ||
188
+ Object.values(codexSettings.hooks).some((groups) => !Array.isArray(groups))
189
+ ) {
190
+ throw new Error('Codex hook settings are malformed');
191
+ }
192
+ const codexHookEvents = Object.keys(codexSettings.hooks).length;
193
+ if (codexHookEvents === 0) {
194
+ throw new Error('Codex hook settings contain no lifecycle events');
195
+ }
196
+ return { claudeSkills, codexSkills, claudeHooks, codexHookEvents };
197
+ }
198
+
66
199
  /**
67
200
  * Build the set of expected numeric tokens that MUST appear consistently.
68
201
  * Returns an object describing canonical counts and a regex helper.
69
202
  */
70
- function buildExpectations(counts, constitution = { articles: 0, nonNegotiable: 0 }) {
203
+ function buildExpectations(
204
+ counts,
205
+ constitution = { articles: 0, nonNegotiable: 0 },
206
+ runtimeTasks = {},
207
+ providerSurfaces = {},
208
+ ) {
71
209
  return {
72
210
  squads: counts.squads,
73
211
  agents: counts.totalAgents,
74
212
  orqx: counts.totalOrqx,
75
213
  tasks: counts.tasks,
214
+ squadTasks: runtimeTasks.squadTasks ?? counts.tasks,
215
+ developmentTasks: runtimeTasks.developmentTasks ?? 0,
216
+ totalTasks: runtimeTasks.totalTasks ?? counts.tasks,
217
+ resolvableTasks: runtimeTasks.resolvableTasks ?? 0,
218
+ claudeSkills: providerSurfaces.claudeSkills ?? 0,
219
+ codexSkills: providerSurfaces.codexSkills ?? 0,
220
+ claudeHooks: providerSurfaces.claudeHooks ?? 0,
221
+ codexHookEvents: providerSurfaces.codexHookEvents ?? 0,
76
222
  articles: constitution.articles,
77
223
  nonNegotiable: constitution.nonNegotiable,
78
224
  };
79
225
  }
80
226
 
227
+ function parseMetric(raw) {
228
+ return parseInt(String(raw).replace(/[.,]/g, ''), 10);
229
+ }
230
+
231
+ function metricPattern(value) {
232
+ const digits = String(value);
233
+ if (digits.length <= 3) return digits;
234
+ return `${digits}|${digits.slice(0, -3)}[.,]${digits.slice(-3)}`;
235
+ }
236
+
81
237
  /**
82
238
  * Extract numeric counts that look like squads/agents/orqx/tasks claims.
83
239
  * Returns drift findings (what the doc claims vs what is canonical).
84
240
  */
85
- function findDrift(content, expected) {
241
+ function findDrift(content, expected, options = {}) {
86
242
  const findings = [];
87
243
 
88
244
  // Squads: "<N> squads" or "<N> squad"
89
- const squadsRe = /\b(\d{1,4})\s+squads?\b/gi;
245
+ const squadsRe = /(?<![\d.,])(\d{1,4})\s+squads?\b/gi;
90
246
  for (const m of content.matchAll(squadsRe)) {
91
247
  const n = parseInt(m[1], 10);
92
248
  if (n !== expected.squads && n >= 5) {
@@ -96,7 +252,7 @@ function findDrift(content, expected) {
96
252
  }
97
253
 
98
254
  // Agents/agentes: "<N> agentes" or "<N> agents"
99
- const agentsRe = /\b(\d{1,4})\s+(agentes|agents)\b/gi;
255
+ const agentsRe = /(?<![\d.,])(\d{1,4})\s+(agentes|agents)\b/gi;
100
256
  for (const m of content.matchAll(agentsRe)) {
101
257
  const n = parseInt(m[1], 10);
102
258
  if (n !== expected.agents && n >= 50) {
@@ -104,13 +260,115 @@ function findDrift(content, expected) {
104
260
  }
105
261
  }
106
262
 
107
- // Tasks (com ou sem separador de milhar): "1.237 tasks", "1,237 tasks", "1237 tasks"
108
- const tasksRe = /\b(\d{1,3}(?:[.,]\d{3})?|\d{4,5})\s+tasks\b/gi;
263
+ const scopedTaskPatterns = [
264
+ { kind: 'squad tasks', expected: expected.squadTasks, re: /(?<![\d.,])(\d{1,3}(?:[.,]\d{3})?|\d{4,5})\s+squad\s+tasks\b/gi },
265
+ { kind: 'development tasks', expected: expected.developmentTasks, re: /(?<![\d.,])(\d{1,3}(?:[.,]\d{3})?|\d{4,5})\s+development\s+tasks\b/gi },
266
+ { kind: 'total task files', expected: expected.totalTasks, re: /(?<![\d.,])(\d{1,3}(?:[.,]\d{3})?|\d{4,5})\s+task\s+files\b/gi },
267
+ { kind: 'resolvable task pointers', expected: expected.resolvableTasks, re: /(?<![\d.,])(\d{1,3}(?:[.,]\d{3})?|\d{4,5})\s+(?:ponteiros|pointers)[^.\n]{0,30}resolv/gi },
268
+ ];
269
+ for (const metric of scopedTaskPatterns) {
270
+ if (!metric.expected) continue;
271
+ for (const m of content.matchAll(metric.re)) {
272
+ const n = parseMetric(m[1]);
273
+ if (n !== metric.expected) {
274
+ findings.push({ kind: metric.kind, found: n, expected: metric.expected, snippet: m[0].trim() });
275
+ }
276
+ }
277
+ }
278
+
279
+ // Generic task claims may refer to squad, development, total, or resolvable
280
+ // files. Scoped phrases above enforce the meaning of each metric.
281
+ const tasksRe = /(?<![\d.,])(\d{1,3}(?:[.,]\d{3})?|\d{4,5})\s+tasks\b/gi;
282
+ const acceptedTaskCounts = new Set([
283
+ expected.squadTasks,
284
+ expected.developmentTasks,
285
+ expected.totalTasks,
286
+ expected.resolvableTasks,
287
+ ].filter(Boolean));
109
288
  for (const m of content.matchAll(tasksRe)) {
110
- const raw = m[1].replace(/[.,]/g, '');
111
- const n = parseInt(raw, 10);
112
- if (n !== expected.tasks && n >= 100) {
113
- findings.push({ kind: 'tasks', found: n, expected: expected.tasks, snippet: m[0] });
289
+ const n = parseMetric(m[1]);
290
+ if (!acceptedTaskCounts.has(n) && n >= 100) {
291
+ findings.push({ kind: 'tasks', found: n, expected: expected.totalTasks, snippet: m[0] });
292
+ }
293
+ }
294
+
295
+ if (options.requireTaskBreakdown) {
296
+ const required = [
297
+ ['squad tasks', expected.squadTasks, 'squad\\s+tasks'],
298
+ ['development tasks', expected.developmentTasks, 'development\\s+tasks'],
299
+ ['total task files', expected.totalTasks, 'task\\s+files'],
300
+ ['resolvable task pointers', expected.resolvableTasks, '(?:ponteiros|pointers)[^.\\n]{0,30}resolv'],
301
+ ];
302
+ for (const [kind, value, suffix] of required) {
303
+ if (!value) continue;
304
+ const re = new RegExp(`\\b(?:${metricPattern(value)})\\s+${suffix}`, 'i');
305
+ if (!re.test(content)) {
306
+ findings.push({ kind: `${kind} (missing)`, found: null, expected: value, snippet: 'claim ausente' });
307
+ }
308
+ }
309
+ }
310
+
311
+ const skillsClaim = content.match(/(?:Skills\s+instaladas|Installed\s+skills)\s*\|\s*(\d+)\s*\|\s*(\d+)/i);
312
+ if (skillsClaim) {
313
+ for (const [kind, found, wanted] of [
314
+ ['Claude skills', Number(skillsClaim[1]), expected.claudeSkills],
315
+ ['Codex skills', Number(skillsClaim[2]), expected.codexSkills],
316
+ ]) {
317
+ if (wanted && found !== wanted) findings.push({ kind, found, expected: wanted, snippet: skillsClaim[0] });
318
+ }
319
+ }
320
+ const hooksClaim = content.match(/(?:Hooks\s+registrados|Registered\s+hooks)\s*\|\s*(\d+)\s+(?:registros|native\s+registrations)[^|]*\|\s*(\d+)\s+(?:eventos|lifecycle\s+events)/i);
321
+ if (hooksClaim) {
322
+ for (const [kind, found, wanted] of [
323
+ ['Claude hook registrations', Number(hooksClaim[1]), expected.claudeHooks],
324
+ ['Codex hook events', Number(hooksClaim[2]), expected.codexHookEvents],
325
+ ]) {
326
+ if (wanted && found !== wanted) findings.push({ kind, found, expected: wanted, snippet: hooksClaim[0] });
327
+ }
328
+ }
329
+ const hookProseRe = /(?<![\d.,])(\d{1,3})\s+(?:Claude\s+Code\s+hook\s+registrations?|hooks?\s+(?:registrados?|ativos?)|(?:(?:active|registered|enforcement)\s+)(?:(?:Claude(?:\s+Code)?|native(?:\s+Claude(?:\s+Code)?)?)\s+)?hooks|hooks)\b/gi;
330
+ for (const claim of content.matchAll(hookProseRe)) {
331
+ const found = Number(claim[1]);
332
+ if (expected.claudeHooks && found !== expected.claudeHooks) {
333
+ findings.push({
334
+ kind: 'Claude hook registrations',
335
+ found,
336
+ expected: expected.claudeHooks,
337
+ snippet: claim[0],
338
+ });
339
+ }
340
+ }
341
+ const codexLifecycleProseRe = /(?<![\d.,])(\d{1,3})\s+(?:(?:Codex\s+)?lifecycle\s+events?|eventos\s+de\s+lifecycle)\b/gi;
342
+ for (const claim of content.matchAll(codexLifecycleProseRe)) {
343
+ const found = Number(claim[1]);
344
+ if (expected.codexHookEvents && found !== expected.codexHookEvents) {
345
+ findings.push({
346
+ kind: 'Codex hook events',
347
+ found,
348
+ expected: expected.codexHookEvents,
349
+ snippet: claim[0],
350
+ });
351
+ }
352
+ }
353
+ if (options.requireProviderBreakdown) {
354
+ if (!skillsClaim) findings.push({ kind: 'provider skills (missing)', found: null, expected: `${expected.claudeSkills}/${expected.codexSkills}`, snippet: 'claim ausente' });
355
+ if (!hooksClaim) findings.push({ kind: 'provider hooks (missing)', found: null, expected: `${expected.claudeHooks}/${expected.codexHookEvents}`, snippet: 'claim ausente' });
356
+ }
357
+
358
+ if (options.requireAgentTotal) {
359
+ const expectedAgentPattern = metricPattern(expected.agents);
360
+ const agentClaim = new RegExp(
361
+ '(?:SINAPSE\\s+(?:includes|inclui)|O\\s+SINAPSE\\s+inclui)[^.\\n]{0,40}' +
362
+ `\\b(?:${expectedAgentPattern})\\s+(?:specialized\\s+agents|agentes\\s+especializados)`,
363
+ 'i',
364
+ );
365
+ if (!agentClaim.test(content)) {
366
+ findings.push({
367
+ kind: 'global agent total (missing)',
368
+ found: null,
369
+ expected: expected.agents,
370
+ snippet: 'claim ausente',
371
+ });
114
372
  }
115
373
  }
116
374
 
@@ -171,14 +429,28 @@ function main() {
171
429
 
172
430
  const counts = collectCounts();
173
431
  const constitution = collectConstitutionCounts();
174
- const expected = buildExpectations(counts, constitution);
432
+ let runtimeTasks;
433
+ let providerSurfaces;
434
+ try {
435
+ runtimeTasks = collectRuntimeTaskCounts({ expectedSquadTasks: counts.tasks });
436
+ providerSurfaces = collectProviderSurfaceCounts();
437
+ } catch (error) {
438
+ console.error(`FALHA - nao foi possivel medir as metricas canonicas: ${error.message}`);
439
+ process.exit(1);
440
+ }
441
+ const expected = buildExpectations(counts, constitution, runtimeTasks, providerSurfaces);
175
442
 
176
443
  console.log('Article VII (Metrics Accuracy) — validacao de drift');
177
- console.log('Numeros canonicos (de .sinapse-ai/constitution.md):');
444
+ console.log('Numeros canonicos (medidos das fontes em disco):');
178
445
  console.log(` squads ${expected.squads}`);
179
446
  console.log(` agentes ${expected.agents}`);
180
447
  console.log(` orqx ${expected.orqx}`);
181
- console.log(` tasks ${expected.tasks}`);
448
+ console.log(` squad tasks ${expected.squadTasks}`);
449
+ console.log(` dev tasks ${expected.developmentTasks}`);
450
+ console.log(` task files ${expected.totalTasks}`);
451
+ console.log(` resolvable ${expected.resolvableTasks}`);
452
+ console.log(` skills C/Codex ${expected.claudeSkills}/${expected.codexSkills}`);
453
+ console.log(` hooks C/Codex ${expected.claudeHooks}/${expected.codexHookEvents}`);
182
454
  console.log(` artigos ${expected.articles}`);
183
455
  console.log(` NON-NEGOTIABLE ${expected.nonNegotiable}`);
184
456
  console.log('');
@@ -191,7 +463,12 @@ function main() {
191
463
  console.log(` [skip] ${target.file} nao encontrado`);
192
464
  continue;
193
465
  }
194
- const drift = findDrift(content, expected);
466
+ const drift = findDrift(content, expected, {
467
+ requireTaskBreakdown: target.requireTaskBreakdown === true,
468
+ requireProviderBreakdown:
469
+ target.requireTaskBreakdown === true || target.requireProviderBreakdown === true,
470
+ requireAgentTotal: target.requireAgentTotal === true,
471
+ });
195
472
  if (drift.length === 0) {
196
473
  console.log(` [ok] ${target.file}`);
197
474
  } else {
@@ -214,7 +491,7 @@ function main() {
214
491
  console.log('');
215
492
  console.log('Acao corretiva (Article VII NON-NEGOTIABLE):');
216
493
  console.log(' 1. Atualize cada arquivo listado acima com os numeros canonicos.');
217
- console.log(' 2. Rode `npm run sync:counts` para regenerar a constitution.');
494
+ console.log(' 2. Se o inventario mudou, rode `npm run sync:counts`.');
218
495
  console.log(' 3. Rode `npm run validate:article-vii` novamente para confirmar.');
219
496
 
220
497
  if (fixMode) {
@@ -234,4 +511,12 @@ if (require.main === module) {
234
511
  main();
235
512
  }
236
513
 
237
- module.exports = { findDrift, buildExpectations, collectConstitutionCounts, TARGETS };
514
+ module.exports = {
515
+ findDrift,
516
+ buildExpectations,
517
+ collectConstitutionCounts,
518
+ collectRuntimeTaskCounts,
519
+ collectProviderSurfaceCounts,
520
+ countHookRegistrations,
521
+ TARGETS,
522
+ };
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+
7
+ const legacy = require('../.sinapse-ai/infrastructure/scripts/validate-codex-command-registry');
8
+
9
+ function resolveRegistryPath(projectRoot, declaredPath) {
10
+ if (typeof declaredPath !== 'string' || !declaredPath.trim() || path.isAbsolute(declaredPath)) return null;
11
+ const resolved = path.resolve(projectRoot, declaredPath);
12
+ const relative = path.relative(path.resolve(projectRoot), resolved);
13
+ if (relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) return null;
14
+ return resolved;
15
+ }
16
+
17
+ function validateCodexCommandRegistry(options = {}) {
18
+ const projectRoot = options.projectRoot || process.cwd();
19
+ const { registryPath, registry, error } = legacy.loadRegistry(projectRoot);
20
+ const errors = [];
21
+ const requiredCoverage = options.requiredCoverage === false
22
+ ? null
23
+ : options.requiredCoverage || legacy.REQUIRED_COMMAND_COVERAGE;
24
+
25
+ if (error) return { ok: false, errors: [error], warnings: [], metrics: { agents: 0, commands: 0 } };
26
+
27
+ let commandCount = 0;
28
+ const seenAgentAliases = new Map();
29
+ for (const [agentId, agentSpec] of Object.entries(registry.agents || {})) {
30
+ const skillPath = path.join(projectRoot, '.agents', 'skills', agentSpec.skillId || agentId, 'SKILL.md');
31
+ if (!fs.existsSync(skillPath)) {
32
+ errors.push(`${agentId}: missing skill file ${path.relative(projectRoot, skillPath)}`);
33
+ }
34
+
35
+ const sourceOfTruth = resolveRegistryPath(projectRoot, agentSpec.sourceOfTruth);
36
+ if (!sourceOfTruth) {
37
+ errors.push(`${agentId}: invalid or missing source of truth`);
38
+ } else if (!fs.existsSync(sourceOfTruth)) {
39
+ errors.push(`${agentId}: missing source of truth ${path.relative(projectRoot, sourceOfTruth)}`);
40
+ }
41
+
42
+ for (const alias of legacy.collectAgentAliases(agentId, agentSpec)) {
43
+ const owner = seenAgentAliases.get(alias);
44
+ if (owner && owner !== agentId) errors.push(`duplicate agent alias "${alias}" claimed by ${owner} and ${agentId}`);
45
+ else seenAgentAliases.set(alias, agentId);
46
+ }
47
+
48
+ const seenCommandAliases = new Map();
49
+ for (const [commandId, commandSpec] of Object.entries(agentSpec.commands || {})) {
50
+ commandCount += 1;
51
+ const targetPath = resolveRegistryPath(projectRoot, commandSpec.target);
52
+ if (!targetPath) {
53
+ errors.push(`${agentId}.${commandId}: invalid or missing target`);
54
+ } else if (!fs.existsSync(targetPath)) {
55
+ errors.push(`${agentId}.${commandId}: missing target ${path.relative(projectRoot, targetPath)}`);
56
+ }
57
+ for (const resource of commandSpec.resources || []) {
58
+ const resourcePath = resolveRegistryPath(projectRoot, resource);
59
+ if (!resourcePath) {
60
+ errors.push(`${agentId}.${commandId}: invalid or missing resource`);
61
+ } else if (!fs.existsSync(resourcePath)) {
62
+ errors.push(`${agentId}.${commandId}: missing resource ${path.relative(projectRoot, resourcePath)}`);
63
+ }
64
+ }
65
+ for (const alias of legacy.collectCommandAliases(commandId, commandSpec)) {
66
+ const owner = seenCommandAliases.get(alias);
67
+ if (owner && owner !== commandId) errors.push(`duplicate command alias "${alias}" claimed by ${owner} and ${commandId}`);
68
+ else seenCommandAliases.set(alias, commandId);
69
+ }
70
+ }
71
+ }
72
+
73
+ legacy.validateRequiredCoverage(registry, requiredCoverage, errors);
74
+ return {
75
+ ok: errors.length === 0,
76
+ errors,
77
+ warnings: [],
78
+ metrics: {
79
+ agents: Object.keys(registry.agents || {}).length,
80
+ commands: commandCount,
81
+ registryPath: path.relative(projectRoot, registryPath),
82
+ },
83
+ };
84
+ }
85
+
86
+ function main() {
87
+ const args = legacy.parseArgs();
88
+ const result = validateCodexCommandRegistry(args);
89
+ if (!args.quiet) {
90
+ console.log(args.json ? JSON.stringify(result, null, 2) : legacy.formatHumanReport(result));
91
+ }
92
+ if (!result.ok) process.exitCode = 1;
93
+ }
94
+
95
+ if (require.main === module) main();
96
+
97
+ module.exports = {
98
+ ...legacy,
99
+ resolveRegistryPath,
100
+ validateCodexCommandRegistry,
101
+ };
@@ -116,6 +116,76 @@ const FORBIDDEN_PATTERNS = [
116
116
  reason:
117
117
  'Internal script — must be invoked internally by `npx sinapse-ai install`, never shown as a user-facing command.',
118
118
  },
119
+ {
120
+ id: 'legacy-codex-skills-flow',
121
+ regex: /(?:(?<![.:\w])codex\b[^\n]{0,80}(?<![A-Za-z0-9._-])\/skills\b|(?<![A-Za-z0-9._-])\/skills\b[^\n]{0,40}(?:flow|activation|ativacao|ativação|activate|ativar|agent))/i,
122
+ reason: 'Codex activation uses `$snps` or `$sinapse-agent <id>`, not `/skills`.',
123
+ },
124
+ {
125
+ id: 'legacy-custom-agent-slash-command',
126
+ regex: /\/my-agent\b/,
127
+ reason: 'Do not document `/my-agent`; use the provider-native SINAPSE activator.',
128
+ },
129
+ {
130
+ id: 'legacy-dev-slash-activation',
131
+ regex: /(?:^\s*\/dev\s*$|(?:activate|ativar|activation|ativacao|ativação|agent commands?|comandos? de agentes)[^\n]{0,80}\/dev\b)/i,
132
+ reason: 'Use `@developer` in Claude Code or `$sinapse-agent developer` in Codex.',
133
+ },
134
+ {
135
+ id: 'legacy-dev-at-activation',
136
+ regex: /(?:^\s*(?:\|\s*)?`?@dev\b|(?:activate|ativar|activation|ativacao|ativação|agent commands?|comandos? de agentes)[^\n]{0,80}@dev\b)/i,
137
+ reason: 'Use `@developer`; the short `@dev` activation is obsolete.',
138
+ },
139
+ {
140
+ id: 'legacy-codex-at-activation',
141
+ regex: /codex[^\n]{0,100}(?:activate|ativar|activation|ativacao|ativação|shortcut|atalho|use)[^\n]{0,50}@(?:<[^>]+>|[a-z][\w-]*)/i,
142
+ reason: 'Codex activation uses `$snps` or `$sinapse-agent <id>`, not `@agent`.',
143
+ },
144
+ {
145
+ id: 'unresolvable-init-project-status',
146
+ regex: /\*init-project-status\b/i,
147
+ reason: '`init-project-status` has no public resolvable task pointer; project status loads automatically.',
148
+ },
149
+ {
150
+ id: 'legacy-sinapse-fullstack-name',
151
+ regex: /\bSINAPSE-FullStack\b/i,
152
+ reason: 'The current public framework name is SINAPSE AI.',
153
+ },
154
+ {
155
+ id: 'legacy-developer-codename',
156
+ regex: /(?:\bDex\s*(?:\(|the\s+)Builder\b|\b(?:developer agent|agente desenvolvedor)\s*\(\s*Dex\s*\)|@developer\s*\(\s*Dex\s*\))/i,
157
+ reason: 'The current developer persona is Pixel the Builder.',
158
+ },
159
+ {
160
+ id: 'unsupported-provider-full-support',
161
+ regex: /\b(?:Gemini\s+CLI|GitHub\s+Copilot)\b/i,
162
+ reason: 'Current public installation support is limited to Claude Code and Codex.',
163
+ },
164
+ {
165
+ id: 'unsupported-cursor-provider',
166
+ regex: /\bCursor\b/i,
167
+ reason: 'Current public installation support is limited to Claude Code and Codex.',
168
+ },
169
+ {
170
+ id: 'legacy-cursor-install-option',
171
+ regex: /--ide\s+cursor\b/i,
172
+ reason: 'The current installer generates Claude Code and Codex adapters; do not document the retired Cursor selector.',
173
+ },
174
+ {
175
+ id: 'legacy-cursor-rules-path',
176
+ regex: /\.cursor[\\/]rules[\\/]/i,
177
+ reason: 'Current provider adapter paths are `.claude/agents/`, `.codex/agents/`, and `.agents/skills/`.',
178
+ },
179
+ {
180
+ id: 'claude-commands-as-agent-config',
181
+ regex: /(?:\.claude[\\/]commands[\\/](?:SINAPSE[\\/]|[^\n]{0,60}(?:agent\s+configuration|configura(?:ç|c)ão\s+de\s+agentes|para\s+Claude\s+Code))|Claude\s+Code\s+(?:uses|usa)[^\n]{0,60}\.claude[\\/]commands[\\/])/i,
182
+ reason: 'Claude Code agent adapters live in `.claude/agents/`; `.claude/commands/` is not the agent configuration path.',
183
+ },
184
+ {
185
+ id: 'legacy-agent-name-slash-activation',
186
+ regex: /\/(?:agent-name|nome-do-agente)\b/i,
187
+ reason: 'Use `@agent-id` in Claude Code or `$sinapse-agent <id>` in Codex.',
188
+ },
119
189
  ];
120
190
 
121
191
  // ── Paths ────────────────────────────────────────────────────────────────────
@@ -124,7 +194,7 @@ const FORBIDDEN_PATTERNS = [
124
194
  * Directories whose `.md` files are public user-facing installation docs.
125
195
  * Everything in these trees is scanned.
126
196
  */
127
- const PUBLIC_DOC_DIRS = ['docs/installation'];
197
+ const PUBLIC_DOC_DIRS = ['docs/installation', 'docs/pt/installation'];
128
198
 
129
199
  /**
130
200
  * Additional top-level public doc files (relative to root).
@@ -133,7 +203,18 @@ const PUBLIC_DOC_DIRS = ['docs/installation'];
133
203
  * Quick Start commands (including `agents`/`ideate`) and are exactly where
134
204
  * the drift was found; bring them under the same guard as README.
135
205
  */
136
- const PUBLIC_DOC_FILES = ['README.md', 'docs/guides/user-guide.md', 'docs/pt/guides/user-guide.md'];
206
+ const PUBLIC_DOC_FILES = [
207
+ 'README.md',
208
+ 'README.en.md',
209
+ 'docs/getting-started.md',
210
+ 'docs/troubleshooting.md',
211
+ 'docs/guides/user-guide.md',
212
+ 'docs/pt/guides/user-guide.md',
213
+ 'docs/guides/codex-config.md',
214
+ 'docs/guides/ide-integration.md',
215
+ 'docs/guides/project-status-feature.md',
216
+ 'docs/pt/guides/project-status-feature.md',
217
+ ];
137
218
 
138
219
  /**
139
220
  * Path fragments that identify historical / release notes. Any file whose
@@ -276,7 +357,10 @@ function scanFile(rootDir, relPath) {
276
357
  if (allowed.has(i)) continue;
277
358
  const line = lines[i];
278
359
  for (const rule of FORBIDDEN_PATTERNS) {
279
- const match = line.match(rule.regex);
360
+ const candidate = rule.id === 'legacy-codex-skills-flow'
361
+ ? line.replace(/https?:\/\/[^\s)>]+/gi, (url) => ' '.repeat(url.length))
362
+ : line;
363
+ const match = candidate.match(rule.regex);
280
364
  if (match) {
281
365
  violations.push({
282
366
  file: relPath,