Skip to content

Commit 3fc8a86

Browse files
committed
feat: add agent name annotation to AI agent config materials
Extract the agent name from CHAINLOOP_AI_AGENT_CONFIG material JSON payload and surface it as a `chainloop.material.aiagent.name` annotation, making it queryable without downloading the full evidence content. Signed-off-by: Jose I. Paris <jiparis@chainloop.dev>
1 parent 2c2e9ec commit 3fc8a86

4 files changed

Lines changed: 88 additions & 0 deletions

File tree

pkg/attestation/crafter/materials/chainloop_ai_agent_config.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import (
2929
"github.com/rs/zerolog"
3030
)
3131

32+
var annotationAIAgentName = api.CreateAnnotation("material.aiagent.name")
33+
3234
type ChainloopAIAgentConfigCrafter struct {
3335
*crafterCommon
3436
backend *casclient.CASBackend
@@ -69,5 +71,15 @@ func (c *ChainloopAIAgentConfigCrafter) Craft(ctx context.Context, artifactPath
6971
return nil, err
7072
}
7173

74+
// Extract agent name from the validated JSON and surface it as an annotation
75+
var envelope struct {
76+
Agent struct {
77+
Name string `json:"name"`
78+
} `json:"agent"`
79+
}
80+
if err := json.Unmarshal(f, &envelope); err == nil && envelope.Agent.Name != "" {
81+
material.Annotations[annotationAIAgentName] = envelope.Agent.Name
82+
}
83+
7284
return material, nil
7385
}

pkg/attestation/crafter/materials/chainloop_ai_agent_config_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import (
2525
schemaapi "github.com/chainloop-dev/chainloop/app/controlplane/api/workflowcontract/v1"
2626
"github.com/chainloop-dev/chainloop/internal/aiagentconfig"
2727
"github.com/chainloop-dev/chainloop/internal/schemavalidators"
28+
"github.com/chainloop-dev/chainloop/pkg/casclient"
29+
mUploader "github.com/chainloop-dev/chainloop/pkg/casclient/mocks"
2830
"github.com/rs/zerolog"
2931
"github.com/stretchr/testify/assert"
3032
"github.com/stretchr/testify/require"
@@ -260,3 +262,49 @@ func TestChainloopAIAgentConfigCrafter_RejectsExtraFields(t *testing.T) {
260262
require.Error(t, err)
261263
assert.Contains(t, err.Error(), "AI agent config validation failed")
262264
}
265+
266+
func TestChainloopAIAgentConfigCrafter_AgentNameAnnotation(t *testing.T) {
267+
testCases := []struct {
268+
name string
269+
filePath string
270+
expectedAgentName string
271+
}{
272+
{
273+
name: "claude agent",
274+
filePath: "./testdata/ai-agent-config-claude.json",
275+
expectedAgentName: "claude",
276+
},
277+
{
278+
name: "cursor agent",
279+
filePath: "./testdata/ai-agent-config-cursor.json",
280+
expectedAgentName: "cursor",
281+
},
282+
}
283+
284+
for _, tc := range testCases {
285+
t.Run(tc.name, func(t *testing.T) {
286+
logger := zerolog.Nop()
287+
schema := &schemaapi.CraftingSchema_Material{
288+
Name: "test",
289+
Type: schemaapi.CraftingSchema_Material_CHAINLOOP_AI_AGENT_CONFIG,
290+
}
291+
292+
uploader := mUploader.NewUploader(t)
293+
uploader.On("UploadFile", context.TODO(), tc.filePath).
294+
Return(&casclient.UpDownStatus{
295+
Digest: "deadbeef",
296+
Filename: tc.filePath,
297+
}, nil)
298+
299+
backend := &casclient.CASBackend{Uploader: uploader}
300+
301+
crafter, err := NewChainloopAIAgentConfigCrafter(schema, backend, &logger)
302+
require.NoError(t, err)
303+
304+
got, err := crafter.Craft(context.TODO(), tc.filePath)
305+
require.NoError(t, err)
306+
307+
assert.Equal(t, tc.expectedAgentName, got.Annotations[annotationAIAgentName])
308+
})
309+
}
310+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"schema_version": "0.1",
3+
"agent": {"name": "claude"},
4+
"config_hash": "abc123",
5+
"captured_at": "2026-03-13T10:00:00Z",
6+
"config_files": [
7+
{
8+
"path": "CLAUDE.md",
9+
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
10+
"size": 42,
11+
"content": "IyBQcm9qZWN0IFJ1bGVz"
12+
}
13+
]
14+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"schema_version": "0.1",
3+
"agent": {"name": "cursor"},
4+
"config_hash": "def456",
5+
"captured_at": "2026-03-13T10:00:00Z",
6+
"config_files": [
7+
{
8+
"path": ".cursor/rules/coding.md",
9+
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
10+
"size": 20,
11+
"content": "IyBDb2RpbmcgUnVsZXM="
12+
}
13+
]
14+
}

0 commit comments

Comments
 (0)