sinapse-ai 1.6.1 → 1.8.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/.claude/CLAUDE.md +5 -11
- package/.claude/hooks/README.md +14 -1
- package/.claude/hooks/code-intel-pretool.cjs +115 -0
- package/.claude/hooks/enforce-delegation.cjs +31 -3
- package/.claude/hooks/enforce-framework-boundary.cjs +324 -0
- package/.claude/hooks/enforce-permission-mode.cjs +249 -0
- package/.claude/hooks/secret-scanning.cjs +34 -43
- package/.claude/hooks/synapse-engine.cjs +23 -23
- package/.claude/hooks/telemetry-post-tool.cjs +128 -0
- package/.claude/hooks/telemetry-stop.cjs +132 -0
- package/.claude/hooks/verify-packages.cjs +9 -2
- package/.claude/rules/documentation-first.md +1 -1
- package/.claude/rules/hook-governance.md +2 -0
- package/.sinapse-ai/cli/commands/health/index.js +24 -0
- package/.sinapse-ai/core/README.md +11 -0
- package/.sinapse-ai/core/config/config-loader.js +19 -0
- package/.sinapse-ai/core/config/merge-utils.js +8 -0
- package/.sinapse-ai/core/errors/constants.js +147 -0
- package/.sinapse-ai/core/errors/error-registry.js +176 -0
- package/.sinapse-ai/core/errors/index.js +50 -0
- package/.sinapse-ai/core/errors/serializer.js +147 -0
- package/.sinapse-ai/core/errors/sinapse-error.js +144 -0
- package/.sinapse-ai/core/errors/utils.js +187 -0
- package/.sinapse-ai/core/execution/build-orchestrator.js +47 -49
- package/.sinapse-ai/core/execution/build-state-manager.js +183 -31
- package/.sinapse-ai/core/execution/parallel-executor.js +7 -1
- package/.sinapse-ai/core/execution/semantic-merge-engine.js +26 -14
- package/.sinapse-ai/core/execution/subagent-dispatcher.js +201 -60
- package/.sinapse-ai/core/execution/wave-executor.js +4 -1
- package/.sinapse-ai/core/grounding/README.md +71 -11
- package/.sinapse-ai/core/health-check/checks/project/framework-config.js +38 -2
- package/.sinapse-ai/core/health-check/checks/project/package-json.js +47 -3
- package/.sinapse-ai/core/health-check/checks/services/gemini-cli.js +117 -0
- package/.sinapse-ai/core/health-check/checks/services/index.js +2 -0
- package/.sinapse-ai/core/health-check/healers/index.js +40 -3
- package/.sinapse-ai/core/ideation/ideation-engine.js +212 -107
- package/.sinapse-ai/core/ids/gate-evaluator.js +318 -0
- package/.sinapse-ai/core/ids/gates/g5-semantic-handshake.js +190 -0
- package/.sinapse-ai/core/ids/gates/g6-ci-integrity.js +162 -0
- package/.sinapse-ai/core/ids/index.js +30 -0
- package/.sinapse-ai/core/memory/__tests__/active-modules.verify.js +11 -0
- package/.sinapse-ai/core/memory/gotchas-memory.js +37 -2
- package/.sinapse-ai/core/orchestration/agent-invoker.js +29 -6
- package/.sinapse-ai/core/orchestration/brownfield-handler.js +36 -3
- package/.sinapse-ai/core/orchestration/condition-evaluator.js +57 -0
- package/.sinapse-ai/core/orchestration/executors/epic-3-executor.js +76 -5
- package/.sinapse-ai/core/orchestration/executors/epic-4-executor.js +63 -17
- package/.sinapse-ai/core/orchestration/executors/epic-6-executor.js +153 -41
- package/.sinapse-ai/core/orchestration/executors/epic-executor.js +40 -0
- package/.sinapse-ai/core/orchestration/greenfield-handler.js +87 -3
- package/.sinapse-ai/core/orchestration/master-orchestrator.js +150 -10
- package/.sinapse-ai/core/orchestration/parallel-executor.js +6 -1
- package/.sinapse-ai/core/orchestration/recovery-handler.js +81 -8
- package/.sinapse-ai/core/orchestration/workflow-executor.js +41 -0
- package/.sinapse-ai/core/registry/registry-loader.js +71 -5
- package/.sinapse-ai/core/registry/squad-agent-resolver.js +253 -0
- package/.sinapse-ai/core/synapse/context/context-tracker.js +104 -9
- package/.sinapse-ai/core/synapse/context/index.js +19 -0
- package/.sinapse-ai/core/synapse/context/semantic-handshake-engine.js +555 -0
- package/.sinapse-ai/core/synapse/diagnostics/collectors/pipeline-collector.js +4 -2
- package/.sinapse-ai/core/synapse/engine.js +43 -3
- package/.sinapse-ai/core/telemetry/ids-sink.js +188 -0
- package/.sinapse-ai/core/utils/output-formatter.js +8 -290
- package/.sinapse-ai/core/utils/spawn-safe.js +186 -0
- package/.sinapse-ai/core-config.yaml +68 -1
- package/.sinapse-ai/data/entity-registry.yaml +15082 -13618
- package/.sinapse-ai/data/registry-update-log.jsonl +143 -0
- package/.sinapse-ai/development/agents/developer.md +2 -0
- package/.sinapse-ai/development/agents/devops.md +9 -0
- package/.sinapse-ai/development/external-executors/README.md +18 -0
- package/.sinapse-ai/development/external-executors/codex.md +56 -0
- package/.sinapse-ai/development/scripts/populate-entity-registry.js +65 -9
- package/.sinapse-ai/development/scripts/squad/squad-downloader.js +169 -14
- package/.sinapse-ai/development/tasks/delegate-to-external-executor.md +152 -0
- package/.sinapse-ai/development/tasks/github-devops-pre-push-quality-gate.md +46 -29
- package/.sinapse-ai/development/tasks/update-sinapse.md +3 -3
- package/.sinapse-ai/hooks/sinapse-brand-grounding.cjs +4 -7
- package/.sinapse-ai/hooks/sinapse-ds-grounding.cjs +5 -8
- package/.sinapse-ai/hooks/sinapse-vault-grounding.cjs +6 -9
- package/.sinapse-ai/infrastructure/integrations/ai-providers/ai-provider-factory.js +4 -1
- package/.sinapse-ai/infrastructure/integrations/ai-providers/claude-provider.js +57 -55
- package/.sinapse-ai/infrastructure/integrations/pm-adapters/github-adapter.js +9 -7
- package/.sinapse-ai/infrastructure/scripts/ide-sync/gemini-commands.js +298 -0
- package/.sinapse-ai/infrastructure/scripts/ide-sync/index.js +127 -6
- package/.sinapse-ai/infrastructure/scripts/ide-sync/persona-renderer.js +97 -0
- package/.sinapse-ai/infrastructure/scripts/ide-sync/transformers/antigravity.js +121 -0
- package/.sinapse-ai/infrastructure/scripts/ide-sync/transformers/cursor.js +119 -0
- package/.sinapse-ai/infrastructure/scripts/ide-sync/transformers/github-copilot.js +191 -0
- package/.sinapse-ai/infrastructure/scripts/ide-sync/transformers/kimi.js +448 -0
- package/.sinapse-ai/install-manifest.yaml +218 -114
- package/.sinapse-ai/product/templates/engine/renderer.js +20 -1
- package/.sinapse-ai/scripts/pm.sh +18 -6
- package/bin/cli.js +17 -0
- package/bin/commands/agents.js +96 -0
- package/bin/commands/doctor.js +15 -0
- package/bin/commands/ideate.js +129 -0
- package/bin/commands/uninstall.js +40 -0
- package/bin/postinstall.js +50 -4
- package/bin/sinapse.js +146 -2
- package/bin/utils/secret-scanner-core.js +253 -0
- package/bin/utils/staged-secret-scan.js +106 -40
- package/docs/framework/collaboration-autonomy-plan.md +18 -18
- package/docs/guides/parallel-workflow.md +6 -6
- package/package.json +22 -5
- package/packages/installer/src/installer/git-hooks-installer.js +384 -0
- package/packages/installer/src/installer/sinapse-ai-installer.js +16 -0
- package/packages/installer/src/wizard/ide-config-generator.js +23 -0
- package/packages/installer/src/wizard/validators.js +38 -1
- package/packages/installer/tests/unit/artifact-copy-pipeline/artifact-copy-pipeline.test.js +5 -1
- package/packages/installer/tests/unit/doctor/doctor-checks.test.js +44 -22
- package/packages/installer/tests/unit/git-hooks-installer.test.js +262 -0
- package/scripts/eval-runner.js +422 -0
- package/scripts/generate-install-manifest.js +13 -9
- package/scripts/generate-synapse-runtime.js +51 -0
- package/scripts/regenerate-orqx-stubs.ps1 +6 -5
- package/scripts/validate-all.js +1 -0
- package/scripts/validate-evals.js +466 -0
- package/scripts/validate-schemas.js +539 -0
- package/scripts/validate-squad-orqx.js +9 -2
- package/squads/claude-code-mastery/knowledge-base/memory-systems-reference.md +1 -1
- package/squads/squad-brand/templates/client-delivery-template.md +1 -1
- package/squads/squad-content/knowledge-base/social-compression-framework.md +1 -1
- package/squads/squad-council/knowledge-base/brand-strategy-models.md +1 -1
- package/.sinapse-ai/development/scripts/elicitation-engine.js +0 -385
- package/.sinapse-ai/development/scripts/elicitation-session-manager.js +0 -300
- package/.sinapse-ai/development/tasks/test-validation-task.md +0 -172
- package/docs/chrome-brain-upgrade-plan.md +0 -624
- package/docs/constitution-compliance.md +0 -87
- package/docs/mega-upgrade-orchestration-plan.md +0 -71
- package/docs/research-synthesis-for-upgrade.md +0 -511
- package/docs/security-audit-report.md +0 -306
|
@@ -33,6 +33,39 @@ const GITHUB_API_BASE =
|
|
|
33
33
|
*/
|
|
34
34
|
const DEFAULT_SQUADS_PATH = './squads';
|
|
35
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Allowlist of HTTPS hosts permitted when following HTTP redirects.
|
|
38
|
+
* Prevents SSRF / redirect-based exfiltration to arbitrary hosts.
|
|
39
|
+
* @constant {Set<string>}
|
|
40
|
+
*/
|
|
41
|
+
const ALLOWED_REDIRECT_HOSTS = new Set([
|
|
42
|
+
'raw.githubusercontent.com',
|
|
43
|
+
'api.github.com',
|
|
44
|
+
'github.com',
|
|
45
|
+
'objects.githubusercontent.com',
|
|
46
|
+
'codeload.github.com',
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Maximum number of redirects to follow before aborting a fetch.
|
|
51
|
+
* @constant {number}
|
|
52
|
+
*/
|
|
53
|
+
const MAX_REDIRECTS = 5;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Per-request timeout (ms). Aborts a hung/slow remote so a compromised or
|
|
57
|
+
* unresponsive host can't stall the installer indefinitely (P3-001).
|
|
58
|
+
* @constant {number}
|
|
59
|
+
*/
|
|
60
|
+
const REQUEST_TIMEOUT_MS = 30000;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Maximum response size (bytes). Caps a single download so a malicious/
|
|
64
|
+
* misconfigured host can't exhaust memory (10 MB is ample for a squad).
|
|
65
|
+
* @constant {number}
|
|
66
|
+
*/
|
|
67
|
+
const MAX_RESPONSE_BYTES = 10 * 1024 * 1024;
|
|
68
|
+
|
|
36
69
|
/**
|
|
37
70
|
* Error codes for SquadDownloaderError
|
|
38
71
|
* @enum {string}
|
|
@@ -370,6 +403,51 @@ class SquadDownloader {
|
|
|
370
403
|
this._log(`Downloaded ${contents.length} items to ${targetPath}`);
|
|
371
404
|
}
|
|
372
405
|
|
|
406
|
+
/**
|
|
407
|
+
* Resolve and validate that an item lands strictly inside the target path.
|
|
408
|
+
* Defends against zip-slip / path traversal when `item.name` originates from
|
|
409
|
+
* an untrusted remote source (GitHub API / registry).
|
|
410
|
+
*
|
|
411
|
+
* @private
|
|
412
|
+
* @param {string} targetPath - Trusted base directory
|
|
413
|
+
* @param {string} itemName - Untrusted entry name from remote response
|
|
414
|
+
* @returns {string} Safe, resolved item path contained within targetPath
|
|
415
|
+
* @throws {SquadDownloaderError} VALIDATION_ERROR if traversal is detected
|
|
416
|
+
*/
|
|
417
|
+
_safeResolve(targetPath, itemName) {
|
|
418
|
+
// Reject names that are missing, contain path separators, or use '..'.
|
|
419
|
+
if (
|
|
420
|
+
typeof itemName !== 'string' ||
|
|
421
|
+
itemName.length === 0 ||
|
|
422
|
+
itemName === '.' ||
|
|
423
|
+
itemName === '..' ||
|
|
424
|
+
itemName.includes('/') ||
|
|
425
|
+
itemName.includes('\\') ||
|
|
426
|
+
itemName.includes('\0')
|
|
427
|
+
) {
|
|
428
|
+
throw new SquadDownloaderError(
|
|
429
|
+
DownloaderErrorCodes.VALIDATION_ERROR,
|
|
430
|
+
`Unsafe item name from remote response: "${itemName}"`,
|
|
431
|
+
'Squad entries must be plain file/directory names without path separators or ".."',
|
|
432
|
+
);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const itemPath = path.join(targetPath, itemName);
|
|
436
|
+
const resolved = path.resolve(itemPath);
|
|
437
|
+
const base = path.resolve(targetPath);
|
|
438
|
+
|
|
439
|
+
// Resolved path must be strictly inside the base directory.
|
|
440
|
+
if (resolved !== base && !resolved.startsWith(base + path.sep)) {
|
|
441
|
+
throw new SquadDownloaderError(
|
|
442
|
+
DownloaderErrorCodes.VALIDATION_ERROR,
|
|
443
|
+
`Path traversal detected: "${itemName}" escapes target directory`,
|
|
444
|
+
'Squad entries must stay within the download directory',
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
return itemPath;
|
|
449
|
+
}
|
|
450
|
+
|
|
373
451
|
/**
|
|
374
452
|
* Download contents recursively
|
|
375
453
|
* @private
|
|
@@ -378,7 +456,8 @@ class SquadDownloader {
|
|
|
378
456
|
*/
|
|
379
457
|
async _downloadContents(contents, targetPath) {
|
|
380
458
|
for (const item of contents) {
|
|
381
|
-
|
|
459
|
+
// Validate containment BEFORE any filesystem write (zip-slip guard).
|
|
460
|
+
const itemPath = this._safeResolve(targetPath, item.name);
|
|
382
461
|
|
|
383
462
|
if (item.type === 'file') {
|
|
384
463
|
// Download file - Buffer is written directly (supports binary files)
|
|
@@ -400,9 +479,10 @@ class SquadDownloader {
|
|
|
400
479
|
* @private
|
|
401
480
|
* @param {string} url - URL to fetch
|
|
402
481
|
* @param {boolean} [useApi=false] - Whether to use GitHub API headers
|
|
482
|
+
* @param {number} [redirectCount=0] - Current redirect depth (internal)
|
|
403
483
|
* @returns {Promise<Buffer>} Response body as Buffer (supports binary files)
|
|
404
484
|
*/
|
|
405
|
-
_fetch(url, useApi = false) {
|
|
485
|
+
_fetch(url, useApi = false, redirectCount = 0) {
|
|
406
486
|
return new Promise((resolve, reject) => {
|
|
407
487
|
const options = {
|
|
408
488
|
headers: {
|
|
@@ -417,8 +497,7 @@ class SquadDownloader {
|
|
|
417
497
|
}
|
|
418
498
|
}
|
|
419
499
|
|
|
420
|
-
https
|
|
421
|
-
.get(url, options, (res) => {
|
|
500
|
+
const req = https.get(url, options, (res) => {
|
|
422
501
|
// Check for rate limiting
|
|
423
502
|
if (res.statusCode === 403) {
|
|
424
503
|
const rateLimitRemaining = res.headers['x-ratelimit-remaining'];
|
|
@@ -438,7 +517,53 @@ class SquadDownloader {
|
|
|
438
517
|
|
|
439
518
|
// Check for redirect
|
|
440
519
|
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
441
|
-
|
|
520
|
+
// Drain the redirect response to free the socket.
|
|
521
|
+
res.resume();
|
|
522
|
+
|
|
523
|
+
// Cap redirect depth to prevent redirect loops.
|
|
524
|
+
if (redirectCount >= MAX_REDIRECTS) {
|
|
525
|
+
reject(
|
|
526
|
+
new SquadDownloaderError(
|
|
527
|
+
DownloaderErrorCodes.NETWORK_ERROR,
|
|
528
|
+
`Too many redirects (>${MAX_REDIRECTS}) while fetching ${url}`,
|
|
529
|
+
'The remote server may be misconfigured or attempting a redirect loop',
|
|
530
|
+
),
|
|
531
|
+
);
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// Resolve the Location against the current URL and enforce an
|
|
536
|
+
// HTTPS host allowlist (SSRF / exfiltration guard).
|
|
537
|
+
let redirectUrl;
|
|
538
|
+
try {
|
|
539
|
+
redirectUrl = new URL(res.headers.location, url);
|
|
540
|
+
} catch {
|
|
541
|
+
reject(
|
|
542
|
+
new SquadDownloaderError(
|
|
543
|
+
DownloaderErrorCodes.NETWORK_ERROR,
|
|
544
|
+
`Invalid redirect URL: ${res.headers.location}`,
|
|
545
|
+
),
|
|
546
|
+
);
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
if (
|
|
551
|
+
redirectUrl.protocol !== 'https:' ||
|
|
552
|
+
!ALLOWED_REDIRECT_HOSTS.has(redirectUrl.hostname)
|
|
553
|
+
) {
|
|
554
|
+
reject(
|
|
555
|
+
new SquadDownloaderError(
|
|
556
|
+
DownloaderErrorCodes.VALIDATION_ERROR,
|
|
557
|
+
`Refusing redirect to disallowed host: ${redirectUrl.protocol}//${redirectUrl.hostname}`,
|
|
558
|
+
'Redirects are restricted to trusted GitHub HTTPS hosts',
|
|
559
|
+
),
|
|
560
|
+
);
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
this._fetch(redirectUrl.toString(), useApi, redirectCount + 1)
|
|
565
|
+
.then(resolve)
|
|
566
|
+
.catch(reject);
|
|
442
567
|
return;
|
|
443
568
|
}
|
|
444
569
|
|
|
@@ -453,25 +578,55 @@ class SquadDownloader {
|
|
|
453
578
|
return;
|
|
454
579
|
}
|
|
455
580
|
|
|
456
|
-
// Collect chunks as Buffer objects to support binary files
|
|
581
|
+
// Collect chunks as Buffer objects to support binary files, capping
|
|
582
|
+
// total size to prevent a malicious host from exhausting memory.
|
|
457
583
|
const chunks = [];
|
|
584
|
+
let total = 0;
|
|
458
585
|
res.on('data', (chunk) => {
|
|
586
|
+
total += chunk.length;
|
|
587
|
+
if (total > MAX_RESPONSE_BYTES) {
|
|
588
|
+
const err = new SquadDownloaderError(
|
|
589
|
+
DownloaderErrorCodes.NETWORK_ERROR,
|
|
590
|
+
`Response exceeded ${MAX_RESPONSE_BYTES} bytes — aborting`,
|
|
591
|
+
'The remote file is unexpectedly large; verify the source',
|
|
592
|
+
);
|
|
593
|
+
if (typeof req.destroy === 'function') req.destroy(err);
|
|
594
|
+
else reject(err);
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
459
597
|
chunks.push(chunk);
|
|
460
598
|
});
|
|
461
599
|
res.on('end', () => {
|
|
462
600
|
// Concatenate all chunks into a single Buffer
|
|
463
601
|
resolve(Buffer.concat(chunks));
|
|
464
602
|
});
|
|
465
|
-
})
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
// Abort hung requests (P3-001) — destroy() surfaces via the 'error' handler.
|
|
606
|
+
// Guarded: test doubles for https.get may not implement setTimeout/destroy.
|
|
607
|
+
if (typeof req.setTimeout === 'function') {
|
|
608
|
+
req.setTimeout(REQUEST_TIMEOUT_MS, () => {
|
|
609
|
+
const err = new SquadDownloaderError(
|
|
610
|
+
DownloaderErrorCodes.NETWORK_ERROR,
|
|
611
|
+
`Request timed out after ${REQUEST_TIMEOUT_MS}ms fetching ${url}`,
|
|
612
|
+
'Check internet connection or try again later',
|
|
473
613
|
);
|
|
614
|
+
if (typeof req.destroy === 'function') req.destroy(err);
|
|
615
|
+
else reject(err);
|
|
474
616
|
});
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
req.on('error', (error) => {
|
|
620
|
+
reject(
|
|
621
|
+
error instanceof SquadDownloaderError
|
|
622
|
+
? error
|
|
623
|
+
: new SquadDownloaderError(
|
|
624
|
+
DownloaderErrorCodes.NETWORK_ERROR,
|
|
625
|
+
`Network error: ${error.message}`,
|
|
626
|
+
'Check internet connection',
|
|
627
|
+
),
|
|
628
|
+
);
|
|
629
|
+
});
|
|
475
630
|
});
|
|
476
631
|
}
|
|
477
632
|
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# delegate-to-external-executor.md
|
|
2
|
+
|
|
3
|
+
**Task**: Delegate Implementation to External Executor
|
|
4
|
+
|
|
5
|
+
**Purpose**: Standardize the orchestrator/executor split for SINAPSE workflows. The active SINAPSE runtime keeps authority over story interpretation, acceptance criteria validation, constitutional gates, review, and story updates while a separate CLI runtime performs only the implementation attempt.
|
|
6
|
+
|
|
7
|
+
**When to use**: Use only for `@developer` implementation work where the story scope is clear enough to hand to another runtime. Do not use for @product-lead, @quality-gate, @sprint-lead, @devops, architecture approval, or release authority.
|
|
8
|
+
|
|
9
|
+
## Task Definition
|
|
10
|
+
|
|
11
|
+
```yaml
|
|
12
|
+
task: delegateToExternalExecutor()
|
|
13
|
+
responsavel: Orchestrating agent
|
|
14
|
+
responsavel_type: Agente
|
|
15
|
+
atomic_layer: Organism
|
|
16
|
+
|
|
17
|
+
inputs:
|
|
18
|
+
- campo: prompt
|
|
19
|
+
tipo: string
|
|
20
|
+
obrigatorio: true
|
|
21
|
+
validacao: Must cite acceptance criteria, story path, file scope, and explicit non-goals
|
|
22
|
+
- campo: slug
|
|
23
|
+
tipo: string
|
|
24
|
+
obrigatorio: true
|
|
25
|
+
validacao: Stable filesystem-safe run slug
|
|
26
|
+
- campo: story_id
|
|
27
|
+
tipo: string
|
|
28
|
+
obrigatorio: false
|
|
29
|
+
- campo: story_path
|
|
30
|
+
tipo: string
|
|
31
|
+
obrigatorio: false
|
|
32
|
+
- campo: workdir
|
|
33
|
+
tipo: string
|
|
34
|
+
obrigatorio: false
|
|
35
|
+
default: Current project root
|
|
36
|
+
- campo: provider
|
|
37
|
+
tipo: string
|
|
38
|
+
obrigatorio: false
|
|
39
|
+
default: codex
|
|
40
|
+
|
|
41
|
+
outputs:
|
|
42
|
+
- campo: run_dir
|
|
43
|
+
tipo: string
|
|
44
|
+
destino: Orchestrator
|
|
45
|
+
- campo: output
|
|
46
|
+
tipo: file
|
|
47
|
+
destino: <run_dir>/output.md
|
|
48
|
+
- campo: log
|
|
49
|
+
tipo: file
|
|
50
|
+
destino: <run_dir>/<provider>.log
|
|
51
|
+
- campo: diff
|
|
52
|
+
tipo: git-diff
|
|
53
|
+
destino: Orchestrator review
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Configuration
|
|
57
|
+
|
|
58
|
+
Delegation is disabled by default.
|
|
59
|
+
|
|
60
|
+
```yaml
|
|
61
|
+
dev:
|
|
62
|
+
execution_mode: native # native | delegate
|
|
63
|
+
delegate_to: codex
|
|
64
|
+
auto_review: true
|
|
65
|
+
|
|
66
|
+
external_executors:
|
|
67
|
+
enabled: false
|
|
68
|
+
default_sandbox: workspace-write # read-only | workspace-write | full-auto | danger-full-access
|
|
69
|
+
run_dir: .sinapse/external-runs
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Pre-Conditions
|
|
73
|
+
|
|
74
|
+
```yaml
|
|
75
|
+
pre_conditions:
|
|
76
|
+
- [ ] External executor provider is installed and available on PATH.
|
|
77
|
+
- [ ] Working tree is clean, or existing intentional changes are already committed.
|
|
78
|
+
- [ ] Prompt cites the story path and acceptance criteria.
|
|
79
|
+
- [ ] Prompt lists allowed file scope and explicit non-goals.
|
|
80
|
+
- [ ] Delegated work is implementation work owned by @developer.
|
|
81
|
+
- [ ] Orchestrator has enough context to review the resulting diff.
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Execution
|
|
85
|
+
|
|
86
|
+
### 1. Build the Prompt
|
|
87
|
+
|
|
88
|
+
The orchestrator writes a prompt that contains:
|
|
89
|
+
|
|
90
|
+
- Story ID and story path
|
|
91
|
+
- Acceptance criteria copied or summarized from the story
|
|
92
|
+
- Allowed file paths or modules
|
|
93
|
+
- Testing expectations
|
|
94
|
+
- Constraints from Constitution and project rules
|
|
95
|
+
- Explicit instruction that the executor must not update story status, checkboxes, File List, PRs, or releases
|
|
96
|
+
|
|
97
|
+
### 2. Start the Delegate Run
|
|
98
|
+
|
|
99
|
+
Use the wrapper:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
sinapse-delegate codex -t <slug> -f <prompt_file> -d <workdir>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The wrapper prints:
|
|
106
|
+
|
|
107
|
+
```text
|
|
108
|
+
STATUS=started
|
|
109
|
+
RUN_DIR=.sinapse/external-runs/<timestamp>-<slug>
|
|
110
|
+
PID=<pid>
|
|
111
|
+
LOG=<run_dir>/codex.log
|
|
112
|
+
OUTPUT=<run_dir>/output.md
|
|
113
|
+
PROMPT=<run_dir>/prompt.md
|
|
114
|
+
COMMAND=<provider command>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### 3. Monitor Completion
|
|
118
|
+
|
|
119
|
+
The orchestrator may tail the log or wait for the PID. Do not mark story progress while the external executor is still running.
|
|
120
|
+
|
|
121
|
+
### 4. Review Output and Diff
|
|
122
|
+
|
|
123
|
+
The orchestrator must read:
|
|
124
|
+
|
|
125
|
+
- `<run_dir>/output.md`
|
|
126
|
+
- `<run_dir>/<provider>.log`
|
|
127
|
+
- `git diff`
|
|
128
|
+
|
|
129
|
+
Then validate:
|
|
130
|
+
|
|
131
|
+
```yaml
|
|
132
|
+
review_checklist:
|
|
133
|
+
- [ ] Every acceptance criterion is satisfied.
|
|
134
|
+
- [ ] Diff scope matches the story and prompt.
|
|
135
|
+
- [ ] Article IV No Invention: every change traces to a requirement.
|
|
136
|
+
- [ ] Tests were added or updated when behavior changed.
|
|
137
|
+
- [ ] Lint, typecheck, and relevant tests pass.
|
|
138
|
+
- [ ] No story state was mutated before review approval.
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### 5. Accept or Iterate
|
|
142
|
+
|
|
143
|
+
- **Approved**: orchestrator updates story checkboxes, File List, status, and final validation evidence.
|
|
144
|
+
- **Rejected**: orchestrator writes specific feedback and may start a new run with a new slug or iteration suffix.
|
|
145
|
+
|
|
146
|
+
## Anti-Patterns
|
|
147
|
+
|
|
148
|
+
- Marking a story done by trusting the executor summary without reading the diff.
|
|
149
|
+
- Delegating @product-lead, @quality-gate, @sprint-lead, or @devops authority to an external runtime.
|
|
150
|
+
- Letting the executor create PRs, push, release, or mutate story state.
|
|
151
|
+
- Delegating vague work without acceptance criteria and file scope.
|
|
152
|
+
- Running with `danger-full-access` unless the surrounding environment is externally sandboxed.
|
|
@@ -318,45 +318,64 @@ If conflicts detected, fail with message:
|
|
|
318
318
|
Resolve conflicts before pushing.
|
|
319
319
|
```
|
|
320
320
|
|
|
321
|
-
### 4. Run
|
|
321
|
+
### 4. Run Layer 1 Quality Gate (lint + test + typecheck) — CANONICAL
|
|
322
322
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
323
|
+
> **Do NOT reimplement lint/test/typecheck here.** The framework already ships
|
|
324
|
+
> the canonical Layer 1 pre-commit gate (`core/quality-gates/layer1-precommit.js`),
|
|
325
|
+
> exposed via the CLI. Calling it keeps a single source of truth for the quality
|
|
326
|
+
> checks (Constitution Art. I — CLI First) instead of forking the logic.
|
|
327
327
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
}
|
|
328
|
+
```bash
|
|
329
|
+
sinapse qa run --layer=1
|
|
330
|
+
```
|
|
332
331
|
|
|
332
|
+
Layer 1 runs **lint (ESLint), unit tests (Jest), and typecheck** — fast local
|
|
333
|
+
checks — and gracefully skips any check whose npm script is absent. Exit code:
|
|
334
|
+
`0` = PASS, non-zero = FAIL.
|
|
335
|
+
|
|
336
|
+
```javascript
|
|
337
|
+
const { execSync } = require('child_process');
|
|
338
|
+
|
|
339
|
+
function runLayer1QualityGate(projectRoot) {
|
|
333
340
|
try {
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
});
|
|
338
|
-
console.log(`✓ ${scriptName} PASSED`);
|
|
341
|
+
// The canonical 3-check Layer 1 gate. stdio:'inherit' streams its report.
|
|
342
|
+
execSync('sinapse qa run --layer=1', { cwd: projectRoot, stdio: 'inherit' });
|
|
343
|
+
console.log('✓ Layer 1 (lint + test + typecheck) PASSED');
|
|
339
344
|
return { passed: true };
|
|
340
345
|
} catch (error) {
|
|
341
|
-
console.error(
|
|
346
|
+
console.error('❌ Layer 1 quality gate FAILED');
|
|
342
347
|
return { passed: false, error };
|
|
343
348
|
}
|
|
344
349
|
}
|
|
345
350
|
```
|
|
346
351
|
|
|
347
|
-
### 5. Run npm
|
|
352
|
+
### 5. Run npm run build (if script exists)
|
|
348
353
|
|
|
349
|
-
|
|
354
|
+
Build is outside Layer 1's scope (Layer 1 is the fast lint/test/typecheck pass),
|
|
355
|
+
so it stays a separate step. Skips gracefully when the `build` script is absent.
|
|
350
356
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
357
|
+
```javascript
|
|
358
|
+
function runBuild(projectRoot) {
|
|
359
|
+
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
360
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
354
361
|
|
|
355
|
-
|
|
362
|
+
if (!packageJson.scripts || !packageJson.scripts.build) {
|
|
363
|
+
console.log('⚠️ Script "build" not found - skipping');
|
|
364
|
+
return { skipped: true };
|
|
365
|
+
}
|
|
356
366
|
|
|
357
|
-
|
|
367
|
+
try {
|
|
368
|
+
execSync('npm run build', { cwd: projectRoot, stdio: 'inherit' });
|
|
369
|
+
console.log('✓ build PASSED');
|
|
370
|
+
return { passed: true };
|
|
371
|
+
} catch (error) {
|
|
372
|
+
console.error('❌ build FAILED');
|
|
373
|
+
return { passed: false, error };
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
```
|
|
358
377
|
|
|
359
|
-
###
|
|
378
|
+
### 6. Run CodeRabbit CLI Review (TR-3.14.12)
|
|
360
379
|
|
|
361
380
|
```javascript
|
|
362
381
|
const { execSync } = require('child_process');
|
|
@@ -503,7 +522,7 @@ if (coderabbitResult.gateImpact === 'CONCERNS') {
|
|
|
503
522
|
}
|
|
504
523
|
```
|
|
505
524
|
|
|
506
|
-
###
|
|
525
|
+
### 7. Run Security Scan (TR-3.14.11)
|
|
507
526
|
|
|
508
527
|
```javascript
|
|
509
528
|
const { execSync } = require('child_process');
|
|
@@ -630,7 +649,7 @@ function determineSecurityGate(results) {
|
|
|
630
649
|
}
|
|
631
650
|
```
|
|
632
651
|
|
|
633
|
-
###
|
|
652
|
+
### 7.1 Impact Analysis (Code Intelligence — Advisory Only)
|
|
634
653
|
|
|
635
654
|
> **Added by:** Story NOG-7 (DevOps Pre-Push Impact Analysis)
|
|
636
655
|
> **Behavior:** Advisory only — NEVER blocks push. Auto-skips if code intelligence unavailable.
|
|
@@ -688,7 +707,7 @@ Impact Analysis:
|
|
|
688
707
|
|
|
689
708
|
---
|
|
690
709
|
|
|
691
|
-
###
|
|
710
|
+
### 8. Verify Story Status (Optional - if using story-driven workflow)
|
|
692
711
|
|
|
693
712
|
```javascript
|
|
694
713
|
function checkStoryStatus(storyPath) {
|
|
@@ -735,9 +754,7 @@ Mode: {framework-development | project-development}
|
|
|
735
754
|
Quality Checks:
|
|
736
755
|
✓ No uncommitted changes
|
|
737
756
|
✓ No merge conflicts
|
|
738
|
-
✓
|
|
739
|
-
✓ npm test PASSED
|
|
740
|
-
✓ npm run typecheck PASSED
|
|
757
|
+
✓ Layer 1 (lint+test+typecheck) PASSED (via `sinapse qa run --layer=1`)
|
|
741
758
|
✓ npm run build PASSED
|
|
742
759
|
✓ Security scan PASSED
|
|
743
760
|
⚠️ Story status SKIPPED (no story file)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Task: Update SINAPSE Framework
|
|
2
2
|
|
|
3
|
-
> **Version:**
|
|
3
|
+
> **Version:** 5.2.0
|
|
4
4
|
> **Created:** 2026-01-29
|
|
5
|
-
> **Updated:** 2026-
|
|
5
|
+
> **Updated:** 2026-06-15
|
|
6
6
|
> **Type:** SYNC (git-native framework synchronization)
|
|
7
7
|
> **Agent:** @devops (Pipeline) or @sinapse (Orion)
|
|
8
|
-
> **Execution:**
|
|
8
|
+
> **Execution:** Bash script (~150 lines)
|
|
9
9
|
|
|
10
10
|
## Purpose
|
|
11
11
|
|
|
@@ -39,15 +39,12 @@ function readStdin() {
|
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
// Story 10.47: delegate to the shared grounding config loader instead of
|
|
43
|
+
// duplicating the read+parse. The require is guarded so the hook stays
|
|
44
|
+
// fail-open even if the shared module is somehow absent at runtime.
|
|
42
45
|
function loadConfig() {
|
|
43
46
|
try {
|
|
44
|
-
|
|
45
|
-
const raw = fs.readFileSync(CONFIG_PATH, 'utf8');
|
|
46
|
-
let yaml;
|
|
47
|
-
try { yaml = require('js-yaml'); } catch { return null; }
|
|
48
|
-
const parsed = yaml.load(raw);
|
|
49
|
-
if (!parsed || typeof parsed !== 'object') return null;
|
|
50
|
-
return parsed;
|
|
47
|
+
return require('../core/grounding/config-loader.cjs').loadGroundingConfig(CONFIG_PATH);
|
|
51
48
|
} catch {
|
|
52
49
|
return null;
|
|
53
50
|
}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* (PT + EN). Injects the first 3000 chars of the DS law file as
|
|
12
12
|
* `<ds-grounding>` context.
|
|
13
13
|
*
|
|
14
|
-
* Coexistence:
|
|
14
|
+
* Coexistence: the user's personal `~/.claude/hooks/design-system-grounding.cjs` (if present)
|
|
15
15
|
* reads `ds-routing.json` and is unaffected by this file.
|
|
16
16
|
*/
|
|
17
17
|
|
|
@@ -66,15 +66,12 @@ function readStdin() {
|
|
|
66
66
|
});
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
// Story 10.47: delegate to the shared grounding config loader instead of
|
|
70
|
+
// duplicating the read+parse. The require is guarded so the hook stays
|
|
71
|
+
// fail-open even if the shared module is somehow absent at runtime.
|
|
69
72
|
function loadConfig() {
|
|
70
73
|
try {
|
|
71
|
-
|
|
72
|
-
const raw = fs.readFileSync(CONFIG_PATH, 'utf8');
|
|
73
|
-
let yaml;
|
|
74
|
-
try { yaml = require('js-yaml'); } catch { return null; }
|
|
75
|
-
const parsed = yaml.load(raw);
|
|
76
|
-
if (!parsed || typeof parsed !== 'object') return null;
|
|
77
|
-
return parsed;
|
|
74
|
+
return require('../core/grounding/config-loader.cjs').loadGroundingConfig(CONFIG_PATH);
|
|
78
75
|
} catch {
|
|
79
76
|
return null;
|
|
80
77
|
}
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
*
|
|
14
14
|
* Coexistence with personal hooks: this file lives at
|
|
15
15
|
* .sinapse-ai/hooks/sinapse-vault-grounding.cjs
|
|
16
|
-
* and reads `sinapse-ai-config.yaml`.
|
|
17
|
-
* `~/.claude/hooks/vault-grounding.cjs` reads `vault-routing.json`. The two
|
|
16
|
+
* and reads `sinapse-ai-config.yaml`. The user's personal
|
|
17
|
+
* `~/.claude/hooks/vault-grounding.cjs` (if present) reads `vault-routing.json`. The two
|
|
18
18
|
* files do not collide (different filenames, different config sources, and
|
|
19
19
|
* fail-open guarantees that running both is safe).
|
|
20
20
|
*
|
|
@@ -54,15 +54,12 @@ function readStdin() {
|
|
|
54
54
|
});
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
// Story 10.47: delegate to the shared grounding config loader instead of
|
|
58
|
+
// duplicating the read+parse. The require is guarded so the hook stays
|
|
59
|
+
// fail-open even if the shared module is somehow absent at runtime.
|
|
57
60
|
function loadConfig() {
|
|
58
61
|
try {
|
|
59
|
-
|
|
60
|
-
const raw = fs.readFileSync(CONFIG_PATH, 'utf8');
|
|
61
|
-
let yaml;
|
|
62
|
-
try { yaml = require('js-yaml'); } catch { return null; }
|
|
63
|
-
const parsed = yaml.load(raw);
|
|
64
|
-
if (!parsed || typeof parsed !== 'object') return null;
|
|
65
|
-
return parsed;
|
|
62
|
+
return require('../core/grounding/config-loader.cjs').loadGroundingConfig(CONFIG_PATH);
|
|
66
63
|
} catch {
|
|
67
64
|
return null;
|
|
68
65
|
}
|
|
@@ -42,7 +42,10 @@ const DEFAULT_CONFIG = {
|
|
|
42
42
|
},
|
|
43
43
|
},
|
|
44
44
|
claude: {
|
|
45
|
-
model:
|
|
45
|
+
// No hardcoded model: stale IDs get rejected by the CLI ("model may not
|
|
46
|
+
// exist"). null → omit --model and use the user's CLI default, which is
|
|
47
|
+
// always a valid, current model.
|
|
48
|
+
model: null,
|
|
46
49
|
timeout: 300000,
|
|
47
50
|
dangerouslySkipPermissions: false,
|
|
48
51
|
},
|