sinapse-ai 7.7.2 → 7.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/.claude/hooks/enforce-git-push-authority.sh +34 -2
  2. package/.claude/rules/safe-collaboration.md +12 -1
  3. package/.codex/catalog.json +157 -0
  4. package/.codex/command-registry.json +441 -0
  5. package/.codex/scripts/generate-codex-greeting.js +101 -0
  6. package/.codex/scripts/resolve-codex-command.js +147 -0
  7. package/.codex/skills/sinapse-analyst/SKILL.md +5 -4
  8. package/.codex/skills/sinapse-architect/SKILL.md +5 -4
  9. package/.codex/skills/sinapse-data-engineer/SKILL.md +5 -4
  10. package/.codex/skills/sinapse-dev/SKILL.md +5 -4
  11. package/.codex/skills/sinapse-devops/SKILL.md +5 -4
  12. package/.codex/skills/sinapse-orqx/SKILL.md +10 -15
  13. package/.codex/skills/sinapse-pm/SKILL.md +5 -4
  14. package/.codex/skills/sinapse-po/SKILL.md +4 -3
  15. package/.codex/skills/sinapse-qa/SKILL.md +12 -11
  16. package/.codex/skills/sinapse-sm/SKILL.md +5 -4
  17. package/.codex/skills/sinapse-squad-creator/SKILL.md +5 -4
  18. package/.codex/skills/sinapse-ux-design-expert/SKILL.md +5 -4
  19. package/.codex/tasks/convene-sinapse-council.md +28 -0
  20. package/.codex/tasks/create-sinapse-strategic-brief.md +29 -0
  21. package/.codex/tasks/onboard-sinapse-codex.md +34 -0
  22. package/.codex/tasks/plan-sinapse-initiative.md +33 -0
  23. package/.codex/tasks/resolve-sinapse-conflict.md +28 -0
  24. package/.codex/tasks/route-sinapse-request.md +33 -0
  25. package/.codex/tasks/status-sinapse-capabilities.md +28 -0
  26. package/.sinapse-ai/core-config.yaml +1 -1
  27. package/.sinapse-ai/data/entity-registry.yaml +903 -805
  28. package/.sinapse-ai/data/registry-update-log.jsonl +10 -0
  29. package/.sinapse-ai/infrastructure/scripts/codex-parity/catalog.js +123 -0
  30. package/.sinapse-ai/infrastructure/scripts/codex-skills-sync/index.js +60 -11
  31. package/.sinapse-ai/infrastructure/scripts/codex-skills-sync/validate.js +44 -16
  32. package/.sinapse-ai/infrastructure/scripts/sync-codex-local-first.js +156 -0
  33. package/.sinapse-ai/infrastructure/scripts/validate-codex-command-registry.js +264 -0
  34. package/.sinapse-ai/infrastructure/scripts/validate-codex-integration.js +15 -6
  35. package/.sinapse-ai/infrastructure/scripts/validate-codex-sync.js +156 -0
  36. package/.sinapse-ai/infrastructure/scripts/validate-parity.js +3 -1
  37. package/.sinapse-ai/infrastructure/scripts/validate-paths.js +8 -10
  38. package/.sinapse-ai/infrastructure/templates/safe-collab/README.md +52 -17
  39. package/.sinapse-ai/infrastructure/templates/safe-collab/apply.sh +85 -0
  40. package/.sinapse-ai/infrastructure/templates/safe-collab/safe-collaboration-rule.md +11 -0
  41. package/.sinapse-ai/install-manifest.yaml +41 -21
  42. package/.sinapse-ai/project-config.yaml +1 -1
  43. package/bin/utils/collab-start.js +267 -0
  44. package/bin/utils/git-branch-guard.js +76 -0
  45. package/bin/utils/pre-push-safety.js +110 -0
  46. package/bin/utils/staged-secret-scan.js +108 -0
  47. package/docs/ORQX-PLAN.md +3 -2
  48. package/docs/codex-parity-program.md +670 -0
  49. package/docs/codex-total-parity-orchestration-plan.md +301 -0
  50. package/docs/codex-workflow-task-parity.md +87 -0
  51. package/docs/collaboration-autonomy-plan.md +243 -0
  52. package/docs/guides/framework-contributor-mode.md +310 -0
  53. package/docs/guides/parallel-collaboration-source-of-truth.md +481 -0
  54. package/package.json +11 -3
  55. package/packages/installer/tests/unit/entity-registry-bootstrap.test.js +2 -2
  56. package/scripts/ensure-manifest.js +9 -0
