@ismaelsoilet/synapsejs 1.1.1 → 1.2.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/bin/synapse.ts CHANGED
@@ -534,7 +534,11 @@ async function main() {
534
534
  }
535
535
 
536
536
  case 'test': {
537
- const report = await runSliceOracles(root);
537
+ const enableGate =
538
+ process.argv.includes('--gate') ||
539
+ fs.existsSync(path.join(root, '.jev.json')) ||
540
+ Boolean(process.env.JEV_ACTIVE || process.env.JEV_PROVIDER || process.env.TYPESAFE_API_KEY);
541
+ const report = await runSliceOracles(root, { enableJevTriage: enableGate });
538
542
 
539
543
  process.stdout.write(`${JSON.stringify({ operation: 'PBT_ORACLE_TEST_SUITE', ...report }, null, 2)}\n`);
540
544
  process.exit(report.status === 'PASS' ? 0 : 1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ismaelsoilet/synapsejs",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "description": "Bun-only fullstack framework where a feature is a single contiguous file",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -50,6 +50,7 @@
50
50
  "access": "public"
51
51
  },
52
52
  "dependencies": {
53
+ "@ismaelsoilet/jev-harness": "^0.2.1",
53
54
  "@sinclair/typebox": "^0.34.40",
54
55
  "fast-check": "^3.23.2",
55
56
  "postgres": "^3.4.9",
@@ -6,6 +6,7 @@
6
6
  * Preserves Locality of Behavior (LoB): each slice owns its own table contracts!
7
7
  */
8
8
 
9
+ import { type AbortGateResult, shouldAbortTrajectory } from '@ismaelsoilet/jev-harness';
9
10
  import * as fs from 'fs';
10
11
  import * as path from 'path';
11
12
  import * as ts from 'typescript';
@@ -34,6 +35,7 @@ export interface MigrationReport {
34
35
  code?: SlicesDirErrorCode;
35
36
  message?: string;
36
37
  candidates?: string[];
38
+ abortSignal?: AbortGateResult;
37
39
  }
38
40
 
39
41
  export interface BidirectionalDdl {
@@ -404,13 +406,28 @@ export async function runSliceMigrations(
404
406
  }
405
407
  }
406
408
 
409
+ let abortSignal: AbortGateResult | undefined;
410
+ if (hasFailure) {
411
+ try {
412
+ const failureDetails = migrations
413
+ .filter((m) => m.status === 'FAILED')
414
+ .map((m) => `Slice ${m.slice}: ${m.error}`)
415
+ .join('\n');
416
+ abortSignal = await shouldAbortTrajectory(
417
+ `Apply sliceSchema migrations across discovered slices in ${baseDir}`,
418
+ failureDetails
419
+ );
420
+ } catch {}
421
+ }
422
+
407
423
  return {
408
424
  status: hasFailure ? 'FAIL' : 'PASS',
409
425
  totalDiscovered: migrations.length,
410
426
  appliedCount,
411
427
  skippedCount,
412
428
  statementsApplied: statementsAppliedTotal,
413
- migrations
429
+ migrations,
430
+ abortSignal
414
431
  };
415
432
  } finally {
416
433
  if (isPostgres) {
@@ -9,6 +9,7 @@
9
9
  * Nothing here may report PASS when no invariant was executed.
10
10
  */
11
11
 
12
+ import { type TestTriageResult, triageTestFailure } from '@ismaelsoilet/jev-harness';
12
13
  import * as fs from 'fs';
13
14
  import * as path from 'path';
14
15
  import { findSliceFiles, resolveSlicesDir, type SlicesDirErrorCode } from './slice-discovery';
@@ -42,6 +43,7 @@ export interface OracleReport {
42
43
  code?: OracleErrorCode;
43
44
  message?: string;
44
45
  candidates?: string[];
46
+ triage?: TestTriageResult;
45
47
  }
46
48
 
47
49
  export interface JunitSuite {
@@ -154,6 +156,8 @@ export function oracleWrapperSource(importPath: string, sliceName: string): stri
154
156
  export interface OracleRunOptions {
155
157
  /** Injected by tests: runs a command and returns its exit code and output. */
156
158
  spawn?: (args: string[], cwd: string) => Promise<{ exitCode: number; output: string }>;
159
+ /** Enable Jev System One semantic test triage on failures. Default: true */
160
+ enableJevTriage?: boolean;
157
161
  }
158
162
 
159
163
  async function defaultSpawn(args: string[], cwd: string): Promise<{ exitCode: number; output: string }> {
@@ -241,6 +245,12 @@ export async function runSliceOracles(
241
245
  );
242
246
 
243
247
  if (!fs.existsSync(reportPath)) {
248
+ let triageResult: TestTriageResult | undefined;
249
+ if (options.enableJevTriage !== false) {
250
+ try {
251
+ triageResult = await triageTestFailure(output.trim() || `O runner Bun test falhou com exit ${exitCode}`);
252
+ } catch {}
253
+ }
244
254
  return {
245
255
  status: 'FAIL',
246
256
  totalSlices: wrappers.size,
@@ -249,7 +259,8 @@ export async function runSliceOracles(
249
259
  passedCases: 0,
250
260
  results: [],
251
261
  code: 'RUNNER_FAILED',
252
- message: `O runner não produziu relatório (exit ${exitCode}). ${output.trim().slice(-400)}`.trim()
262
+ message: `O runner não produziu relatório (exit ${exitCode}). ${output.trim().slice(-400)}`.trim(),
263
+ triage: triageResult
253
264
  };
254
265
  }
255
266
 
@@ -268,6 +279,12 @@ export async function runSliceOracles(
268
279
  const passedSlices = results.filter((item) => item.passed).length;
269
280
 
270
281
  if (totalCases === 0) {
282
+ let triageResult: TestTriageResult | undefined;
283
+ if (options.enableJevTriage !== false) {
284
+ try {
285
+ triageResult = await triageTestFailure(`Nenhum invariante foi executado em ${results.length} fatia(s).`);
286
+ } catch {}
287
+ }
271
288
  return {
272
289
  status: 'FAIL',
273
290
  slicesDir: path.relative(root, slicesDir),
@@ -277,17 +294,34 @@ export async function runSliceOracles(
277
294
  passedCases: 0,
278
295
  results,
279
296
  code: 'NO_CASES',
280
- message: `Nenhum invariante foi executado em ${results.length} fatia(s).`
297
+ message: `Nenhum invariante foi executado em ${results.length} fatia(s).`,
298
+ triage: triageResult
281
299
  };
282
300
  }
283
301
 
302
+ const isPass = passedSlices === results.length;
303
+ let triageResult: TestTriageResult | undefined;
304
+ if (!isPass && options.enableJevTriage !== false) {
305
+ try {
306
+ const failedSummaries = results
307
+ .filter((r) => !r.passed)
308
+ .flatMap((r) =>
309
+ r.cases.filter((c) => !c.passed).map((c) => `[${r.slice}] ${c.name}: ${c.message || 'assertion failed'}`)
310
+ )
311
+ .join('\n');
312
+ const failureTrace = failedSummaries || output.trim() || 'Slice oracle invariants failed';
313
+ triageResult = await triageTestFailure(failureTrace);
314
+ } catch {}
315
+ }
316
+
284
317
  return {
285
- status: passedSlices === results.length ? 'PASS' : 'FAIL',
318
+ status: isPass ? 'PASS' : 'FAIL',
286
319
  slicesDir: path.relative(root, slicesDir),
287
320
  totalSlices: results.length,
288
321
  passedSlices,
289
322
  totalCases,
290
323
  passedCases,
291
- results
324
+ results,
325
+ triage: triageResult
292
326
  };
293
327
  }
package/src/mcp/server.ts CHANGED
@@ -6,6 +6,16 @@
6
6
  * directly to AI Agents (Cursor, Claude Code, Windsurf, Antigravity) via JSON-RPC 2.0 over stdio.
7
7
  */
8
8
 
9
+ import {
10
+ type AbortGateResult,
11
+ modulateReasoningEffort,
12
+ type ReasoningEffortResult,
13
+ shouldAbortTrajectory,
14
+ type TestTriageResult,
15
+ triageTestFailure,
16
+ type VerificationResult,
17
+ verifyStepCompletion
18
+ } from '@ismaelsoilet/jev-harness';
9
19
  import * as fs from 'fs';
10
20
  import * as path from 'path';
11
21
  import * as readline from 'readline';
@@ -80,7 +90,7 @@ export class SynapseMcpServer {
80
90
  },
81
91
  serverInfo: {
82
92
  name: 'synapse-mcp',
83
- version: '1.1.0'
93
+ version: '1.2.0'
84
94
  }
85
95
  }
86
96
  };
@@ -201,6 +211,67 @@ export class SynapseMcpServer {
201
211
  steps: { type: 'number', description: 'Number of recent migrations to rollback (default: 1)' }
202
212
  }
203
213
  }
214
+ },
215
+ {
216
+ name: 'synapse_test_gate',
217
+ description:
218
+ 'Execute Fast-Check PBT oracles and immediately triage failures with Jev System One semantic test-gate to prevent LLM token waste',
219
+ inputSchema: {
220
+ type: 'object',
221
+ properties: {}
222
+ }
223
+ },
224
+ {
225
+ name: 'synapse_abort_check',
226
+ description:
227
+ 'Evaluate plan and error history with Jev System One to detect doomed trajectories, circular loops, or high-risk refactor dead ends',
228
+ inputSchema: {
229
+ type: 'object',
230
+ required: ['plan', 'history'],
231
+ properties: {
232
+ plan: { type: 'string', description: 'Proposed implementation plan or architectural change' },
233
+ history: { type: 'string', description: 'Recent attempt history, error logs, or failure reasons' }
234
+ }
235
+ }
236
+ },
237
+ {
238
+ name: 'synapse_verify_completion',
239
+ description:
240
+ 'Verify slice implementation and test outputs against acceptance criteria using Jev System One before committing work',
241
+ inputSchema: {
242
+ type: 'object',
243
+ required: ['criteria', 'output'],
244
+ properties: {
245
+ criteria: { type: 'string', description: 'Acceptance criteria or requirement specifications' },
246
+ output: {
247
+ type: 'string',
248
+ description: 'Actual implementation summary, test output, or slice behavior'
249
+ }
250
+ }
251
+ }
252
+ },
253
+ {
254
+ name: 'synapse_reasoning_effort',
255
+ description:
256
+ 'Dynamically modulate agent reasoning effort (Astra-Jev) to low/medium/high based on step context (lowering effort for mechanical commands like split/migrate/skeleton)',
257
+ inputSchema: {
258
+ type: 'object',
259
+ required: ['context'],
260
+ properties: {
261
+ context: {
262
+ type: 'string',
263
+ description: 'Immediate command or task context, e.g. "synapse split", "git commit"'
264
+ },
265
+ provider: {
266
+ type: 'string',
267
+ description: 'Target LLM provider, e.g. "deepseek", "anthropic", "openai"'
268
+ },
269
+ sessionContextTokens: {
270
+ type: 'number',
271
+ description: 'Estimated session context token count to protect prompt cache'
272
+ }
273
+ }
274
+ }
204
275
  }
205
276
  ]
206
277
  }
@@ -503,6 +574,29 @@ export class SynapseMcpServer {
503
574
  steps: args.steps
504
575
  });
