mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-09-04 18:20:04 +00:00
docs(code): add inline comments at agent identity trap zones
Clarify the agent_key vs UUID contract inline at the four highest-ROI trap zones: the Loop struct identity fields, DomainEvent identity fields, the parseUUID/parseUUIDOrNil store helpers, and the WS method resolver helpers. Each comment explains the invariant and links to docs/agent-identity-conventions.md so future readers can find the full rules without archaeology. Comments only — no runtime behavior change.
This commit is contained in:
@@ -68,10 +68,17 @@ type CacheInvalidateFunc func(agentID uuid.UUID, userID string)
|
||||
// Loop is the agent execution loop for one agent instance.
|
||||
// Think → Act → Observe cycle with tool execution.
|
||||
type Loop struct {
|
||||
id string
|
||||
displayName string
|
||||
agentUUID uuid.UUID // set for context propagation
|
||||
tenantID uuid.UUID // agent's owning tenant
|
||||
// id is the human-readable agent_key (e.g. "goctech-leader"). Use for logs,
|
||||
// UI events, system prompt rendering, filesystem paths, and context keys.
|
||||
// NEVER set on DB FK columns or DomainEvent.AgentID — those require UUID.
|
||||
// See docs/agent-identity-conventions.md.
|
||||
id string
|
||||
displayName string
|
||||
// agentUUID is the canonical DB primary key. Use for SQL WHERE/JOIN,
|
||||
// DomainEvent.AgentID, OTel span attributes, and context propagation via
|
||||
// store.WithAgentID. See docs/agent-identity-conventions.md.
|
||||
agentUUID uuid.UUID
|
||||
tenantID uuid.UUID // agent's owning tenant
|
||||
agentType string // "open" or "predefined"
|
||||
defaultTimezone string // system default timezone for bootstrap pre-fill
|
||||
provider providers.Provider
|
||||
|
||||
@@ -27,15 +27,21 @@ const (
|
||||
)
|
||||
|
||||
// DomainEvent is a typed event with metadata for the consolidation pipeline.
|
||||
//
|
||||
// Identity invariant: TenantID and AgentID are string fields for legacy wire
|
||||
// compatibility, but consumers parse them as UUIDs before touching the DB.
|
||||
// Publishers MUST supply valid UUID strings — never agent_key or tenant_slug.
|
||||
// The publish-time observer in validate_agent_id.go warns on drift.
|
||||
// See docs/agent-identity-conventions.md.
|
||||
type DomainEvent struct {
|
||||
ID string // UUID v7 for ordering
|
||||
ID string // UUID v7 for ordering
|
||||
Type EventType
|
||||
SourceID string // dedup key (e.g. session key, run ID)
|
||||
TenantID string
|
||||
AgentID string
|
||||
SourceID string // dedup key (e.g. session key, run ID)
|
||||
TenantID string // MUST be a valid UUID string — never tenant_slug
|
||||
AgentID string // MUST be a valid UUID string — never agent_key
|
||||
UserID string
|
||||
Timestamp time.Time
|
||||
Payload any // typed per EventType (see payload structs below)
|
||||
Payload any // typed per EventType (see payload structs below)
|
||||
}
|
||||
|
||||
// --- Typed payloads, one per EventType ---
|
||||
|
||||
@@ -408,10 +408,10 @@ func (m *AgentLinksMethods) invalidateLinkAgentsByID(ctx context.Context, source
|
||||
|
||||
// --- helpers ---
|
||||
|
||||
// resolveAgentUUID resolves an agent identifier (either UUID or agent_key) to its
|
||||
// canonical UUID via a DB lookup. Tenant-aware via store.TenantIDFromContext(ctx)
|
||||
// inside agentStore.GetByID/GetByKey. Prefer resolveAgentUUIDCached for hot-path
|
||||
// handlers to avoid an unnecessary DB roundtrip.
|
||||
// resolveAgentUUID resolves an agent identifier (either UUID or agent_key) to
|
||||
// its canonical UUID via a DB lookup. Tenant-aware via
|
||||
// store.TenantIDFromContext(ctx) inside agentStore.GetByID/GetByKey. Prefer
|
||||
// resolveAgentUUIDCached in hot-path handlers to avoid the extra DB roundtrip.
|
||||
// See docs/agent-identity-conventions.md trap zone 5.
|
||||
func resolveAgentUUID(ctx context.Context, agentStore store.AgentStore, keyOrID string) (uuid.UUID, error) {
|
||||
if id, err := uuid.Parse(keyOrID); err == nil {
|
||||
@@ -436,11 +436,12 @@ type agentUUIDProvider interface {
|
||||
}
|
||||
|
||||
// resolveAgentUUIDCached is the cache-aware variant of resolveAgentUUID.
|
||||
// Checks the router cache first when the input is an agent_key and the Loop is
|
||||
// cached — avoids a DB roundtrip on the hot path. Falls back to DB lookup on
|
||||
// cache miss or when the input is a UUID string (not cached under the raw UUID
|
||||
// key post-Phase-2 canonicalization). If router is nil, delegates straight to
|
||||
// resolveAgentUUID. See docs/agent-identity-conventions.md trap zone 5.
|
||||
// Checks the router cache first when the input is an agent_key and the Loop
|
||||
// is cached — avoids a DB roundtrip on the hot path. Falls back to DB lookup
|
||||
// on cache miss or when the input is a UUID string (router cache keys are
|
||||
// canonicalized to `tenantID:agentKey`, so UUID inputs never hit the cache).
|
||||
// If router is nil, delegates straight to resolveAgentUUID.
|
||||
// See docs/agent-identity-conventions.md trap zone 5 and section 8.
|
||||
func resolveAgentUUIDCached(ctx context.Context, router *agent.Router, agentStore store.AgentStore, keyOrID string) (uuid.UUID, error) {
|
||||
// Fast path: input is agent_key and the agent is cached in the router.
|
||||
if router != nil {
|
||||
|
||||
@@ -477,12 +477,12 @@ func (s *PGMemoryStore) Close() error { return nil }
|
||||
|
||||
// --- Helpers ---
|
||||
|
||||
// parseUUID returns the parsed UUID or a descriptive error.
|
||||
// Use for every INSERT/UPDATE/UPSERT/DELETE/SELECT-WHERE path where silent nil
|
||||
// would either corrupt data (FK constraints reject it, but callers see cryptic
|
||||
// PG code 23503 instead of a clean Go error) OR hide bugs as empty reads /
|
||||
// zero-row updates. Phase 4 migration target for all 57 CRITICAL sites.
|
||||
// See docs/agent-identity-conventions.md (Phase 6).
|
||||
// parseUUID returns the parsed UUID or a descriptive error. Use for every
|
||||
// INSERT/UPDATE/UPSERT/DELETE and any SELECT WHERE where silent nil would
|
||||
// either corrupt data or hide bugs as empty reads / zero-row updates. FK
|
||||
// constraints reject bad writes at the driver layer, but errors there come
|
||||
// back as cryptic PG 23503 — parseUUID catches them upstream with a clean
|
||||
// Go error. See docs/agent-identity-conventions.md trap zone 3.
|
||||
func parseUUID(s string) (uuid.UUID, error) {
|
||||
id, err := uuid.Parse(s)
|
||||
if err != nil {
|
||||
@@ -492,11 +492,11 @@ func parseUUID(s string) (uuid.UUID, error) {
|
||||
}
|
||||
|
||||
// parseUUIDOrNil returns the parsed UUID or uuid.Nil on failure, without
|
||||
// raising an error. INTENTIONALLY silent — only use for read-only SELECT-WHERE
|
||||
// paths where a no-match (empty result) is acceptable. Do NOT use for writes,
|
||||
// updates, deletes, or any SELECT where empty result would hide a bug.
|
||||
// Prefer parseUUID for any new code.
|
||||
// See docs/agent-identity-conventions.md (Phase 6).
|
||||
// raising an error. INTENTIONALLY silent — only acceptable on read-only
|
||||
// SELECT WHERE paths where a no-match (empty result) is the correct
|
||||
// semantics on bad input. Do NOT use for writes, updates, deletes, or any
|
||||
// SELECT where an empty result would hide a bug. Prefer parseUUID for new
|
||||
// code. See docs/agent-identity-conventions.md trap zone 3.
|
||||
func parseUUIDOrNil(s string) uuid.UUID {
|
||||
id, err := uuid.Parse(s)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user