OpenWriterControle/evals/framework
Darren Hinde 784ffadf92
chore: verify and stabilize main branch (#42)
* chore: update package-lock.json after npm install

* chore: remove unused ajv-cli, keep vitest at stable 1.6.1

- Removed ajv-cli (unused dev dependency, had high severity vulnerability)
- Kept vitest at 1.6.1 (stable, upgrading to v4 requires extensive refactoring)
- Reduced vulnerabilities from 6 to 4 (all moderate, dev-only)
- All remaining vulnerabilities are in esbuild/vite (dev server only, no production impact)
- Build and tests verified working

* feat(evals): add execution-balance validation to 21 OpenAgent tests and enhance agent system

Major improvements to evaluation framework and agent capabilities:

## Execution Balance Evaluator Integration
- Added execution-balance rule to 21 OpenAgent tests (2.7% → 31.5% coverage)
- Validates "read before execute" pattern across critical rules, workflows, and edge cases
- Tests now check: read operations before execution tools, healthy read/exec ratio (≥1.0)
- Coverage includes: approval-gate, context-loading, stop-on-failure, report-first tests

## Agent System Enhancements
- Enhanced OpenCoder agent with critical rules enforcement (approval gates, context loading)
- Added structured delegation rules and workflow phases
- Improved error handling with REPORT→PROPOSE→REQUEST→FIX pattern

## New Agent Creation System
- Added research-backed agent creation templates and guides
- Includes 8 test templates covering planning, context, incremental execution, tools
- Automated agent scaffolding with /create-agent command
- Complete documentation in .opencode/command/openagents/new-agents/

## OpenCoder Test Suite
- Added 8 comprehensive test cases for OpenCoder agent
- Tests cover: planning, context-loading, delegation, error-handling, implementation
- Includes DEBUG_GUIDE.md and QUICK_TEST_GUIDE.md for developers

## Documentation & Tooling
- Added DEVELOPMENT.md guide for contributors
- Enhanced CONTRIBUTING.md with quick reference and common commands
- Improved event-logger with DEBUG_VERBOSE mode for detailed output
- Added debug scripts: show-test-conversation.sh, run-test-verbose.sh
- Removed deprecated EXTERNAL_PR_GUIDE.md

## Version Bump
- Bumped version to 0.0.3 (patch)

Files changed: 60+ files (21 test updates, 16 new agent templates, 8 new tests, docs, tooling)

* feat(ci): add PR title validation for semantic versioning

Add automated PR title validation to ensure conventional commit format.
This enables proper automatic version bumping when PRs are merged.

Features:
- Validates PR titles against conventional commit patterns
- Shows expected version bump (major/minor/patch)
- Posts helpful comment with examples if validation fails
- Supports all conventional commit types (feat, fix, docs, etc.)
- Supports breaking changes (feat!, fix!)
- Supports pre-release tags ([alpha], [beta], [rc])

When PR titles follow the format, version bumping works correctly:
- feat: → minor bump (0.3.0 → 0.4.0)
- fix: → patch bump (0.3.0 → 0.3.1)
- feat!: → major bump (0.3.0 → 1.0.0)

* fix(ci): improve PR checks workflow with sequential execution

Reorganize PR checks to run in logical sequence and skip unnecessary checks:

Changes:
- Run PR title validation first (fast, always required)
- Detect changed files to determine which checks to run
- Only run build checks if evals/ files changed
- Add comprehensive summary job showing all check results
- Prevent cache errors when evals files not changed

Benefits:
- Faster PR checks (skip unnecessary builds)
- Clear sequential execution (title → changes → build)
- Better error messages and summaries
- Reduced CI/CD costs

* fix(ci): resolve PR #42 issues and refactor prompt architecture

## Fixed PR #42 Issues

1. **Prompt Validation** - Refactored to new architecture where agent files are canonical defaults
2. **Package-lock.json** - Fixed workflow to use root package-lock (npm workspaces)
3. **Git Merge Base** - Added fetch-depth: 0 to PR checks workflow

## New Prompt Architecture

**Before:**
- Agent files had to match .opencode/prompts/<agent>/default.md
- Caused validation failures when updating prompts
- Redundant duplication

**After:**
- Agent files (.opencode/agent/*.md) = Canonical defaults (source of truth)
- Prompt variants (.opencode/prompts/<agent>/<model>.md) = Model-specific optimizations
- No more default.md files needed

## Changes

### Workflows
- pr-checks.yml: Added fetch-depth: 0 for full git history
- validate-test-suites.yml: Fixed package-lock path for npm workspaces
- validate-registry.yml: Updated validation messaging

### Scripts
- validate-pr.sh: Validates prompt library structure (rejects default.md files)
- use-prompt.sh: Handles 'default' as agent file, variants as model-specific
- test-prompt.sh: Tests default variant correctly, saves results to prompts/results/

### Documentation
- .opencode/prompts/README.md: Updated architecture explanation
- docs/contributing/CONTRIBUTING.md: Updated prompt workflow
- scripts/development/demo.sh: Updated structure display

## Results

- 12 files changed, 231 insertions(+), 811 deletions(-)
- Net reduction: 580 lines (cleaner codebase)
- All validations passing
- Results directory structure preserved
- Backwards compatible with existing test results
2025-12-09 13:26:52 +00:00
..
docs feat(evals): add opencoder test suite and fix expected violation handling 2025-11-26 01:52:18 +00:00
scripts chore: verify and stabilize main branch (#42) 2025-12-09 13:26:52 +00:00
src chore: verify and stabilize main branch (#42) 2025-12-09 13:26:52 +00:00
.eval-config.example.json feat: implement SDK-based evaluation framework with real agent testing 2025-11-22 14:30:11 +00:00
.gitignore Add evaluation framework structure and OpenCode logging documentation 2025-11-21 14:50:44 +00:00
demo-enhanced-features.sh Add Production-Ready Eval Framework for OpenAgent (#25) 2025-11-28 11:48:13 +00:00
package.json chore: verify and stabilize main branch (#42) 2025-12-09 13:26:52 +00:00
README.md feat: ExecutionBalanceEvaluator and docs (#27) 2025-12-08 23:09:49 +00:00
SDK_EVAL_README.md feat: implement SDK-based evaluation framework with real agent testing 2025-11-22 14:30:11 +00:00
tsconfig.json feat: implement SDK-based evaluation framework with real agent testing 2025-11-22 14:30:11 +00:00

Evaluation Framework

Overview

Reusable framework for evaluating OpenCode agent behavior. Can be used to test any agent against defined standards.

Architecture

framework/
├── src/
│   ├── collector/          # Session data collection
│   │   ├── session-reader.ts
│   │   ├── message-parser.ts
│   │   └── timeline-builder.ts
│   ├── evaluators/         # Evaluation logic
│   │   ├── base-evaluator.ts
│   │   ├── approval-gate.ts
│   │   ├── context-loading.ts
│   │   ├── delegation.ts
│   │   ├── tool-usage.ts
│   │   └── model-selection.ts
│   ├── runner/            # Test execution
│   │   ├── test-runner.ts
│   │   └── session-analyzer.ts
│   ├── reporters/         # Result reporting
│   │   ├── console-reporter.ts
│   │   ├── json-reporter.ts
│   │   └── markdown-reporter.ts
│   ├── types/             # TypeScript types
│   │   └── index.ts
│   ├── config.ts          # Configuration
│   └── index.ts           # Main exports
├── tests/                 # Framework tests
├── package.json
├── tsconfig.json
└── README.md

Installation

npm install

Usage

Basic Example

import { SessionReader, ApprovalGateEvaluator, TimelineBuilder } from '@evals/framework';

// Read session
const reader = new SessionReader('/path/to/project');
const sessionInfo = reader.getSessionInfo('ses_xxxxx');
const messages = reader.getMessages('ses_xxxxx');

// Build timeline
const builder = new TimelineBuilder(reader);
const timeline = builder.buildTimeline('ses_xxxxx');

// Evaluate
const evaluator = new ApprovalGateEvaluator();
const result = evaluator.evaluate(timeline);

console.log(`Passed: ${result.passed}`);
console.log(`Score: ${result.score}/100`);
console.log(`Violations: ${result.violations.length}`);

Running Test Suite

import { TestRunner } from '@evals/framework';

const runner = new TestRunner({
  projectPath: process.cwd(),
  evaluatorsPath: './src/evaluators',
  resultsPath: '../results'
});

// Load test cases
const testCases = runner.loadTestCases('../opencode/openagent/test-cases/approval-gates.yaml');

// Run tests
const suite = runner.runAll(testCases);

console.log(`Pass Rate: ${suite.summary.passRate}%`);

Components

Collector

SessionReader - Read OpenCode session files

const reader = new SessionReader(projectPath);
const info = reader.getSessionInfo(sessionId);
const messages = reader.getMessages(sessionId);
const parts = reader.getParts(sessionId, messageId);

MessageParser - Parse message structure

const parser = new MessageParser();
const agent = parser.getAgent(message);
const model = parser.getModel(message);
const metrics = parser.getMetrics(message);

TimelineBuilder - Build event timeline

const builder = new TimelineBuilder(reader);
const timeline = builder.buildTimeline(sessionId);
const toolCalls = builder.filterByType(timeline, 'tool_call');

Evaluators

All evaluators extend BaseEvaluator and implement:

evaluate(timeline: TimelineEvent[]): EvaluationResult

ApprovalGateEvaluator - Check approval before execution

const evaluator = new ApprovalGateEvaluator();
const result = evaluator.evaluate(timeline);

ContextLoadingEvaluator - Verify context loading

const evaluator = new ContextLoadingEvaluator();
const result = evaluator.evaluate(timeline);

DelegationEvaluator - Validate delegation decisions

const evaluator = new DelegationEvaluator();
const result = evaluator.evaluate(timeline);

ToolUsageEvaluator - Check tool selection

const evaluator = new ToolUsageEvaluator();
const result = evaluator.evaluate(timeline);

ExecutionBalanceEvaluator - Assess balance and ordering of read vs execution actions

import { ExecutionBalanceEvaluator } from '@evals/framework';
const evaluator = new ExecutionBalanceEvaluator();
const result = await evaluator.evaluate(timeline);
console.log(result.meta?.ratio); // read/exec ratio

Violations it may produce:

  • execution-before-read (error): first execution tool used before any read tools.
  • insufficient-read (warning): fewer reads than executions overall.

Runner

TestRunner - Execute test suites

const runner = new TestRunner(config);
const testCases = runner.loadTestCases(path);
const suite = runner.runAll(testCases);

SessionAnalyzer - Analyze historical sessions

const analyzer = new SessionAnalyzer(reader);
const result = analyzer.analyze(sessionId, testCase);

Reporters

ConsoleReporter - Pretty console output

const reporter = new ConsoleReporter();
reporter.report(testSuite);

JSONReporter - Machine-readable JSON

const reporter = new JSONReporter();
reporter.export(testSuite, 'results.json');

MarkdownReporter - Documentation format

const reporter = new MarkdownReporter();
reporter.generate(testSuite, 'report.md');

Types

Core Types

interface SessionInfo {
  id: string;
  version: string;
  title: string;
  time: { created: number; updated: number };
}

interface Message {
  id: string;
  role: 'user' | 'assistant';
  sessionID: string;
  mode?: string;
  modelID?: string;
  providerID?: string;
  tokens?: TokenUsage;
  cost?: number;
  time: { created: number; completed?: number };
}

interface TimelineEvent {
  timestamp: number;
  type: 'user_message' | 'assistant_message' | 'tool_call' | 'patch';
  agent?: string;
  model?: string;
  data: any;
}

interface EvaluationResult {
  evaluator: string;
  passed: boolean;
  score: number;
  violations: Violation[];
  evidence: Evidence[];
}

interface TestResult {
  testCaseId: string;
  sessionId: string;
  passed: boolean;
  score: number;
  evaluationResults: EvaluationResult[];
  metadata: any;
}

Configuration

// config.ts
export const config = {
  projectPath: process.cwd(),
  sessionStoragePath: '~/.local/share/opencode/',
  resultsPath: '../results/',
  passThreshold: 75,
  evaluators: {
    'approval-gate': ApprovalGateEvaluator,
    'context-loading': ContextLoadingEvaluator,
    'delegation': DelegationEvaluator,
    'tool-usage': ToolUsageEvaluator,
  }
};

Development

Setup

npm install
npm run build

Testing

# Run all tests
npm test

# Run specific test
npm test -- session-reader

# Watch mode
npm run test:watch

Building

# Build TypeScript
npm run build

# Watch mode
npm run build:watch

Linting

npm run lint
npm run lint:fix

Adding New Evaluators

  1. Create file in src/evaluators/
  2. Extend BaseEvaluator
  3. Implement evaluate() method
  4. Add tests
  5. Register in config
  6. Export from index.ts

For a detailed step-by-step contributor guide see: docs/contributing/ADDING_EVALUATOR.md

Example:

// src/evaluators/my-evaluator.ts
import { BaseEvaluator } from './base-evaluator';
import { EvaluationResult, TimelineEvent } from '../types';

export class MyEvaluator extends BaseEvaluator {
  evaluate(timeline: TimelineEvent[]): EvaluationResult {
    // Your evaluation logic
    const checks = [
      { name: 'check1', passed: true, weight: 50 },
      { name: 'check2', passed: false, weight: 50 }
    ];
    
    const score = this.calculateScore(checks);
    const violations = this.findViolations(timeline);
    
    return {
      evaluator: 'my-evaluator',
      passed: score >= 75,
      score,
      violations,
      evidence: []
    };
  }
}

API Reference

See API.md for complete API documentation.

Contributing

  1. Fork the repository
  2. Create feature branch
  3. Add tests
  4. Submit pull request

License

MIT


Scripts

Development and debugging scripts are organized in the scripts/ directory:

scripts/
├── debug/          # Session and event debugging
├── test/           # Framework component tests
├── utils/          # Utility scripts (batch runner, etc.)
└── README.md       # Script documentation

See scripts/README.md for detailed usage.

Quick Examples

# Run tests in batches
./scripts/utils/run-tests-batch.sh openagent 3 10

# Debug a session
node scripts/debug/inspect-session.mjs

# Test framework component
npx tsx scripts/test/test-timeline.ts