505
576
  contentText = JSON.stringify(report, null, 2);
577
+ } else if (toolName === 'synapse_test_gate') {
578
+ const report = await runSliceOracles(this.root, { enableJevTriage: true });
579
+ contentText = JSON.stringify(report, null, 2);
580
+ } else if (toolName === 'synapse_abort_check') {
581
+ const plan = String(args.plan || '');
582
+ const history = String(args.history || '');
583
+ const result: AbortGateResult = await shouldAbortTrajectory(plan, history);
584
+ contentText = JSON.stringify(result, null, 2);
585
+ } else if (toolName === 'synapse_verify_completion') {
586
+ const criteria = String(args.criteria || '');
587
+ const output = String(args.output || '');
588
+ const result: VerificationResult = await verifyStepCompletion(criteria, output);
589
+ contentText = JSON.stringify(result, null, 2);
590
+ } else if (toolName === 'synapse_reasoning_effort') {
591
+ const context = String(args.context || '');
592
+ const provider = args.provider ? String(args.provider) : undefined;
593
+ const sessionContextTokens =
594
+ typeof args.sessionContextTokens === 'number' ? args.sessionContextTokens : undefined;
595
+ const result: ReasoningEffortResult = await modulateReasoningEffort(context, {
596
+ provider,
597
+ sessionContextTokens
598
+ });
599
+ contentText = JSON.stringify(result, null, 2);
506
600
  } else {
507
601
  return {
508
602
  jsonrpc: '2.0',
@@ -993,7 +993,7 @@ ${this.renderStylesheets()}
993
993
  {
994
994
  status,
995
995
  framework: 'SynapseJS',
996
- version: '1.1.0',
996
+ version: '1.2.0',
997
997
  uptime: process.uptime(),
998
998
  database: dbHealthy ? 'connected' : 'disconnected',
999
999
  ...(dbError ? { databaseError: dbError } : {}),
@@ -0,0 +1,6 @@
1
+ {
2
+ "provider": "opencode",
3
+ "model": "typesafe/jev",
4
+ "skip_llm_threshold": 0.65,
5
+ "abort_threshold": 0.70
6
+ }
@@ -12,6 +12,7 @@
12
12
  "mcp": "synapse mcp",
13
13
  "skeleton": "synapse skeleton",
14
14
  "test": "synapse test",
15
+ "test:gate": "synapse test --gate",
15
16
  "new-slice": "synapse new-slice",
16
17
  "split": "synapse split",
17
18
  "build": "synapse build",