@@ -0,0 +1,264 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+
7
+ const REQUIRED_COMMAND_COVERAGE = Object.freeze({
8
+ 'sinapse-orqx': ['onboard', 'route', 'plan', 'status', 'brief', 'resolve', 'council'],
9
+ 'sinapse-pm': [
10
+ 'create-prd',
11
+ 'create-brownfield-prd',
12
+ 'create-epic',
13
+ 'create-story',
14
+ 'research',
15
+ 'execute-epic',
16
+ 'gather-requirements',
17
+ 'write-spec',
18
+ 'shard-prd',
19
+ ],
20
+ 'sinapse-po': [
21
+ 'validate-story',
22
+ 'validate-story-draft',
23
+ 'backlog-review',
24
+ 'backlog-prioritize',
25
+ 'backlog-schedule',
26
+ 'close-story',
27
+ 'sync-story',
28
+ 'pull-story',
29
+ 'stories-index',
30
+ ],
31
+ 'sinapse-sm': ['draft', 'story-checklist'],
32
+ 'sinapse-dev': [
33
+ 'develop',
34
+ 'run-tests',
35
+ 'apply-qa-fixes',
36
+ 'execute-subtask',
37
+ 'verify-subtask',
38
+ 'build-autonomous',
39
+ 'build-resume',
40
+ 'build-status',
41
+ 'build',
42
+ ],
43
+ 'sinapse-qa': [
44
+ 'review',
45
+ 'review-story',
46
+ 'code-review',
47
+ 'gate',
48
+ 'review-build',
49
+ 'create-fix-request',
50
+ 'test-design',
51
+ 'generate-tests',
52
+ 'run-tests',
53
+ 'nfr-assess',
54
+ 'validate-libraries',
55
+ 'security-check',
56
+ 'validate-migrations',
57
+ 'evidence-check',
58
+ 'false-positive-check',
59
+ 'console-check',
60
+ ],
61
+ });
62
+
63
+ function parseArgs(argv = process.argv.slice(2)) {
64
+ const args = new Set(argv);
65
+ return {
66
+ quiet: args.has('--quiet') || args.has('-q'),
67
+ json: args.has('--json'),
68
+ };
69
+ }
70
+
71
+ function normalizeAgentAlias(value) {
72
+ return String(value || '').trim().replace(/^@/, '').toLowerCase();
73
+ }
74
+
75
+ function normalizeCommandAlias(value) {
76
+ return String(value || '').trim().replace(/^\*/, '').toLowerCase();
77
+ }
78
+
79
+ function collectAgentAliases(agentId, agentSpec) {
80
+ return [agentId, ...(agentSpec.aliases || [])]
81
+ .map((alias) => normalizeAgentAlias(alias))
82
+ .filter(Boolean);
83
+ }
84
+
85
+ function collectCommandAliases(commandId, commandSpec) {
86
+ return [commandId, ...(commandSpec.aliases || [])]
87
+ .map((alias) => normalizeCommandAlias(alias))
88
+ .filter(Boolean);
89
+ }
90
+
91
+ function loadRegistry(projectRoot) {
92
+ const registryPath = path.join(projectRoot, '.codex', 'command-registry.json');
93
+ if (!fs.existsSync(registryPath)) {
94
+ return {
95
+ registryPath,
96
+ registry: null,
97
+ error: `Missing Codex command registry: ${path.relative(projectRoot, registryPath)}`,
98
+ };
99
+ }
100
+
101
+ try {
102
+ return {
103
+ registryPath,
104
+ registry: JSON.parse(fs.readFileSync(registryPath, 'utf8')),
105
+ error: null,
106
+ };
107
+ } catch (error) {
108
+ return {
109
+ registryPath,
110
+ registry: null,
111
+ error: `Unable to parse Codex command registry: ${error.message}`,
112
+ };
113
+ }
114
+ }
115
+
116
+ function validateRequiredCoverage(registry, requiredCoverage, errors) {
117
+ for (const [agentId, requiredCommands] of Object.entries(requiredCoverage || {})) {
118
+ const agentSpec = (registry.agents || {})[agentId];
119
+ if (!agentSpec) {
120
+ errors.push(`missing required agent coverage: ${agentId}`);
121
+ continue;
122
+ }
123
+
124
+ const availableCommands = new Set();
125
+ for (const [commandId, commandSpec] of Object.entries(agentSpec.commands || {})) {
126
+ for (const alias of collectCommandAliases(commandId, commandSpec)) {
127
+ availableCommands.add(alias);
128
+ }
129
+ }
130
+
131
+ for (const requiredCommand of requiredCommands) {
132
+ if (!availableCommands.has(normalizeCommandAlias(requiredCommand))) {
133
+ errors.push(`${agentId}: missing required command coverage for ${requiredCommand}`);
134
+ }
135
+ }
136
+ }
137
+ }
138
+
139
+ function validateCodexCommandRegistry(options = {}) {
140
+ const projectRoot = options.projectRoot || process.cwd();
141
+ const { registryPath, registry, error } = loadRegistry(projectRoot);
142
+ const errors = [];
143
+ const requiredCoverage =
144
+ options.requiredCoverage === false
145
+ ? null
146
+ : options.requiredCoverage || REQUIRED_COMMAND_COVERAGE;
147
+
148
+ if (error) {
149
+ errors.push(error);
150
+ return {
151
+ ok: false,
152
+ errors,
153
+ warnings: [],
154
+ metrics: { agents: 0, commands: 0 },
155
+ };
156
+ }
157
+
158
+ let commandCount = 0;
159
+ const seenAgentAliases = new Map();
160
+ for (const [agentId, agentSpec] of Object.entries(registry.agents || {})) {
161
+ const skillPath = path.join(projectRoot, '.codex', 'skills', agentSpec.skillId || agentId, 'SKILL.md');
162
+ if (!fs.existsSync(skillPath)) {
163
+ errors.push(`${agentId}: missing skill file ${path.relative(projectRoot, skillPath)}`);
164
+ }
165
+
166
+ const sourceOfTruth = path.join(projectRoot, agentSpec.sourceOfTruth || '');
167
+ if (!fs.existsSync(sourceOfTruth)) {
168
+ errors.push(`${agentId}: missing source of truth ${path.relative(projectRoot, sourceOfTruth)}`);
169
+ }
170
+
171
+ for (const alias of collectAgentAliases(agentId, agentSpec)) {
172
+ const owner = seenAgentAliases.get(alias);
173
+ if (owner && owner !== agentId) {
174
+ errors.push(`duplicate agent alias "${alias}" claimed by ${owner} and ${agentId}`);
175
+ } else {
176
+ seenAgentAliases.set(alias, agentId);
177
+ }
178
+ }
179
+
180
+ const seenCommandAliases = new Map();
181
+ for (const [commandId, commandSpec] of Object.entries(agentSpec.commands || {})) {
182
+ commandCount += 1;
183
+
184
+ const targetPath = path.join(projectRoot, commandSpec.target || '');
185
+ if (!fs.existsSync(targetPath)) {
186
+ errors.push(`${agentId}.${commandId}: missing target ${path.relative(projectRoot, targetPath)}`);
187
+ }
188
+
189
+ for (const resource of commandSpec.resources || []) {
190
+ const resourcePath = path.join(projectRoot, resource);
191
+ if (!fs.existsSync(resourcePath)) {
192
+ errors.push(`${agentId}.${commandId}: missing resource ${path.relative(projectRoot, resourcePath)}`);
193
+ }
194
+ }
195
+
196
+ for (const alias of collectCommandAliases(commandId, commandSpec)) {
197
+ const owner = seenCommandAliases.get(alias);
198
+ if (owner && owner !== commandId) {
199
+ errors.push(`${agentId}: duplicate command alias "${alias}" claimed by ${owner} and ${commandId}`);
200
+ } else {
201
+ seenCommandAliases.set(alias, commandId);
202
+ }
203
+ }
204
+ }
205
+ }
206
+
207
+ validateRequiredCoverage(registry, requiredCoverage, errors);
208
+
209
+ return {
210
+ ok: errors.length === 0,
211
+ errors,
212
+ warnings: [],
213
+ metrics: {
214
+ agents: Object.keys(registry.agents || {}).length,
215
+ commands: commandCount,
216
+ registryPath: path.relative(projectRoot, registryPath),
217
+ },
218
+ };
219
+ }
220
+
221
+ function formatHumanReport(result) {
222
+ if (result.ok) {
223
+ return `OK Codex command registry validation passed (agents: ${result.metrics.agents}, commands: ${result.metrics.commands})`;
224
+ }
225
+
226
+ return [
227
+ `X Codex command registry validation failed (${result.errors.length} issue(s))`,
228
+ ...result.errors.map((error) => `- ${error}`),
229
+ ].join('\n');
230
+ }
231
+
232
+ function main() {
233
+ const args = parseArgs();
234
+ const result = validateCodexCommandRegistry(args);
235
+
236
+ if (!args.quiet) {
237
+ if (args.json) {
238
+ console.log(JSON.stringify(result, null, 2));
239
+ } else {
240
+ console.log(formatHumanReport(result));
241
+ }
242
+ }
243
+
244
+ if (!result.ok) {
245
+ process.exitCode = 1;
246
+ }
247
+ }
248
+
249
+ if (require.main === module) {
250
+ main();
251
+ }
252
+
253
+ module.exports = {
254
+ parseArgs,
255
+ loadRegistry,
256
+ normalizeAgentAlias,
257
+ normalizeCommandAlias,
258
+ collectAgentAliases,
259
+ collectCommandAliases,
260
+ validateRequiredCoverage,
261
+ validateCodexCommandRegistry,
262
+ formatHumanReport,
263
+ REQUIRED_COMMAND_COVERAGE,
264
+ };
@@ -3,6 +3,10 @@
3
3
 
