feat(brain): expose supersession metadata

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-02 07:53:58 +00:00
parent 92703d5af5
commit 425008f855
2 changed files with 119 additions and 2 deletions

View file

@ -1,5 +1,7 @@
<?php
// SPDX-License-Identifier: EUPL-1.2
declare(strict_types=1);
namespace Core\Mod\Agentic\Models;
@ -165,7 +167,7 @@ class BrainMemory extends Model
$maxDepth = 50;
while ($current->supersedes_id !== null && $depth < $maxDepth) {
$current = $current->supersedes;
$current = self::withTrashed()->find($current->supersedes_id);
if ($current === null) {
break;
@ -185,13 +187,15 @@ class BrainMemory extends Model
'agent_id' => $this->agent_id,
'type' => $this->type,
'content' => $this->content,
'tags' => $this->tags,
'tags' => $this->tags ?? [],
'project' => $this->project,
'confidence' => $this->confidence,
'score' => round($score, 4),
'source' => $this->source ?? 'manual',
'supersedes_id' => $this->supersedes_id,
'supersedes_count' => $this->getSupersessionDepth(),
'expires_at' => $this->expires_at?->toIso8601String(),
'deleted_at' => $this->deleted_at?->toIso8601String(),
'created_at' => $this->created_at?->toIso8601String(),
'updated_at' => $this->updated_at?->toIso8601String(),
];

View file

@ -0,0 +1,113 @@
<?php
// SPDX-License-Identifier: EUPL-1.2
declare(strict_types=1);
namespace Core\Mod\Agentic\Tests\Feature;
use Core\Mod\Agentic\Models\BrainMemory;
use Core\Tenant\Models\Workspace;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Str;
use Tests\TestCase;
class BrainMemoryTest extends TestCase
{
use RefreshDatabase;
private Workspace $workspace;
protected function setUp(): void
{
parent::setUp();
$this->workspace = Workspace::factory()->create();
}
public function test_BrainMemory_toMcpContext_Good_IncludesSupersessionDepthAndEmptyTags(): void
{
$root = BrainMemory::create([
'workspace_id' => $this->workspace->id,
'agent_id' => 'virgil',
'type' => 'decision',
'content' => 'Prefer named actions for all agent capabilities.',
'confidence' => 0.9,
]);
$child = BrainMemory::create([
'workspace_id' => $this->workspace->id,
'agent_id' => 'virgil',
'type' => 'decision',
'content' => 'Keep the action registry namespaced.',
'tags' => ['actions', 'registry'],
'confidence' => 0.95,
'supersedes_id' => $root->id,
]);
$context = $child->toMcpContext();
$this->assertSame(1, $context['supersedes_count']);
$this->assertSame(['actions', 'registry'], $context['tags']);
$this->assertNull($context['deleted_at']);
}
public function test_BrainMemory_toMcpContext_Bad_HandlesMissingSupersededMemory(): void
{
$memory = new BrainMemory([
'workspace_id' => $this->workspace->id,
'agent_id' => 'virgil',
'type' => 'research',
'content' => 'Missing ancestors should not break MCP output.',
'confidence' => 0.8,
]);
$memory->supersedes_id = Str::uuid()->toString();
$context = $memory->toMcpContext();
$this->assertSame(0, $context['supersedes_count']);
$this->assertSame([], $context['tags']);
}
public function test_BrainMemory_toMcpContext_Ugly_WalksSoftDeletedAncestors(): void
{
$root = BrainMemory::create([
'workspace_id' => $this->workspace->id,
'agent_id' => 'virgil',
'type' => 'decision',
'content' => 'Start with the root memory.',
'confidence' => 0.9,
]);
$middle = BrainMemory::create([
'workspace_id' => $this->workspace->id,
'agent_id' => 'virgil',
'type' => 'decision',
'content' => 'Replace the root memory.',
'confidence' => 0.9,
'supersedes_id' => $root->id,
]);
$middle->delete();
$leaf = BrainMemory::create([
'workspace_id' => $this->workspace->id,
'agent_id' => 'virgil',
'type' => 'decision',
'content' => 'Replace the deleted middle memory.',
'confidence' => 0.9,
'supersedes_id' => $middle->id,
]);
$context = $leaf->toMcpContext();
$this->assertSame(2, $context['supersedes_count']);
$this->assertNull($context['deleted_at']);
$deletedContext = BrainMemory::withTrashed()->findOrFail($middle->id)->toMcpContext();
$this->assertNotNull($deletedContext['deleted_at']);
$this->assertSame(1, $deletedContext['supersedes_count']);
}
}