mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-13 22:15:47 +00:00
refactor(store/pg): propagate error from optAgentUUID wrapper
optAgentUUID now returns (*uuid.UUID, error). All three known callers updated to check the error and wrap with context: 1. vault_documents.go UpsertDocument — also migrates tenant_id and team_id to parseUUID in the same hot path. 2. vault_documents.go Search — migrates tenant_id and agent_id. 3. vault_documents_enrichment.go FindSimilarDocs — migrates tenant_id, agent_id, and doc_id. Pre-Phase-4, the wrapper silently converted garbage agent_id strings to uuid.Nil, letting the downstream INSERT hit a FK violation with cryptic PG code 23503. Now bad input fails at the caller boundary with a clear Go error wrapping the raw value. Phase 4 Step 4e of agent identity hardening (TD-1). Covers sub-step 4b migrations for vault_documents.go and vault_documents_enrichment.go as a side effect.
This commit is contained in:
1 parent
9a8eb396b0
commit
32af80b28c
2 files changed
+45
-13
No files matched your search
@@ -55,18 +55,32 @@ func (s *PGVaultStore) SetEmbeddingProvider(provider store.EmbeddingProvider) {
|
||||
func (s *PGVaultStore) Close() error { return nil }
|
||||
|
||||
// optAgentUUID converts a nullable *string agent_id to *uuid.UUID for SQL.
|
||||
func optAgentUUID(agentID *string) *uuid.UUID {
|
||||
// Returns (nil, nil) when the input is nil or empty — a legitimate SQL NULL.
|
||||
// Returns (nil, error) on a non-empty, non-UUID input — propagating the error
|
||||
// prevents silent-nil writes that would otherwise corrupt data (pre-Phase-4
|
||||
// this helper silently returned nil on garbage input).
|
||||
// See docs/agent-identity-conventions.md (Phase 6).
|
||||
func optAgentUUID(agentID *string) (*uuid.UUID, error) {
|
||||
if agentID == nil || *agentID == "" {
|
||||
return nil
|
||||
return nil, nil
|
||||
}
|
||||
u := mustParseUUID(*agentID)
|
||||
return &u
|
||||
u, err := parseUUID(*agentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &u, nil
|
||||
}
|
||||
|
||||
// UpsertDocument inserts or updates a vault document.
|
||||
func (s *PGVaultStore) UpsertDocument(ctx context.Context, doc *store.VaultDocument) error {
|
||||
tid := mustParseUUID(doc.TenantID)
|
||||
aid := optAgentUUID(doc.AgentID)
|
||||
tid, err := parseUUID(doc.TenantID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("vault upsert: tenant: %w", err)
|
||||
}
|
||||
aid, err := optAgentUUID(doc.AgentID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("vault upsert: agent: %w", err)
|
||||
}
|
||||
now := time.Now().UTC()
|
||||
|
||||
meta, err := json.Marshal(doc.Metadata)
|
||||
@@ -91,7 +105,10 @@ func (s *PGVaultStore) UpsertDocument(ctx context.Context, doc *store.VaultDocum
|
||||
|
||||
var teamID *uuid.UUID
|
||||
if doc.TeamID != nil && *doc.TeamID != "" {
|
||||
t := mustParseUUID(*doc.TeamID)
|
||||
t, err := parseUUID(*doc.TeamID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("vault upsert: team: %w", err)
|
||||
}
|
||||
teamID = &t
|
||||
}
|
||||
|
||||
@@ -358,8 +375,14 @@ func (s *PGVaultStore) UpdateHash(ctx context.Context, tenantID, id, newHash str
|
||||
|
||||
// Search performs hybrid FTS + vector search on vault_documents.
|
||||
func (s *PGVaultStore) Search(ctx context.Context, opts store.VaultSearchOptions) ([]store.VaultSearchResult, error) {
|
||||
tid := mustParseUUID(opts.TenantID)
|
||||
aid := optAgentUUID(&opts.AgentID) // empty string → nil → no agent filter
|
||||
tid, err := parseUUID(opts.TenantID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("vault search: tenant: %w", err)
|
||||
}
|
||||
aid, err := optAgentUUID(&opts.AgentID) // empty string → nil → no agent filter
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("vault search: agent: %w", err)
|
||||
}
|
||||
|
||||
// Build team filter for search sub-queries.
|
||||
tf := buildSearchTeamFilter(opts.TeamID, opts.TeamIDs)
|
||||
|
||||
@@ -46,13 +46,22 @@ func (s *PGVaultStore) UpdateSummaryAndReembed(ctx context.Context, tenantID, do
|
||||
// Returns top-N neighbors excluding the source doc itself.
|
||||
// Empty agentID means no agent filter.
|
||||
func (s *PGVaultStore) FindSimilarDocs(ctx context.Context, tenantID, agentID, docID string, limit int) ([]store.VaultSearchResult, error) {
|
||||
tid := mustParseUUID(tenantID)
|
||||
aid := optAgentUUID(&agentID)
|
||||
did := mustParseUUID(docID)
|
||||
tid, err := parseUUID(tenantID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("find similar: tenant: %w", err)
|
||||
}
|
||||
aid, err := optAgentUUID(&agentID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("find similar: agent: %w", err)
|
||||
}
|
||||
did, err := parseUUID(docID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("find similar: doc: %w", err)
|
||||
}
|
||||
|
||||
// Fetch source embedding.
|
||||
var embStr *string
|
||||
err := s.db.QueryRowContext(ctx,
|
||||
err = s.db.QueryRowContext(ctx,
|
||||
`SELECT embedding::text FROM vault_documents WHERE id = $1 AND tenant_id = $2`,
|
||||
did, tid,
|
||||
).Scan(&embStr)
|
||||
|
||||
Reference in new issue
Block a user