Implement consistency architecture (10-principle framework)
Adds: - ARCHITECTURE.md: Full documentation of 3-layer system - Context Pipeline: Preprocessing layer (pipeline.js) - Workflow Router: 4 fixed workflows (coding, debug, deploy, audit) - Validation Layer: Post-response quality gate (validator.js) - Format Templates: Structured output templates - TOOLS.md: Beavault connection documentation Architecture: - Layer 1: Behavior rules (always injected) - Layer 2: Persistent facts (structured memory) - Layer 3: Ephemeral context - Priority enforcement: Rules > Prefs > Task > Chat - Memory write policy: Only confirmed fixes, repeated preferences
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Context Pipeline - Preprocessing Layer
|
||||
*
|
||||
* Every request flows through:
|
||||
* 1. Load Rules → 2. Load Preferences → 3. Load Memory → 4. Build Packet
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
class ContextPipeline {
|
||||
constructor() {
|
||||
this.rules = [];
|
||||
this.preferences = [];
|
||||
this.memory = [];
|
||||
this.context = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Step 1: Load Behavior Rules (always injected)
|
||||
*/
|
||||
async loadRules() {
|
||||
const ruleFiles = [
|
||||
'SOUL.md',
|
||||
'AGENTS.md',
|
||||
'IDENTITY.md',
|
||||
'MEMORY.md'
|
||||
];
|
||||
|
||||
for (const file of ruleFiles) {
|
||||
const content = await this.readFile(file);
|
||||
if (content) {
|
||||
this.rules.push({
|
||||
source: file,
|
||||
content: content,
|
||||
priority: 100 // Absolute priority
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Load active workflow rules if specified
|
||||
const workflow = process.env.ACTIVE_WORKFLOW;
|
||||
if (workflow) {
|
||||
const workflowRules = await this.readFile(`workflows/${workflow}.md`);
|
||||
if (workflowRules) {
|
||||
this.rules.push({
|
||||
source: `workflows/${workflow}.md`,
|
||||
content: workflowRules,
|
||||
priority: 95
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Step 2: Load Persistent Preferences (from structured memory)
|
||||
*/
|
||||
async loadPreferences() {
|
||||
const prefFiles = [
|
||||
'memory/items/mem_pref:communication_style.json',
|
||||
'memory/items/mem_pref:format.json',
|
||||
'memory/items/mem_pref:memory_persistence.json',
|
||||
'memory/items/mem_identity:name.json',
|
||||
'memory/items/mem_identity:role.json'
|
||||
];
|
||||
|
||||
for (const file of prefFiles) {
|
||||
const data = await this.readJson(file);
|
||||
if (data) {
|
||||
this.preferences.push({
|
||||
source: file,
|
||||
content: data.content,
|
||||
priority: data.importance || 7,
|
||||
tags: data.tags || []
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Step 3: Load Relevant Memory
|
||||
*/
|
||||
async loadRelevantMemory(query, maxResults = 5) {
|
||||
// For now, load recent episodic memories
|
||||
// Future: implement vector search
|
||||
|
||||
const memoryFiles = [
|
||||
'memory/2026-07-04.md',
|
||||
'memory/2026-07-03.md'
|
||||
];
|
||||
|
||||
for (const file of memoryFiles) {
|
||||
const content = await this.readFile(file);
|
||||
if (content) {
|
||||
this.memory.push({
|
||||
source: file,
|
||||
content: content.substring(0, 2000), // Truncate
|
||||
priority: 5
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Load project memory if PROJECT context set
|
||||
const project = process.env.CURRENT_PROJECT;
|
||||
if (project) {
|
||||
const projectStatus = await this.readFile(`Projects/${project}/memory/STATUS.md`);
|
||||
if (projectStatus) {
|
||||
this.memory.push({
|
||||
source: `Projects/${project}/memory/STATUS.md`,
|
||||
content: projectStatus,
|
||||
priority: 8
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Step 4: Build Context Packet
|
||||
*/
|
||||
buildPacket(taskInput) {
|
||||
// Sort by priority (highest first)
|
||||
const allContext = [
|
||||
...this.rules,
|
||||
...this.preferences.sort((a, b) => b.priority - a.priority),
|
||||
...this.memory
|
||||
];
|
||||
|
||||
const packet = {
|
||||
system: allContext.filter(c => c.priority >= 90).map(c => c.content).join('\n\n---\n\n'),
|
||||
preferences: allContext.filter(c => c.priority >= 5 && c.priority < 90).map(c => c.content).join('\n\n'),
|
||||
memory: allContext.filter(c => c.priority < 5).map(c => c.content).join('\n\n'),
|
||||
task: taskInput,
|
||||
metadata: {
|
||||
rules_count: this.rules.length,
|
||||
prefs_count: this.preferences.length,
|
||||
memory_count: this.memory.length
|
||||
}
|
||||
};
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility: Read file from workspace
|
||||
*/
|
||||
async readFile(filepath) {
|
||||
const fullPath = path.join('/home/jcbeasley/.openclaw/workspace', filepath);
|
||||
try {
|
||||
return fs.readFileSync(fullPath, 'utf8');
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility: Read JSON file
|
||||
*/
|
||||
async readJson(filepath) {
|
||||
const fullPath = path.join('/home/jcbeasley/.openclaw/workspace', filepath);
|
||||
try {
|
||||
const content = fs.readFileSync(fullPath, 'utf8');
|
||||
return JSON.parse(content);
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Export for use
|
||||
module.exports = ContextPipeline;
|
||||
|
||||
// CLI usage
|
||||
if (require.main === module) {
|
||||
const pipeline = new ContextPipeline();
|
||||
|
||||
pipeline
|
||||
.loadRules()
|
||||
.then(() => pipeline.loadPreferences())
|
||||
.then(() => pipeline.loadRelevantMemory(process.argv[2] || ''))
|
||||
.then(() => {
|
||||
const packet = pipeline.buildPacket(process.argv[2] || 'No task specified');
|
||||
console.log(JSON.stringify(packet, null, 2));
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Pipeline error:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Validation Layer - Post-Response Quality Gate
|
||||
*
|
||||
* After LLM responds, validate against:
|
||||
* - Rules followed
|
||||
* - Format compliance
|
||||
* - Workflow adherence
|
||||
* - Safety constraints
|
||||
*/
|
||||
|
||||
class ResponseValidator {
|
||||
constructor(workflow) {
|
||||
this.workflow = workflow;
|
||||
this.errors = [];
|
||||
this.warnings = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a response against all checks
|
||||
*/
|
||||
validate(response) {
|
||||
this.errors = [];
|
||||
this.warnings = [];
|
||||
|
||||
this.checkRulesFollowed(response);
|
||||
this.checkFormatCompliance(response);
|
||||
this.checkWorkflowAdherence(response);
|
||||
this.checkSafetyConstraints(response);
|
||||
|
||||
return {
|
||||
passed: this.errors.length === 0,
|
||||
errors: this.errors,
|
||||
warnings: this.warnings,
|
||||
summary: this.buildSummary()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check 1: Rules Followed
|
||||
*/
|
||||
checkRulesFollowed(response) {
|
||||
// Check for prohibited phrases
|
||||
const prohibited = [
|
||||
{ pattern: /it should work/i, msg: "Assumed working without verification" },
|
||||
{ pattern: /probably|maybe|likely/i, msg: "Speculative language detected" },
|
||||
{ pattern: /i think/i, msg: "Unverified assumption" }
|
||||
];
|
||||
|
||||
for (const check of prohibited) {
|
||||
if (check.pattern.test(response)) {
|
||||
this.errors.push({
|
||||
category: 'rules',
|
||||
message: check.msg,
|
||||
suggestion: 'Verify before stating'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Check for required phrases (workflow dependent)
|
||||
const required = this.getRequiredPhrases();
|
||||
for (const phrase of required) {
|
||||
if (!response.includes(phrase)) {
|
||||
this.warnings.push({
|
||||
category: 'rules',
|
||||
message: `Missing required element: "${phrase}"`,
|
||||
suggestion: `Add "${phrase}" to response`
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check 2: Format Compliance
|
||||
*/
|
||||
checkFormatCompliance(response) {
|
||||
const checks = [
|
||||
{ pattern: /^##\s+/m, name: 'Section headers', required: true },
|
||||
{ pattern: /\|.*\|.*\|/m, name: 'Tables (optional)', required: false },
|
||||
{ pattern: /^- \[x\]|^- \[ \]/m, name: 'Checkbox lists (optional)', required: false }
|
||||
];
|
||||
|
||||
for (const check of checks) {
|
||||
if (check.required && !check.pattern.test(response)) {
|
||||
this.errors.push({
|
||||
category: 'format',
|
||||
message: `Missing required format: ${check.name}`,
|
||||
suggestion: `Add ${check.name.toLowerCase()}`
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check 3: Workflow Adherence
|
||||
*/
|
||||
checkWorkflowAdherence(response) {
|
||||
const workflowChecks = {
|
||||
'coding': [
|
||||
{ pattern: /## Verification/, msg: 'Missing verification section' },
|
||||
{ pattern: /## Files Modified/, msg: 'Missing files modified section' }
|
||||
],
|
||||
'debug': [
|
||||
{ pattern: /## Cause|## Root Cause/, msg: 'Missing cause/root cause' },
|
||||
{ pattern: /## Fix|## Solution/, msg: 'Missing fix section' }
|
||||
],
|
||||
'deploy': [
|
||||
{ pattern: /## Pre-Deployment|## Backup/, msg: 'Missing pre-deployment/backup' },
|
||||
{ pattern: /## Verification/, msg: 'Missing verification section' }
|
||||
],
|
||||
'audit': [
|
||||
{ pattern: /## Findings/, msg: 'Missing findings section' },
|
||||
{ pattern: /## Recommendations/, msg: 'Missing recommendations' }
|
||||
]
|
||||
};
|
||||
|
||||
const checks = workflowChecks[this.workflow] || [];
|
||||
for (const check of checks) {
|
||||
if (!check.pattern.test(response)) {
|
||||
this.warnings.push({
|
||||
category: 'workflow',
|
||||
message: check.msg,
|
||||
suggestion: `Follow ${this.workflow} workflow format`
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check 4: Safety Constraints
|
||||
*/
|
||||
checkSafetyConstraints(response) {
|
||||
const safetyChecks = [
|
||||
{
|
||||
pattern: /rm -rf|dd if=|mkfs\.\w+|>\s*\/dev\/\w+/,
|
||||
msg: 'Potentially destructive command',
|
||||
severity: 'error'
|
||||
},
|
||||
{
|
||||
pattern: /ALTER\s+TABLE\s+.*DROP|DELETE\s+FROM\s+\w+\s+WHERE/i,
|
||||
msg: 'Database destructive operation',
|
||||
severity: 'error'
|
||||
},
|
||||
{
|
||||
pattern: /chmod\s+777|chown\s+-R/i,
|
||||
msg: 'Overly permissive permissions',
|
||||
severity: 'warning'
|
||||
}
|
||||
];
|
||||
|
||||
for (const check of safetyChecks) {
|
||||
if (check.pattern.test(response)) {
|
||||
const entry = {
|
||||
category: 'safety',
|
||||
message: check.msg,
|
||||
suggestion: 'Review for safety'
|
||||
};
|
||||
|
||||
if (check.severity === 'error') {
|
||||
this.errors.push(entry);
|
||||
} else {
|
||||
this.warnings.push(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get required phrases for current workflow
|
||||
*/
|
||||
getRequiredPhrases() {
|
||||
const phrases = {
|
||||
'coding': ['Verification'],
|
||||
'debug': ['Cause', 'Fix'],
|
||||
'deploy': ['Backup', 'Verification'],
|
||||
'audit': ['Findings']
|
||||
};
|
||||
return phrases[this.workflow] || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Build validation summary
|
||||
*/
|
||||
buildSummary() {
|
||||
const parts = [];
|
||||
|
||||
if (this.errors.length === 0 && this.warnings.length === 0) {
|
||||
parts.push('✅ All checks passed');
|
||||
} else {
|
||||
if (this.errors.length > 0) {
|
||||
parts.push(`❌ ${this.errors.length} error(s)`);
|
||||
}
|
||||
if (this.warnings.length > 0) {
|
||||
parts.push(`⚠️ ${this.warnings.length} warning(s)`);
|
||||
}
|
||||
}
|
||||
|
||||
return parts.join(', ');
|
||||
}
|
||||
}
|
||||
|
||||
// Export for use
|
||||
module.exports = ResponseValidator;
|
||||
|
||||
// CLI usage
|
||||
if (require.main === module) {
|
||||
const workflow = process.argv[2] || 'coding';
|
||||
const responseFile = process.argv[3];
|
||||
|
||||
if (!responseFile) {
|
||||
console.error('Usage: node validator.js <workflow> <response-file>');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const fs = require('fs');
|
||||
const response = fs.readFileSync(responseFile, 'utf8');
|
||||
|
||||
const validator = new ResponseValidator(workflow);
|
||||
const result = validator.validate(response);
|
||||
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
}
|
||||
Reference in New Issue
Block a user