4
4
  const fs = require('fs');
5
5
  const path = require('path');
6
+ const {
7
+ loadCodexCatalogConfig,
8
+ getConfiguredSkillIds,
9
+ } = require('./codex-parity/catalog');
6
10
 
7
11
  function getDefaultOptions() {
8
12
  const projectRoot = process.cwd();
@@ -41,14 +45,15 @@ function countSkillFiles(skillsDir) {
41
45
 
42
46
  function validateCodexIntegration(options = {}) {
43
47
  const projectRoot = options.projectRoot || process.cwd();
48
+ const config = loadCodexCatalogConfig(projectRoot);
44
49
  const resolved = {
45
50
  ...getDefaultOptions(),
46
51
  ...options,
47
52
  projectRoot,
48
53
  instructionsFile: options.instructionsFile || path.join(projectRoot, 'AGENTS.md'),
49
- agentsDir: options.agentsDir || path.join(projectRoot, '.codex', 'agents'),
50
- skillsDir: options.skillsDir || path.join(projectRoot, '.codex', 'skills'),
51
- sourceAgentsDir: options.sourceAgentsDir || path.join(projectRoot, '.sinapse-ai', 'development', 'agents'),
54
+ agentsDir: options.agentsDir || path.join(projectRoot, config.codexAgentsDir || '.codex/agents'),
55
+ skillsDir: options.skillsDir || path.join(projectRoot, config.skillsDir || '.codex/skills'),
56
+ sourceAgentsDir: options.sourceAgentsDir || path.join(projectRoot, config.canonicalAgentsDir || '.sinapse-ai/development/agents'),
52
57
  };
53
58
  const errors = [];
54
59
  const warnings = [];
@@ -70,12 +75,15 @@ function validateCodexIntegration(options = {}) {
70
75
  const sourceCount = countMarkdownFiles(resolved.sourceAgentsDir);
71
76
  const codexAgentsCount = countMarkdownFiles(resolved.agentsDir);
72
77
  const codexSkillsCount = countSkillFiles(resolved.skillsDir);
78
+ const expectedSkillIds = getConfiguredSkillIds(config);
73
79
 
74
- if (sourceCount > 0 && codexAgentsCount !== sourceCount) {
80
+ if (config.catalogMode !== 'expanded' && sourceCount > 0 && codexAgentsCount !== sourceCount) {
75
81
  warnings.push(`Codex agent count differs from source (${codexAgentsCount}/${sourceCount})`);
76
82
  }
77
83
 
78
- if (sourceCount > 0 && codexSkillsCount !== sourceCount) {
84
+ if (expectedSkillIds && codexSkillsCount !== expectedSkillIds.length) {
85
+ warnings.push(`Codex skill count differs from configured catalog (${codexSkillsCount}/${expectedSkillIds.length})`);
86
+ } else if (!expectedSkillIds && sourceCount > 0 && codexSkillsCount !== sourceCount) {
79
87
  warnings.push(`Codex skill count differs from source (${codexSkillsCount}/${sourceCount})`);
80
88
  }
81
89
 
@@ -84,9 +92,10 @@ function validateCodexIntegration(options = {}) {
84
92
  errors,
85
93
  warnings,
86
94
  metrics: {
87
- sourceAgents: sourceCount,
95
+ canonicalAgents: sourceCount,
88
96
  codexAgents: codexAgentsCount,
89
97
  codexSkills: codexSkillsCount,
98
+ expectedSkills: expectedSkillIds ? expectedSkillIds.length : sourceCount,
90
99
  },
91
100
  };
92
101
  }
@@ -0,0 +1,156 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const { spawnSync } = require('child_process');
7
+ const { loadCodexCatalogConfig } = require('./codex-parity/catalog');
8
+ const { validateCodexCommandRegistry } = require('./validate-codex-command-registry');
9
+ const { validateCodexIntegration } = require('./validate-codex-integration');
10
+ const { validateCodexSkills } = require('./codex-skills-sync/validate');
11
+ const { validatePaths } = require('./validate-paths');
12
+
13
+ function parseArgs(argv = process.argv.slice(2)) {
14
+ const args = new Set(argv);
15
+ return {
16
+ quiet: args.has('--quiet') || args.has('-q'),
17
+ json: args.has('--json'),
18
+ };
19
+ }
20
+
21
+ function runLegacyCodexValidate(projectRoot, options = {}) {
22
+ const args = [
23
+ path.join('.sinapse-ai', 'infrastructure', 'scripts', 'ide-sync', 'index.js'),
24
+ 'validate',
25
+ '--ide',
26
+ 'codex',
27
+ '--strict',
28
+ ];
29
+
30
+ if (options.quiet) args.push('--quiet');
31
+
32
+ const result = spawnSync(process.execPath, args, {
33
+ cwd: projectRoot,
34
+ encoding: 'utf8',
35
+ });
36
+
37
+ return {
38
+ ok: result.status === 0,
39
+ mode: 'canonical',
40
+ checks: [],
41
+ errors: result.status === 0 ? [] : ['Legacy Codex sync validation failed'],
42
+ warnings: [],
43
+ raw: [result.stdout, result.stderr].filter(Boolean).join('\n').trim(),
44
+ };
45
+ }
46
+
47
+ function normalizeCheck(id, input) {
48
+ return {
49
+ id,
50
+ ok: Boolean(input?.ok),
51
+ errors: Array.isArray(input?.errors) ? input.errors : [],
52
+ warnings: Array.isArray(input?.warnings) ? input.warnings : [],
53
+ metrics: input?.metrics || {},
54
+ };
55
+ }
56
+
57
+ function validateCodexSync(options = {}, deps = {}) {
58
+ const projectRoot = options.projectRoot || process.cwd();
59
+ const config = loadCodexCatalogConfig(projectRoot);
60
+ const runLegacyValidate = deps.runLegacyCodexValidate || runLegacyCodexValidate;
61
+ const runCodexCommands = deps.validateCodexCommandRegistry || validateCodexCommandRegistry;
62
+ const runCodexIntegration = deps.validateCodexIntegration || validateCodexIntegration;
63
+ const runCodexSkills = deps.validateCodexSkills || validateCodexSkills;
64
+ const runPaths = deps.validatePaths || validatePaths;
65
+
66
+ if (config.catalogMode !== 'expanded') {
67
+ return runLegacyValidate(projectRoot, options);
68
+ }
69
+
70
+ const catalogPath = path.join(projectRoot, '.codex', 'catalog.json');
71
+ const checks = [
72
+ normalizeCheck('codex-integration', runCodexIntegration({ projectRoot, quiet: true })),
73
+ normalizeCheck('codex-commands', runCodexCommands({ projectRoot, quiet: true })),
74
+ normalizeCheck('codex-skills', runCodexSkills({ projectRoot, strict: true, quiet: true })),
75
+ normalizeCheck('paths', runPaths({ projectRoot, quiet: true })),
76
+ ];
77
+
78
+ if (!fs.existsSync(catalogPath)) {
79
+ checks.unshift({
80
+ id: 'codex-catalog',
81
+ ok: false,
82
+ errors: ['Missing Codex catalog file: .codex/catalog.json'],
83
+ warnings: [],
84
+ metrics: {},
85
+ });
86
+ } else {
87
+ checks.unshift({
88
+ id: 'codex-catalog',
89
+ ok: true,
90
+ errors: [],
91
+ warnings: [],
92
+ metrics: {},
93
+ });
94
+ }
95
+
96
+ return {
97
+ ok: checks.every((check) => check.ok),
98
+ mode: 'expanded',
99
+ checks,
100
+ errors: checks.flatMap((check) => check.errors.map((error) => `${check.id}: ${error}`)),
101
+ warnings: checks.flatMap((check) => check.warnings.map((warning) => `${check.id}: ${warning}`)),
102
+ };
103
+ }
104
+
105
+ function formatHumanReport(result) {
106
+ if (result.mode === 'canonical' && result.raw) {
107
+ return result.raw;
108
+ }
109
+
110
+ const lines = [
111
+ result.ok
112
+ ? 'OK Codex sync validation passed'
113
+ : `X Codex sync validation failed (${result.errors.length} issue(s))`,
114
+ ];
115
+
116
+ for (const check of result.checks || []) {
117
+ lines.push(`${check.ok ? 'OK' : 'X'} ${check.id}`);
118
+ if (check.errors.length > 0) {
119
+ lines.push(...check.errors.map((error) => `- ${error}`));
120
+ }
121
+ if (check.warnings.length > 0) {
122
+ lines.push(...check.warnings.map((warning) => `! ${warning}`));
123
+ }
124
+ }
125
+
126
+ return lines.join('\n');
127
+ }
128
+
129
+ function main() {
130
+ const args = parseArgs();
131
+ const result = validateCodexSync(args);
132
+
133
+ if (!args.quiet) {
134
+ if (args.json) {
135
+ console.log(JSON.stringify(result, null, 2));
136
+ } else {
137
+ console.log(formatHumanReport(result));
138
+ }
139
+ }
140
+
141
+ if (!result.ok) {
142
+ process.exitCode = 1;
143
+ }
144
+ }
145
+
146
+ if (require.main === module) {
147
+ main();
148
+ }
149
+
150
+ module.exports = {
151
+ parseArgs,
152
+ validateCodexSync,
153
+ formatHumanReport,
154
+ runLegacyCodexValidate,
155
+ normalizeCheck,
156
+ };
@@ -6,6 +6,7 @@ const path = require('path');
6
6
  const yaml = require('js-yaml');
7
7
  const { spawnSync } = require('child_process');
8
8
  const { validateClaudeIntegration } = require('./validate-claude-integration');
9
+ const { validateCodexSync } = require('./validate-codex-sync');
9
10
  const { validateCodexIntegration } = require('./validate-codex-integration');
10
11
  const { validateCodexSkills } = require('./codex-skills-sync/validate');
11
12
  const { validatePaths } = require('./validate-paths');
@@ -218,6 +219,7 @@ function diffCompatibilityContracts(currentContract, previousContract) {
218
219
  function runParityValidation(options = {}, deps = {}) {
219
220
  const projectRoot = options.projectRoot || process.cwd();
220
221
  const runSync = deps.runSyncValidate || runSyncValidate;
222
+ const runCodexSync = deps.validateCodexSync || validateCodexSync;
221
223
  const runClaudeIntegration = deps.validateClaudeIntegration || validateClaudeIntegration;
222
224
  const runCodexIntegration = deps.validateCodexIntegration || validateCodexIntegration;
223
225
  const runCodexSkills = deps.validateCodexSkills || validateCodexSkills;
@@ -234,7 +236,7 @@ function runParityValidation(options = {}, deps = {}) {
234
236
  const checks = [
235
237
  { id: 'claude-sync', exec: () => runSync('claude-code', projectRoot) },
236
238
  { id: 'claude-integration', exec: () => runClaudeIntegration({ projectRoot }) },
237
- { id: 'codex-sync', exec: () => runSync('codex', projectRoot) },
239
+ { id: 'codex-sync', exec: () => runCodexSync({ projectRoot, quiet: true }) },
238
240
  { id: 'codex-integration', exec: () => runCodexIntegration({ projectRoot }) },
239
241
  { id: 'codex-skills', exec: () => runCodexSkills({ projectRoot, strict: true, quiet: true }) },
240
242
  { id: 'paths', exec: () => runPaths({ projectRoot }) },
@@ -3,6 +3,10 @@
3
3
 
4
4
  const fs = require('fs');
5
5
  const path = require('path');
6
+ const {
7
+ loadCodexCatalogConfig,
8
+ validateSkillActivationPaths,
9
+ } = require('./codex-parity/catalog');
6
10
 
7
11
  const FORBIDDEN_ABSOLUTE_PATTERNS = [
8
12
  /\/Users\/[^\s/'"]+/g,
@@ -54,21 +58,15 @@ function collectAbsolutePathViolations(content, filePath) {
54
58
  return errors;
55
59
  }
56
60
 
57
- function validateSkillPathConventions(content, filePath) {
58
- const errors = [];
59
- if (!content.includes('.sinapse-ai/development/agents/')) {
60
- errors.push(`${filePath} missing canonical source path ".sinapse-ai/development/agents/"`);
61
- }
62
- if (!content.includes('.sinapse-ai/development/scripts/generate-greeting.js')) {
63
- errors.push(`${filePath} missing canonical greeting script path`);
64
- }
65
- return errors;
61
+ function validateSkillPathConventions(content, filePath, config) {
62
+ return validateSkillActivationPaths(content, filePath, config);
66
63
  }
67
64
 
68
65
  function validatePaths(options = {}) {
69
66
  const resolved = { ...getDefaultOptions(), ...options };
70
67
  const errors = [];
71
68
  const checkedFiles = [];
69
+ const config = loadCodexCatalogConfig(resolved.projectRoot);
72
70
 
73
71
  const skillFiles = listSkillFiles(resolved.skillsDir);
74
72
  const filesToCheck = [...resolved.requiredFiles, ...skillFiles];
@@ -90,7 +88,7 @@ function validatePaths(options = {}) {
90
88
  errors.push(...collectAbsolutePathViolations(content, path.relative(resolved.projectRoot, file)));
91
89
 
92
90
  if (file.endsWith('SKILL.md')) {
93
- errors.push(...validateSkillPathConventions(content, path.relative(resolved.projectRoot, file)));
91
+ errors.push(...validateSkillPathConventions(content, path.relative(resolved.projectRoot, file), config));
94
92
  }
95
93
  }
96
94
 
@@ -2,38 +2,73 @@
2
2
 
3
3
  Template reutilizavel para configurar colaboracao segura em qualquer projeto.
4
4
 
5
- ## Uso
5
+ ## Fonte canonica
6
6
 
7
- Copie os arquivos deste diretorio para o projeto alvo:
7
+ Para a politica completa e atualizada de colaboracao paralela, use:
8
8
 
9
- ```bash
10
- # 1. Regra para agentes Claude Code
11
- cp safe-collaboration-rule.md <projeto>/.claude/rules/safe-collaboration.md
9
+ - `docs/guides/parallel-collaboration-source-of-truth.md`
10
+
11
+ Este README explica o template. O documento acima define a regra mestra.
12
+
13
+ ## Uso rapido
12
14
 
13
- # 2. Guia para a equipe
14
- cp parallel-workflow-guide.md <projeto>/docs/guides/parallel-workflow.md
15
+ ```bash
16
+ bash apply.sh <projeto> <owner-github> <collab-github> [prefix1] [prefix2]
17
+ ```
15
18
 
16
- # 3. CODEOWNERS (ajuste os usernames)
17
- cp CODEOWNERS.template <projeto>/.github/CODEOWNERS
19
+ ### Exemplo
18
20
 
19
- # 4. PR template simplificado
20
- cp pull_request_template.md <projeto>/.github/PULL_REQUEST_TEMPLATE.md
21
+ ```bash
22
+ bash .sinapse-ai/infrastructure/templates/safe-collab/apply.sh \
23
+ /path/to/meu-projeto \
24
+ caioimori \
25
+ Matheus-soier \
26
+ caio \
27
+ soier
21
28
  ```
22
29
 
23
- ## Configuracao no GitHub
30
+ Isso cria automaticamente:
31
+ - `.claude/rules/safe-collaboration.md` — regras para agentes
32
+ - `.github/PULL_REQUEST_TEMPLATE.md` — template simplificado de PR
33
+ - `.github/CODEOWNERS` — code owners configurado
34
+ - `docs/guides/parallel-workflow.md` — guia para equipe
35
+ - Atualiza `.gitignore` com patterns de runtime
24
36
 
25
- Apos copiar os arquivos, configure no repositorio:
37
+ ## Configuracao manual no GitHub
26
38
 
27
- 1. Settings > Rules > Rulesets > New ruleset
28
- - Target: `main`
39
+ Apos rodar o script, configure no repositorio:
40
+
41
+ 1. **Settings > Rules > Rulesets > New ruleset** (target: `main`)
29
42
  - Block direct pushes
30
43
  - Require 1 PR approval
31
44
  - Block force pushes
32
45
  - Block branch deletion
33
46
  - Dismiss stale reviews
34
47
 
35
- 2. Settings > Collaborators
48
+ 2. **Settings > Collaborators**
36
49
  - Adicionar membros com permissao `Write` (nunca Admin)
37
50
 
38
- 3. Settings > General
51
+ 3. **Settings > General**
39
52
  - Marcar "Automatically delete head branches"
53
+
54
+ ## Arquivos incluidos
55
+
56
+ | Arquivo | Proposito |
57
+ |---------|-----------|
58
+ | `apply.sh` | Script de aplicacao automatica |
59
+ | `safe-collaboration-rule.md` | Regra para `.claude/rules/` |
60
+ | `parallel-workflow-guide.md` | Guia para equipe |
61
+ | `CODEOWNERS.template` | Template de code owners |
62
+ | `pull_request_template.md` | PR template simplificado |
63
+
64
+ ## Placeholders
65
+
66
+ | Placeholder | Descricao | Exemplo |
67
+ |-------------|-----------|---------|
68
+ | `{{USER_1}}` | Nome do usuario 1 | Caio |
69
+ | `{{USER_2}}` | Nome do usuario 2 | Matheus |
70
+ | `{{user1}}` | Prefixo de branch user 1 | caio |
71
+ | `{{user2}}` | Prefixo de branch user 2 | soier |
72
+ | `{{OWNER_GITHUB}}` | Username GitHub do owner | caioimori |
73
+ | `{{COLLAB_GITHUB}}` | Username GitHub do collab | Matheus-soier |
74
+ | `{{PROJECT_NAME}}` | Nome do projeto | meu-projeto |