11---
22date : 2026-03-30
3- author : Onur Solmaz <onur@textcortex.com>
3+ author : Onur Solmaz
44title : Agent Profile API
55tags : [spritz, agents, ui, api, architecture]
66---
77
88## Overview
99
10- This document defines a provider-agnostic agent profile API for rendering a
10+ This document defines the provider-agnostic agent profile API for rendering a
1111Spritz instance with deployment-owned cosmetic metadata such as:
1212
1313- name
@@ -120,7 +120,7 @@ knowledge indirectly in annotations or ACP metadata.
120120
121121## Canonical resource model
122122
123- The recommended model is:
123+ The implemented model is:
124124
125125``` yaml
126126spec :
@@ -158,9 +158,9 @@ Recommended types:
158158 - canonical UI output
159159 - what every UI should read
160160
161- # # Proposed type definitions
161+ # # Type Definitions
162162
163- Suggested CRD additions :
163+ Implemented CRD types :
164164
165165` ` ` go
166166type SpritzAgentRef struct {
@@ -185,7 +185,7 @@ type SpritzAgentProfileStatus struct {
185185}
186186```
187187
188- Suggested placements:
188+ Implemented placements:
189189
190190- ` spritz.spec.agentRef `
191191- ` spritz.spec.profileOverrides `
@@ -215,11 +215,11 @@ This is also cleaner than using only `metadata.annotations` because:
215215
216216## Sync model
217217
218- Spritz should add one extension operation for agent profile sync:
218+ Spritz supports one extension operation for agent profile sync:
219219
220220- ` agent.profile.sync `
221221
222- Its input should contain only the facts needed to compute the profile:
222+ Its input contains only the facts needed to compute the profile:
223223
224224``` json
225225{
@@ -245,7 +245,7 @@ Its input should contain only the facts needed to compute the profile:
245245}
246246```
247247
248- The response should be narrow:
248+ The response is narrow:
249249
250250``` json
251251{
@@ -259,7 +259,7 @@ The response should be narrow:
259259}
260260```
261261
262- The extension should return profile data only. It should not mutate arbitrary
262+ The extension returns profile data only. It does not mutate arbitrary
263263resource state.
264264
265265## Precedence rules
@@ -278,10 +278,10 @@ For the image URL, canonical precedence should be:
2782782 . synced extension output from ` agent.profile.sync `
2792793 . no image URL
280280
281- This precedence should be materialized into ` status.profile ` so the UI does not
281+ This precedence is materialized into ` status.profile ` so the UI does not
282282need to re-implement the logic in multiple places.
283283
284- That means the browser should normally read :
284+ The browser normally reads :
285285
286286- ` status.profile.name `
287287- ` status.profile.imageUrl `
@@ -291,7 +291,7 @@ generic placeholder.
291291
292292## Conversation model
293293
294- The canonical source of per-instance profile data should stay on the instance
294+ The canonical source of per-instance profile data stays on the instance
295295resource, not on ` SpritzConversation ` .
296296
297297Conversation resources already reference the parent instance by ` spritzName ` .
@@ -302,29 +302,26 @@ If later profiling shows that repeated joins are too expensive, Spritz can add
302302an optional derived snapshot to conversation state. That snapshot should still
303303be treated as a cache of instance profile data, not the source of truth.
304304
305- ## API and controller changes
305+ ## Implemented API and Controller Behavior
306306
307- ### API changes
307+ ### API behavior
308308
309- - extend ` operator/api/v1/spritz_types.go ` with :
309+ - ` operator/api/v1/spritz_types.go ` includes :
310310 - ` SpritzAgentRef `
311311 - ` SpritzAgentProfile `
312312 - ` SpritzAgentProfileStatus `
313- - update public API serialization so ` profile ` is included in
314- instance reads and lists
315- - keep ` status.acp.agentInfo ` unchanged
313+ - public API serialization includes profile data in instance reads and lists
314+ - ` status.acp.agentInfo ` remains unchanged and runtime-owned
316315
317- ### Extension framework changes
316+ ### Extension framework behavior
318317
319- - add ` agent.profile.sync ` as a supported operation in the extension registry
320- - define a typed request and response envelope for profile sync
321- - validate that the extension can only return profile fields
318+ - ` agent.profile.sync ` is a supported operation in the extension registry
319+ - profile sync uses a typed request and response envelope
320+ - profile sync output is limited to profile fields
322321
323- ### Reconciliation changes
322+ ### Create-path behavior
324323
325- Spritz needs a control-plane component that computes ` status.profile ` .
326-
327- Recommended sequence:
324+ The current implementation computes ` status.profile ` during create flows:
328325
3293261 . normalize ` spec.agentRef ` and ` spec.profileOverrides `
3303272 . if overrides fully satisfy the profile, use them directly
@@ -338,8 +335,11 @@ Recommended sequence:
338335 - ` lastSyncedAt `
339336 - ` lastError `
340337
341- The first implementation can run this logic in the API create/update path plus
342- an explicit refresh endpoint if needed.
338+ Bindings preserve ` agentRef ` and ` profileOverrides ` in their rendered template
339+ spec, but binding reconciliation does not currently compute a separate
340+ ` status.profile ` for the binding itself.
341+
342+ ## Follow-up Work
343343
344344The long-term preferred implementation is a reconciliation loop that keeps
345345` status.profile ` current whenever:
@@ -348,58 +348,35 @@ The long-term preferred implementation is a reconciliation loop that keeps
348348- ` spec.profileOverrides ` changes
349349- a caller requests refresh
350350
351- ## Suggested implementation phases
352-
353- ### Phase 1: typed model and UI read path
354-
355- - add typed ` agentRef ` , ` profileOverrides ` , and ` profile `
356- - add UI helpers that prefer ` status.profile `
357- - keep ACP metadata as fallback only
358-
359- This phase creates the durable contract first.
360-
361- ### Phase 2: extension integration
362-
363- - add ` agent.profile.sync `
364- - sync profile data during create and update
365- - materialize the merged result into ` status.profile `
366-
367- This phase gives deployments a provider-agnostic hook.
368-
369- ### Phase 3: refresh and reconciliation
370-
371- - add explicit refresh semantics
372- - reconcile stale or missing profile data after create
373- - support background re-sync without rewriting ` spec `
374-
375- This phase makes external profile data durable over time instead of treating it
376- as a one-time create artifact.
351+ The remaining work is to add explicit refresh semantics and background
352+ re-sync. Until then, external profile data is primarily a create-time sync
353+ result.
377354
378355## Validation
379356
380- Required validation:
357+ Implemented validation includes :
381358
382- - unit tests for precedence logic
383- - unit tests for merge behavior between overrides, synced profile data, ACP
384- metadata, and instance name
385- - API tests for instance list and get responses
359+ - unit tests for profile status merge behavior
360+ - API tests for create-time profile sync
361+ - ACP list tests that show ` status.profile ` drives UI-facing profile output
386362- extension tests for:
387363 - synced
388364 - missing
389365 - forbidden
390366 - invalid
391- - reconciliation tests proving ` status.profile ` updates when
392- ` spec.profileOverrides ` changes
393367- UI tests proving:
394368 - ` status.profile ` is preferred
395369 - ACP metadata remains a fallback
396370 - instance name remains the final fallback
397371
372+ Refresh/reconciliation tests are still follow-up work because background
373+ profile reconciliation is not implemented yet.
374+
398375## Migration notes
399376
400377Existing installations may already render from ACP metadata or instance name.
401378
402- Migration should therefore be additive:
379+ Migration is additive:
403380
4043811 . introduce the new fields
4053822 . ship UIs that prefer ` status.profile `
0 commit comments