Shared contracts and value objects for the Conduit tool ecosystem.
Conduit Contracts provides a foundation for building composable, role-based tools with guaranteed quality through shipped tests and custom Pest expectations.
Every tool in the Conduit ecosystem ships with 100% test coverage. When you compose tools together, you only need to test your composition logic - the tools themselves are pre-validated.
Example leverage:
- 10 tools × 19 tests each = 190 tests normally required
- With Conduit: Only 20 tests needed (89% reduction)
- 8.5x testing leverage
composer require conduit-ui/contractsEvery tool performs exactly ONE role:
use ConduitUI\ValueObjects\ToolRole;
ToolRole::MUSIC; // Spotify, Apple Music
ToolRole::INTAKE; // GitHub Issues, Linear
ToolRole::ATTEMPT; // Git commits, code fixes
ToolRole::SUBMISSION; // Pull requests
ToolRole::VALIDATION; // Tests, linters
ToolRole::DELIVERY; // CI/CD, deployment
ToolRole::MEMORY; // Vector databases
ToolRole::ANALYSIS; // LLMs, AI servicesAll tools implement these interfaces:
Tool- Base contract (execute, isAvailable, role, getProviderName)Observable- Event emission (emitStarted, emitCompleted, emitFailed)KnowledgeAware- Knowledge integration (recordToKnowledge, loadContext, queryKnowledge)
Role-specific contracts extend the base:
MusicProvider- suggestPlaylist(), playSong(), detectEmotionalState()IntakeManager- receiveWorkRequest(), classifyRequest(), routeToAgent()AttemptExecutor- attemptSolution(), validateAttempt(), retryOnFailure()SubmissionHandler- proposeSolution(), requestReview(), handleFeedback()ValidationRunner- runValidation(), getViolations()DeliveryHandler- deploy(), rollback(), healthCheck()MemoryProvider- store(), retrieve(), embed(), search()AnalysisProvider- analyze(), reason(), compare()
use ConduitUI\Contracts\Tool;
use ConduitUI\Contracts\Roles\MusicProvider;
use ConduitUI\ValueObjects\{ToolRole, Context, Result, EmotionalState};
class SpotifyTool implements Tool, MusicProvider
{
public function role(): ToolRole
{
return ToolRole::MUSIC;
}
public function getProviderName(): string
{
return 'Spotify';
}
public function isAvailable(): bool
{
return ! empty(config('services.spotify.token'));
}
public function execute(Context $context): Result
{
$this->emitStarted($context);
try {
// Implementation
$result = Result::success('Playlist started');
$this->emitCompleted($result);
return $result;
} catch (\Throwable $e) {
$this->emitFailed($e);
return Result::failure('Failed to start playlist', $e);
}
}
// MusicProvider methods
public function suggestPlaylist(EmotionalState $state): string { /* ... */ }
public function playSong(string $songId): Result { /* ... */ }
public function detectEmotionalState(): EmotionalState { /* ... */ }
// Observable methods
public function emitStarted(Context $context): void { /* ... */ }
public function emitCompleted(Result $result): void { /* ... */ }
public function emitFailed(\Throwable $exception): void { /* ... */ }
// KnowledgeAware methods
public function recordToKnowledge(Result $result): void { /* ... */ }
public function loadContext(string $collection): Context { /* ... */ }
public function queryKnowledge(array $criteria): array { /* ... */ }
}use ConduitUI\Tests\Contracts\Roles\MusicProviderContractTest;
use ConduitUI\ValueObjects\{ToolRole, Context};
class SpotifyToolTest extends MusicProviderContractTest
{
protected function createTool(): Tool
{
return new SpotifyTool();
}
protected function createValidContext(): Context
{
return new MusicContext(
mood: 'focused',
energy: 0.8
);
}
protected function expectedRole(): ToolRole
{
return ToolRole::MUSIC;
}
protected function getValidPlaylistId(): string
{
return 'test-playlist-id';
}
protected function getValidSongId(): string
{
return 'test-song-id';
}
}The abstract test automatically validates:
- ✅ Implements all required interfaces
- ✅ Returns correct role
- ✅ Executes and returns Result
- ✅ Emits events properly
- ✅ Records to knowledge
- ✅ All role-specific methods work
use function ConduitUI\Tests\Pest\Expectations\expect;
// Tool validation
expect($tool)->toBeAValidConduitTool();
expect($tool)->toHaveRole(ToolRole::MUSIC);
expect($tool)->toBeAvailable();
// Execution
expect($tool)->toExecuteSuccessfully($context);
expect($tool)->toFailExecution($invalidContext);
// Events
expect($tool)->toEmitStartedEvent($context);
expect($tool)->toEmitCompletedEvent($result);
// Results
expect($result)->toBeASuccessfulResult();
expect($result)->toBeAFailedResult();
expect($result)->toHaveResultMessage('Operation completed');
// Knowledge
expect($tool)->toRecordToKnowledge($result);
expect($tool)->toLoadContextFromKnowledge('collection');use ConduitUI\ValueObjects\Result;
// Success
$result = Result::success(
message: 'Deployment completed',
data: ['deployment_id' => 123],
metadata: ['duration' => 45.2]
);
// Failure
$result = Result::failure(
message: 'Database connection failed',
error: $exception,
metadata: ['retry_count' => 3]
);
// Check outcome
if ($result->isSuccess()) {
// Handle success
}use ConduitUI\ValueObjects\Context;
use ConduitUI\ValueObjects\ToolRole;
class MusicContext extends Context
{
public function __construct(
public string $mood,
public float $energy,
public array $preferences = []
) {}
public function role(): ToolRole
{
return ToolRole::MUSIC;
}
public function toArray(): array
{
return [
'mood' => $this->mood,
'energy' => $this->energy,
'preferences' => $this->preferences,
];
}
}composer test
composer test-coverageMIT License. See LICENSE for details.
Contributions welcome! Please ensure:
- 100% test coverage maintained
- All Pest expectations pass
- Code follows Laravel Pint standards
- Documentation updated for new contracts
- conduit-ui/issue - GitHub issue management
- conduit-ui/pr - Pull request operations
- conduit-ui/commit - Git commit operations
- conduit-ui/spotify - Spotify integration