Architectural Rules
Synapses lets you define architectural rules that are checked against the code graph. Rules surface violations in context responses, preventing agents from introducing (or missing) structural problems.
Defining Rules in synapses.json
Add rules to the rules array in your project configuration:
{ "rules": [ { "id": "no-direct-db-from-handlers", "description": "HTTP handlers must not import database packages directly", "type": "dependency", "source": "internal/handlers/**", "target": "internal/database/**", "action": "deny" }, { "id": "service-layer-required", "description": "Handlers must go through the service layer to access data", "type": "path", "source": "internal/handlers/**", "target": "internal/database/**", "via": "internal/service/**", "action": "require" } ]}Rule Types
Dependency Rules
Control which packages or files can import/call each other.
{ "type": "dependency", "source": "internal/api/**", "target": "internal/infra/**", "action": "deny"}Actions:
deny— the source must NOT depend on the targetallow— explicitly permit (useful when a broader deny exists)
Edge Type Rules
Control specific relationship types between entities.
{ "type": "edge", "edge_type": "CALLS", "source": "internal/handlers/**", "target": "internal/database/**", "action": "deny"}Supported edge types: CALLS, IMPORTS, IMPLEMENTS, EMBEDS, RETURNS, DEPENDS_ON, TESTED_BY.
Path Pattern Rules (Multi-Hop)
Require or deny multi-hop paths through the graph.
{ "type": "path", "source": "cmd/**", "target": "internal/database/**", "via": "internal/service/**", "action": "require"}This rule says: any path from cmd/ to internal/database/ must pass through internal/service/. A direct dependency from cmd/ to internal/database/ is a violation.
Creating Rules Dynamically
Use rules(action="upsert") to create or update rules at runtime:
{ "tool": "rules", "action": "upsert", "arguments": { "id": "no-circular-service-deps", "description": "Services must not have circular dependencies", "type": "dependency", "source": "internal/service/users/**", "target": "internal/service/billing/**", "action": "deny", "bidirectional": true }}Rules created via rules(action="upsert") are persisted and take effect immediately.
File Pattern Matching
Source and target fields use glob patterns:
| Pattern | Matches |
|---|---|
internal/handlers/** | All files recursively under handlers |
internal/handlers/*.go | Go files directly in handlers |
**/test_*.py | Test files anywhere in the tree |
cmd/api/main.go | Exact file match |
Checking Violations
In get_context
When you call get_context or get_context(mode="intent"), any rule violations affecting the returned entities are included automatically:
// Response includes:{ "violations": [ { "rule_id": "no-direct-db-from-handlers", "description": "HTTP handlers must not import database packages directly", "source": "internal/handlers/user.go:HandleCreateUser", "target": "internal/database/queries.go:InsertUser", "edge_type": "CALLS" } ]}With validate(action=“list”)
Query violations directly:
{ "tool": "validate", "action": "list", "arguments": { "file": "internal/handlers/user.go" }}Returns all rule violations involving the specified file. Omit file to get all violations in the project.
Rule Candidates from Failure Episodes
When agents record failure episodes (memory(action="save") with episode_type: "failure"), Synapses can suggest rules that would prevent recurrence.
{ "tool": "rules", "arguments": { "action": "candidates" }}Returns suggested rules derived from patterns in failure episodes. For example, if multiple failures involve handlers directly calling database functions, Synapses suggests a dependency deny rule.
You can then promote a candidate to an active rule:
{ "tool": "rules", "action": "upsert", "arguments": { "id": "candidate-001", "description": "Auto-suggested: prevent handler-to-db coupling", "type": "dependency", "source": "internal/handlers/**", "target": "internal/database/**", "action": "deny" }}Example: Layered Architecture
A full layered architecture rule set:
{ "rules": [ { "id": "layer-handlers-no-db", "description": "Handlers cannot access database directly", "type": "dependency", "source": "internal/handlers/**", "target": "internal/database/**", "action": "deny" }, { "id": "layer-handlers-use-service", "description": "Handlers must use service layer", "type": "dependency", "source": "internal/handlers/**", "target": "internal/service/**", "action": "allow" }, { "id": "layer-service-no-handlers", "description": "Services cannot depend on handlers", "type": "dependency", "source": "internal/service/**", "target": "internal/handlers/**", "action": "deny" }, { "id": "layer-no-cmd-to-db", "description": "CLI commands must go through service layer", "type": "path", "source": "cmd/**", "target": "internal/database/**", "via": "internal/service/**", "action": "require" } ]}