Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 137 additions & 0 deletions .aiox-core/core/ids/gates/g5-semantic-handshake.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
'use strict';

/**
* G5 — Semantic Handshake Gate
*
* Agent: @dev
* Type: Automated, Blocking when Semantic Handshake reports BLOCKER violations
*
* Purpose: Preserve intent integrity between planning and implementation by
* evaluating proposed code against executable planning constraints.
*/

const path = require('path');
const { VerificationGate } = require(path.resolve(__dirname, '../verification-gate.js'));
const {
SemanticHandshakeEngine,
} = require(path.resolve(__dirname, '../../synapse/context/semantic-handshake-engine.js'));

const G5_DEFAULT_TIMEOUT_MS = 2000;

class G5SemanticHandshakeGate extends VerificationGate {
constructor(options = {}) {
super({
gateId: 'G5',
agent: '@dev',
blocking: true,
timeoutMs: options.timeoutMs ?? G5_DEFAULT_TIMEOUT_MS,
circuitBreakerOptions: options.circuitBreakerOptions,
logger: options.logger,
});

this._engine = options.engine || new SemanticHandshakeEngine({
constraints: options.constraints || [],
});
}

async _doVerify(context = {}) {
const engine = this._createScopedEngine(context);

Comment thread
coderabbitai[bot] marked this conversation as resolved.
const constraints = engine.getConstraints();
if (constraints.length === 0) {
return {
passed: true,
warnings: ['No Semantic Handshake constraints registered'],
opportunities: [],
};
}

if (!this._hasCodeContext(context)) {
return {
passed: true,
warnings: ['Semantic Handshake constraints registered, but no proposed code was provided'],
opportunities: constraints.map(constraint => ({
entity: constraint.id,
recommendation: constraint.description,
severity: constraint.severity,
})),
};
}

const result = await engine.validateExecutionIntent(context);
const report = engine.generateComplianceReport(result);

return {
passed: result.passed,
warnings: [
...result.warnings,
...result.blockingViolations.map(violation => violation.message),
],
opportunities: [
...result.verifiedConstraints.map(constraint => ({
entity: constraint.id,
recommendation: 'Constraint verified',
severity: constraint.severity,
})),
...result.violations.map(violation => ({
entity: violation.id,
recommendation: violation.message,
severity: violation.severity,
matches: violation.matches,
})),
],
override: result.passed ? null : {
reason: 'Semantic Handshake blocker violation',
correctionPrompt: result.correctionPrompt,
report,
},
};
}

_createScopedEngine(context) {
const engine = typeof this._engine.clone === 'function'
? this._engine.clone()
: new SemanticHandshakeEngine();

this._registerContextConstraints(engine, context);
return engine;
}

_registerContextConstraints(engine, context) {
const planningText = [
context.planningOutput,
context.planningText,
context.architectureText,
context.storyText,
].filter(Boolean).join('\n\n');

if (planningText.trim()) {
engine.registerConstraints(planningText, {
source: context.source || '@architect',
metadata: {
storyId: context.storyId || 'unknown',
},
});
}

if (Array.isArray(context.constraints)) {
for (const constraint of context.constraints) {
engine.addConstraint(constraint);
}
}
}

_hasCodeContext(context) {
return Boolean(
context.proposedCode ||
(Array.isArray(context.files) && context.files.length > 0) ||
(Array.isArray(context.diffs) && context.diffs.length > 0) ||
(Array.isArray(context.codeFiles) && context.codeFiles.length > 0),
);
}
}

module.exports = {
G5SemanticHandshakeGate,
G5_DEFAULT_TIMEOUT_MS,
};
8 changes: 7 additions & 1 deletion .aiox-core/core/ids/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ const { G1EpicCreationGate } = require('./gates/g1-epic-creation');
const { G2StoryCreationGate } = require('./gates/g2-story-creation');
const { G3StoryValidationGate } = require('./gates/g3-story-validation');
const { G4DevContextGate, G4_DEFAULT_TIMEOUT_MS } = require('./gates/g4-dev-context');
const {
G5SemanticHandshakeGate,
G5_DEFAULT_TIMEOUT_MS,
} = require('./gates/g5-semantic-handshake');

// IDS-7: Framework Governor (aiox-master integration)
const {
Expand Down Expand Up @@ -142,12 +146,14 @@ module.exports = {
createGateResult,
DEFAULT_TIMEOUT_MS,

// IDS-5a: Gates G1-G4
// IDS-5a/5b: Gates G1-G5
G1EpicCreationGate,
G2StoryCreationGate,
G3StoryValidationGate,
G4DevContextGate,
G4_DEFAULT_TIMEOUT_MS,
G5SemanticHandshakeGate,
G5_DEFAULT_TIMEOUT_MS,

// IDS-7: Framework Governor
FrameworkGovernor,
Expand Down
31 changes: 31 additions & 0 deletions .aiox-core/core/synapse/context/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,34 @@ The manager does not call OpenAI, Anthropic, Claude, Gemini, Kimi or OpenRouter
directly. Inject a summarizer when an agent loop wants model-based compression.
If that summarizer fails, the manager emits `swap:error` and falls back to a
local extractive summary instead of crashing the loop.

## SemanticHandshakeEngine

`SemanticHandshakeEngine` turns planning constraints into executable checks that
can run before implementation. It is deterministic by default and does not call
an LLM.

```javascript
const { SemanticHandshakeEngine } = require('./.aiox-core/core/synapse/context');

const handshake = new SemanticHandshakeEngine();
handshake.registerConstraints('Use serverless functions with PostgreSQL.');

const result = await handshake.validateExecutionIntent({
files: [
{
path: 'src/db.js',
content: "const { Pool } = require('pg');",
},
],
});

if (!result.passed) {
throw new Error(handshake.generateComplianceReport(result));
}
```

The first supported extraction rules cover PostgreSQL, serverless local-state
writes, absolute imports and `eval` bans. Callers can also add structured custom
constraints with `addConstraint()`. Use `toContextMessage()` to inject the
handshake result into an agent context as a system message.
10 changes: 7 additions & 3 deletions .aiox-core/core/synapse/context/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@

'use strict';

const contextTracker = require('./context-tracker');
const contextBuilder = require('./context-builder');
const hierarchicalContext = require('./hierarchical-context-manager');
const path = require('path');

const contextTracker = require(path.resolve(__dirname, './context-tracker.js'));
const contextBuilder = require(path.resolve(__dirname, './context-builder.js'));
const hierarchicalContext = require(path.resolve(__dirname, './hierarchical-context-manager.js'));
const semanticHandshake = require(path.resolve(__dirname, './semantic-handshake-engine.js'));

module.exports = {
...contextTracker,
...contextBuilder,
...hierarchicalContext,
...semanticHandshake,
};
Loading
Loading