sinapse-ai 1.9.1 → 1.10.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.
- package/.sinapse-ai/install-manifest.yaml +1 -1
- package/AGENTS.md +2 -2
- package/CHANGELOG.md +13 -3
- package/README.en.md +4 -3
- package/README.md +5 -6
- package/bin/commands/install.js +6 -3
- package/bin/sinapse.js +20 -0
- package/package.json +1 -1
- package/packages/installer/src/installer/sinapse-ai-installer.js +32 -7
- package/scripts/validate-article-vii.js +68 -7
package/AGENTS.md
CHANGED
|
@@ -23,14 +23,14 @@ These rules are HARD CONSTRAINTS. They apply to every action you take, in any ID
|
|
|
23
23
|
|
|
24
24
|
## Project Context
|
|
25
25
|
|
|
26
|
-
SINAPSE is a meta-framework that orchestrates AI agents into specialized squads for complex development workflows. It runs inside Claude Code and enforces a formal Constitution with
|
|
26
|
+
SINAPSE is a meta-framework that orchestrates AI agents into specialized squads for complex development workflows. It runs inside Claude Code and enforces a formal Constitution with 11 articles governing CLI-first architecture, agent authority, documentation-first development, security, and safe collaboration.
|
|
27
27
|
|
|
28
28
|
### Architecture
|
|
29
29
|
|
|
30
30
|
- **CLI First** -- All intelligence lives in the CLI. Dashboards observe, never control.
|
|
31
31
|
- **4-Layer Boundary** -- L1 (core, immutable) / L2 (templates, extend-only) / L3 (config, mutable) / L4 (runtime, always modify).
|
|
32
32
|
- **Documentation-First** -- No code without a validated story. Pipeline: Epic -> Story -> Validation -> Implementation.
|
|
33
|
-
- **Constitution** --
|
|
33
|
+
- **Constitution** -- 11 articles with automatic gates that block violations. See `.sinapse-ai/constitution.md`.
|
|
34
34
|
|
|
35
35
|
### Project Structure
|
|
36
36
|
|
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
|
-
## [1.
|
|
1
|
+
## [1.10.0](https://github.com/caioimori/sinapse-ai/compare/1.9.1...1.10.0) (2026-06-18)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
* **article-vii:** guarda contagem de artigos da Constitution contra drift ([c010fda](https://github.com/caioimori/sinapse-ai/commit/c010fda484a8d2eb0292ee0c583a60ae1d13a745))
|
|
6
|
+
* **cli:** torna todos os comandos descobriveis no --help + guard de regressao ([1eba7d5](https://github.com/caioimori/sinapse-ai/commit/1eba7d5bb746dd241282e5d33aaa890f52d6d156))
|
|
2
7
|
|
|
3
8
|
### Bug Fixes
|
|
4
9
|
|
|
5
|
-
* **
|
|
6
|
-
* **
|
|
10
|
+
* **install:** reinstall preserva arquivos L3 user-owned (core-config, MEMORY.md) ([b2ab1a5](https://github.com/caioimori/sinapse-ai/commit/b2ab1a56025f12fc9a2123355541eaa41cbbf74b))
|
|
11
|
+
* **security:** corrige 2 vulns moderadas que chegam a quem instala (js-yaml, tar) ([947694b](https://github.com/caioimori/sinapse-ai/commit/947694b8eee3f6ad6da6bb342a73c78fe8ab2658))
|
|
12
|
+
* **security:** elimina interpolacao de shell no setx PATH do install (Windows) ([a4e9cae](https://github.com/caioimori/sinapse-ai/commit/a4e9caeda475b5873169151f811aa1ed92ff8181))
|
|
13
|
+
|
|
14
|
+
### Documentation
|
|
15
|
+
|
|
16
|
+
* corrige contagem da Constitution no README (10->11 artigos, 6->7 NON-NEGOTIABLE) ([40713a5](https://github.com/caioimori/sinapse-ai/commit/40713a51dceda3477fd4b33750e4cfe188cff086))
|
|
7
17
|
|
|
8
18
|
# Changelog
|
|
9
19
|
|
package/README.en.md
CHANGED
|
@@ -33,7 +33,7 @@ Generative AI has a known problem: the more you ask of it, the worse it gets. A
|
|
|
33
33
|
|
|
34
34
|
SINAPSE solves this the way human teams solve it: **coordinated specialization**. Instead of one tired generalist, you have 172 agents in 17 squads, each with a defined role, its own knowledge base, and executable tasks. An orchestrator routes your request to whoever actually knows how to solve it -- automatically, without you needing to memorize agent names or commands.
|
|
35
35
|
|
|
36
|
-
The differential isn't just the quantity of agents. It's **real governance**: 19 active hooks intercept operations at runtime, a Constitution with
|
|
36
|
+
The differential isn't just the quantity of agents. It's **real governance**: 19 active hooks intercept operations at runtime, a Constitution with 11 articles governs the framework, and 7 of those articles are NON-NEGOTIABLE -- violations are blocked before execution, not detected afterwards. **Speed with rigor, without choosing between the two.**
|
|
37
37
|
|
|
38
38
|
---
|
|
39
39
|
|
|
@@ -174,7 +174,7 @@ Deny rules in `.claude/settings.json` enforce this deterministically. **Framewor
|
|
|
174
174
|
|
|
175
175
|
### Constitution
|
|
176
176
|
|
|
177
|
-
SINAPSE is governed by a formal Constitution with
|
|
177
|
+
SINAPSE is governed by a formal Constitution with 11 articles and 19 enforcement hooks:
|
|
178
178
|
|
|
179
179
|
| Article | Principle | Severity |
|
|
180
180
|
|---------|-----------|----------|
|
|
@@ -188,8 +188,9 @@ SINAPSE is governed by a formal Constitution with 10 articles and 19 enforcement
|
|
|
188
188
|
| VIII | Mandatory Delegation | NON-NEGOTIABLE |
|
|
189
189
|
| IX | Safe Collaboration | NON-NEGOTIABLE |
|
|
190
190
|
| X | Security & Data Protection | NON-NEGOTIABLE |
|
|
191
|
+
| XI | Conservative Default | MUST |
|
|
191
192
|
|
|
192
|
-
|
|
193
|
+
7 articles are NON-NEGOTIABLE -- violations are automatically blocked before execution.
|
|
193
194
|
|
|
194
195
|
---
|
|
195
196
|
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://nodejs.org/)
|
|
4
4
|
[](https://github.com/caioimori/sinapse-ai/actions/workflows/ci.yml)
|
|
5
5
|
[](https://github.com/caioimori/sinapse-ai/actions/workflows/ci.yml)
|
|
6
|
-
[](.sinapse-ai/constitution.md)
|
|
7
7
|
|
|
8
8
|
```
|
|
9
9
|
____ ___ _ _ _ ____ ____ _____
|
|
@@ -35,7 +35,7 @@ IA generativa tem um problema conhecido: quanto mais voce pede, pior fica. Um un
|
|
|
35
35
|
|
|
36
36
|
SINAPSE resolve isso do jeito que times humanos resolvem: **especializacao coordenada**. Em vez de um generalista cansado, voce tem 172 agentes em 17 squads, cada um com papel definido, knowledge base propria e tasks executaveis. Um orquestrador roteia seu pedido para quem realmente sabe resolver — automaticamente, sem voce precisar decorar agent names ou comandos.
|
|
37
37
|
|
|
38
|
-
O diferencial nao e apenas quantidade de agentes. E **governanca real**: 13 hooks ativos interceptam operacoes em tempo de execucao, uma Constitution com
|
|
38
|
+
O diferencial nao e apenas quantidade de agentes. E **governanca real**: 13 hooks ativos interceptam operacoes em tempo de execucao, uma Constitution com 11 artigos rege o framework, e 7 desses artigos sao NON-NEGOTIABLE — violacoes sao bloqueadas antes de executar, nao detectadas depois. **Velocidade com rigor, sem escolher entre os dois.**
|
|
39
39
|
|
|
40
40
|
---
|
|
41
41
|
|
|
@@ -137,8 +137,6 @@ Cada release passa por uma matriz de instalacao de 27 combinacoes (3 OSes x 3 pa
|
|
|
137
137
|
|
|
138
138
|
**Sobre Yarn v1 no Windows:** Yarn v1 (Classic) esta em modo manutencao desde 2020, com Yarn v2+ (Berry) como sucessor oficial. A combinacao Windows + Yarn v1 apresenta falhas de resolucao de wrapper que nao compensam investigar por ser ecossistema em declinio. Usuarios Windows em Yarn v1 devem migrar para Yarn v2+ (recomendado) ou usar npm/pnpm. macOS e Linux em Yarn v1 continuam suportados.
|
|
139
139
|
|
|
140
|
-
Registro completo da decisao e resultados: [docs/audits/install-matrix-2026-04-16.md](docs/audits/install-matrix-2026-04-16.md).
|
|
141
|
-
|
|
142
140
|
### "Como atualizar sem perder minhas customizacoes?"
|
|
143
141
|
|
|
144
142
|
```bash
|
|
@@ -215,7 +213,7 @@ Deny rules em `.claude/settings.json` reforcam isso deterministicamente. **Updat
|
|
|
215
213
|
|
|
216
214
|
### Constitution
|
|
217
215
|
|
|
218
|
-
O SINAPSE e governado por uma Constitution formal com
|
|
216
|
+
O SINAPSE e governado por uma Constitution formal com 11 artigos e 13 hooks de enforcement:
|
|
219
217
|
|
|
220
218
|
| Artigo | Principio | Severidade |
|
|
221
219
|
|--------|-----------|------------|
|
|
@@ -229,8 +227,9 @@ O SINAPSE e governado por uma Constitution formal com 10 artigos e 13 hooks de e
|
|
|
229
227
|
| VIII | Mandatory Delegation | NON-NEGOTIABLE |
|
|
230
228
|
| IX | Safe Collaboration | NON-NEGOTIABLE |
|
|
231
229
|
| X | Security & Data Protection | NON-NEGOTIABLE |
|
|
230
|
+
| XI | Conservative Default | MUST |
|
|
232
231
|
|
|
233
|
-
|
|
232
|
+
7 artigos sao NON-NEGOTIABLE — violacoes sao bloqueadas automaticamente antes de executar.
|
|
234
233
|
|
|
235
234
|
---
|
|
236
235
|
|
package/bin/commands/install.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// bin/commands/install.js — `sinapse-ai install` (global) command + helpers.
|
|
2
2
|
// Story GA-1.2 — extracted from bin/cli.js.
|
|
3
3
|
|
|
4
|
-
const { execSync } = require('child_process');
|
|
4
|
+
const { execSync, execFileSync } = require('child_process');
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const path = require('path');
|
|
7
7
|
const { getLogger } = require('../../.sinapse-ai/core/logger');
|
|
@@ -893,11 +893,14 @@ function ensurePathWindows() {
|
|
|
893
893
|
return;
|
|
894
894
|
}
|
|
895
895
|
|
|
896
|
-
|
|
896
|
+
// execFileSync (no shell) — never interpolate the current PATH into a
|
|
897
|
+
// shell command string. A PATH value containing `"`/`&`/`^` would break
|
|
898
|
+
// the command or allow injection; passing args as an array avoids it.
|
|
899
|
+
execFileSync('setx', ['PATH', newPath], { encoding: 'utf8', stdio: 'pipe' });
|
|
897
900
|
logger.always(` ${GREEN}OK${NC} Added ~/bin to Windows User PATH`);
|
|
898
901
|
} catch {
|
|
899
902
|
try {
|
|
900
|
-
|
|
903
|
+
execFileSync('setx', ['PATH', '%USERPROFILE%\\bin'], { encoding: 'utf8', stdio: 'pipe' });
|
|
901
904
|
logger.always(` ${GREEN}OK${NC} Created Windows User PATH with ~/bin`);
|
|
902
905
|
} catch {
|
|
903
906
|
logger.always(` ${YELLOW}WARN${NC} Could not modify PATH. Add manually: ${BIN_DIR}`);
|
package/bin/sinapse.js
CHANGED
|
@@ -73,6 +73,7 @@ USAGE:
|
|
|
73
73
|
sinapse install # Install SINAPSE in current project
|
|
74
74
|
sinapse init <name> # Create new project
|
|
75
75
|
sinapse update # Update to latest version
|
|
76
|
+
sinapse uninstall # Remove SINAPSE from the project (--keep-data)
|
|
76
77
|
sinapse brand # Re-apply SINAPSE branding (after Claude update)
|
|
77
78
|
sinapse validate # Validate installation integrity
|
|
78
79
|
sinapse info # Show system info
|
|
@@ -119,6 +120,25 @@ SERVICE DISCOVERY:
|
|
|
119
120
|
sinapse workers search "transform" --tags=etl,data
|
|
120
121
|
sinapse workers search "api" --format=json
|
|
121
122
|
|
|
123
|
+
GENERATE & CREATE:
|
|
124
|
+
sinapse generate <prd|adr|story|epic|task> # Generate a document from a template
|
|
125
|
+
sinapse create <agent|task|workflow> # Scaffold a new framework component
|
|
126
|
+
|
|
127
|
+
ORCHESTRATION:
|
|
128
|
+
sinapse orchestrate <story-id> # Run the autonomous dev pipeline for a story
|
|
129
|
+
sinapse orchestrate --status # Show / --stop / --resume a running pipeline
|
|
130
|
+
sinapse mode [explore|ask|auto] # Show or set the agent permission mode (--cycle)
|
|
131
|
+
|
|
132
|
+
INTELLIGENCE & HEALTH:
|
|
133
|
+
sinapse health # Framework health analytics (--deep for full scan)
|
|
134
|
+
sinapse graph --deps # Dependency & stats dashboard (--format, --watch)
|
|
135
|
+
sinapse performance # Squad & agent performance ranking (--top N)
|
|
136
|
+
sinapse routing-intel analyze # Routing intelligence analysis
|
|
137
|
+
|
|
138
|
+
INTEGRATIONS & LICENSE:
|
|
139
|
+
sinapse mcp setup # Configure global MCP servers (setup/link/status/add)
|
|
140
|
+
sinapse pro status # SINAPSE Pro license (activate/status/deactivate/features)
|
|
141
|
+
|
|
122
142
|
EXAMPLES:
|
|
123
143
|
# Install in current directory
|
|
124
144
|
npx sinapse-ai@latest
|
package/package.json
CHANGED
|
@@ -141,8 +141,29 @@ async function generateVersionJson(options) {
|
|
|
141
141
|
* @param {boolean} replaceRoot - Whether to replace {root} placeholders
|
|
142
142
|
* @returns {Promise<boolean>} Success status
|
|
143
143
|
*/
|
|
144
|
-
|
|
144
|
+
/**
|
|
145
|
+
* Predicate: is this destination a user-owned L3 file that must survive a
|
|
146
|
+
* re-install? Matches the boundary contract (core-config.yaml + agent
|
|
147
|
+
* MEMORY.md are L3 "mutable / user-owned") and the README promise that
|
|
148
|
+
* re-running install is an idempotent upsert that preserves customizations.
|
|
149
|
+
* @param {string} destPath - Absolute destination path
|
|
150
|
+
* @returns {boolean}
|
|
151
|
+
*/
|
|
152
|
+
function isUserOwnedL3(destPath) {
|
|
153
|
+
const norm = String(destPath).replace(/\\/g, '/');
|
|
154
|
+
if (norm.endsWith('/.sinapse-ai/core-config.yaml')) return true;
|
|
155
|
+
if (/\/\.sinapse-ai\/development\/agents\/[^/]+\/MEMORY\.md$/.test(norm)) return true;
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async function copyFileWithRootReplacement(sourcePath, destPath, replaceRoot = true, preserveExisting = null) {
|
|
145
160
|
try {
|
|
161
|
+
// Preserve user-owned L3 files (core-config.yaml, agent MEMORY.md) when they
|
|
162
|
+
// already exist — a re-install must not silently wipe the user's config.
|
|
163
|
+
if (preserveExisting && preserveExisting(destPath) && await fs.pathExists(destPath)) {
|
|
164
|
+
return 'preserved';
|
|
165
|
+
}
|
|
166
|
+
|
|
146
167
|
await fs.ensureDir(path.dirname(destPath));
|
|
147
168
|
|
|
148
169
|
// Check if file needs {root} replacement (.md, .yaml, .yml)
|
|
@@ -171,7 +192,7 @@ async function copyFileWithRootReplacement(sourcePath, destPath, replaceRoot = t
|
|
|
171
192
|
* @param {Function} onProgress - Progress callback
|
|
172
193
|
* @returns {Promise<string[]>} List of copied files (relative paths)
|
|
173
194
|
*/
|
|
174
|
-
async function copyDirectoryWithRootReplacement(sourceDir, destDir, onProgress = null) {
|
|
195
|
+
async function copyDirectoryWithRootReplacement(sourceDir, destDir, onProgress = null, preserveExisting = null) {
|
|
175
196
|
const copiedFiles = [];
|
|
176
197
|
|
|
177
198
|
if (!await fs.pathExists(sourceDir)) {
|
|
@@ -193,11 +214,13 @@ async function copyDirectoryWithRootReplacement(sourceDir, destDir, onProgress =
|
|
|
193
214
|
}
|
|
194
215
|
|
|
195
216
|
if (item.isDirectory()) {
|
|
196
|
-
const subFiles = await copyDirectoryWithRootReplacement(sourcePath, destPath, onProgress);
|
|
217
|
+
const subFiles = await copyDirectoryWithRootReplacement(sourcePath, destPath, onProgress, preserveExisting);
|
|
197
218
|
copiedFiles.push(...subFiles);
|
|
198
219
|
} else {
|
|
199
|
-
const success = await copyFileWithRootReplacement(sourcePath, destPath);
|
|
200
|
-
|
|
220
|
+
const success = await copyFileWithRootReplacement(sourcePath, destPath, true, preserveExisting);
|
|
221
|
+
// Only count files actually written; 'preserved' (existing user-owned L3)
|
|
222
|
+
// and false (error) must not inflate the installed-files manifest.
|
|
223
|
+
if (success === true) {
|
|
201
224
|
copiedFiles.push(path.relative(destDir, destPath));
|
|
202
225
|
if (onProgress) {
|
|
203
226
|
onProgress({ file: item.name, copied: true });
|
|
@@ -290,6 +313,7 @@ async function installSinapseCore(options = {}) {
|
|
|
290
313
|
folderSource,
|
|
291
314
|
folderDest,
|
|
292
315
|
onProgress,
|
|
316
|
+
isUserOwnedL3,
|
|
293
317
|
);
|
|
294
318
|
|
|
295
319
|
if (copiedFiles.length > 0) {
|
|
@@ -306,8 +330,8 @@ async function installSinapseCore(options = {}) {
|
|
|
306
330
|
|
|
307
331
|
if (await fs.pathExists(fileSource)) {
|
|
308
332
|
spinner.text = `Copying ${file}...`;
|
|
309
|
-
const success = await copyFileWithRootReplacement(fileSource, fileDest);
|
|
310
|
-
if (success) {
|
|
333
|
+
const success = await copyFileWithRootReplacement(fileSource, fileDest, true, isUserOwnedL3);
|
|
334
|
+
if (success === true) {
|
|
311
335
|
result.installedFiles.push(file);
|
|
312
336
|
}
|
|
313
337
|
}
|
|
@@ -495,6 +519,7 @@ module.exports = {
|
|
|
495
519
|
getSinapseCoreSourcePath,
|
|
496
520
|
copyFileWithRootReplacement,
|
|
497
521
|
copyDirectoryWithRootReplacement,
|
|
522
|
+
isUserOwnedL3,
|
|
498
523
|
generateVersionJson,
|
|
499
524
|
generateFileHashes,
|
|
500
525
|
deliverCodexRootFiles,
|
|
@@ -31,6 +31,29 @@ const path = require('path');
|
|
|
31
31
|
const { collectCounts } = require('./sync-counts.js');
|
|
32
32
|
|
|
33
33
|
const ROOT = path.resolve(__dirname, '..');
|
|
34
|
+
const CONSTITUTION = path.join(ROOT, '.sinapse-ai', 'constitution.md');
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Count Constitution articles + NON-NEGOTIABLE ones from the canonical source.
|
|
38
|
+
* Articles are `### <Roman>. <Title>` headings; NON-NEGOTIABLE ones carry the
|
|
39
|
+
* `(NON-NEGOTIABLE)` marker in the heading. This is what was drifting in the
|
|
40
|
+
* README ("10 articles" while the constitution had 11) with no guard — now there
|
|
41
|
+
* is one.
|
|
42
|
+
* @returns {{articles:number, nonNegotiable:number}}
|
|
43
|
+
*/
|
|
44
|
+
function collectConstitutionCounts() {
|
|
45
|
+
let articles = 0;
|
|
46
|
+
let nonNegotiable = 0;
|
|
47
|
+
try {
|
|
48
|
+
const md = fs.readFileSync(CONSTITUTION, 'utf8');
|
|
49
|
+
const headings = md.match(/^###\s+[IVXLC]+\.\s.*$/gm) || [];
|
|
50
|
+
articles = headings.length;
|
|
51
|
+
nonNegotiable = headings.filter((h) => /\(NON-NEGOTIABLE\)/.test(h)).length;
|
|
52
|
+
} catch {
|
|
53
|
+
// Missing/unreadable constitution → leave zeros; the guards below are no-ops.
|
|
54
|
+
}
|
|
55
|
+
return { articles, nonNegotiable };
|
|
56
|
+
}
|
|
34
57
|
|
|
35
58
|
const TARGETS = [
|
|
36
59
|
{ file: 'README.md', label: 'README PT' },
|
|
@@ -44,12 +67,14 @@ const TARGETS = [
|
|
|
44
67
|
* Build the set of expected numeric tokens that MUST appear consistently.
|
|
45
68
|
* Returns an object describing canonical counts and a regex helper.
|
|
46
69
|
*/
|
|
47
|
-
function buildExpectations(counts) {
|
|
70
|
+
function buildExpectations(counts, constitution = { articles: 0, nonNegotiable: 0 }) {
|
|
48
71
|
return {
|
|
49
72
|
squads: counts.squads,
|
|
50
73
|
agents: counts.totalAgents,
|
|
51
74
|
orqx: counts.totalOrqx,
|
|
52
75
|
tasks: counts.tasks,
|
|
76
|
+
articles: constitution.articles,
|
|
77
|
+
nonNegotiable: constitution.nonNegotiable,
|
|
53
78
|
};
|
|
54
79
|
}
|
|
55
80
|
|
|
@@ -89,6 +114,39 @@ function findDrift(content, expected) {
|
|
|
89
114
|
}
|
|
90
115
|
}
|
|
91
116
|
|
|
117
|
+
// Constitution total articles: "<N> artigos"/"<N> articles" NOT immediately
|
|
118
|
+
// part of a NON-NEGOTIABLE clause, plus the shields.io badge
|
|
119
|
+
// "Constitution-<N>%20articles". Guards the exact drift that slipped through
|
|
120
|
+
// before (README said "10 articles" while the constitution had 11).
|
|
121
|
+
if (expected.articles) {
|
|
122
|
+
const artRe = /\b(\d{1,2})\s+(?:artigos|articles)\b(?![^.\n]{0,40}NON-NEGOTIABLE)/gi;
|
|
123
|
+
for (const m of content.matchAll(artRe)) {
|
|
124
|
+
const n = parseInt(m[1], 10);
|
|
125
|
+
if (n !== expected.articles && n >= 5) {
|
|
126
|
+
findings.push({ kind: 'articles', found: n, expected: expected.articles, snippet: m[0].trim() });
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
const badgeRe = /Constitution-(\d{1,2})%20articles/gi;
|
|
130
|
+
for (const m of content.matchAll(badgeRe)) {
|
|
131
|
+
const n = parseInt(m[1], 10);
|
|
132
|
+
if (n !== expected.articles) {
|
|
133
|
+
findings.push({ kind: 'articles (badge)', found: n, expected: expected.articles, snippet: m[0] });
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Constitution NON-NEGOTIABLE article count: "<N> (desses|of those) artigos
|
|
139
|
+
// sao/are NON-NEGOTIABLE".
|
|
140
|
+
if (expected.nonNegotiable) {
|
|
141
|
+
const nnRe = /\b(\d{1,2})\s+(?:(?:desses|de|of\s+those)\s+)?(?:artigos|articles)\s+(?:sao|são|are)\s+NON-NEGOTIABLE/gi;
|
|
142
|
+
for (const m of content.matchAll(nnRe)) {
|
|
143
|
+
const n = parseInt(m[1], 10);
|
|
144
|
+
if (n !== expected.nonNegotiable) {
|
|
145
|
+
findings.push({ kind: 'NON-NEGOTIABLE articles', found: n, expected: expected.nonNegotiable, snippet: m[0].trim() });
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
92
150
|
return findings;
|
|
93
151
|
}
|
|
94
152
|
|
|
@@ -112,14 +170,17 @@ function main() {
|
|
|
112
170
|
const fixMode = args.includes('--fix');
|
|
113
171
|
|
|
114
172
|
const counts = collectCounts();
|
|
115
|
-
const
|
|
173
|
+
const constitution = collectConstitutionCounts();
|
|
174
|
+
const expected = buildExpectations(counts, constitution);
|
|
116
175
|
|
|
117
176
|
console.log('Article VII (Metrics Accuracy) — validacao de drift');
|
|
118
177
|
console.log('Numeros canonicos (de .sinapse-ai/constitution.md):');
|
|
119
|
-
console.log(` squads
|
|
120
|
-
console.log(` agentes
|
|
121
|
-
console.log(` orqx
|
|
122
|
-
console.log(` tasks
|
|
178
|
+
console.log(` squads ${expected.squads}`);
|
|
179
|
+
console.log(` agentes ${expected.agents}`);
|
|
180
|
+
console.log(` orqx ${expected.orqx}`);
|
|
181
|
+
console.log(` tasks ${expected.tasks}`);
|
|
182
|
+
console.log(` artigos ${expected.articles}`);
|
|
183
|
+
console.log(` NON-NEGOTIABLE ${expected.nonNegotiable}`);
|
|
123
184
|
console.log('');
|
|
124
185
|
|
|
125
186
|
const allDrifts = [];
|
|
@@ -173,4 +234,4 @@ if (require.main === module) {
|
|
|
173
234
|
main();
|
|
174
235
|
}
|
|
175
236
|
|
|
176
|
-
module.exports = { findDrift, buildExpectations, TARGETS };
|
|
237
|
+
module.exports = { findDrift, buildExpectations, collectConstitutionCounts, TARGETS };
|