From d08a985ea9bb350ff80b8488f5c998367b380d12 Mon Sep 17 00:00:00 2001 From: dtoro Date: Fri, 10 Jul 2026 09:37:08 +0200 Subject: [PATCH] fix: execution slug collision under back-to-back requests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Live testing hit `entities_slug_key` violations: exec slugs used an 8-char prefix of a UUIDv7, whose leading bytes encode a millisecond timestamp — two executions created seconds apart can share a prefix. Use the full UUID (guaranteed unique) for the exec entity's slug/name in request_execution, the new `run` tool, and the REST RequestExecution handler — all three had the same pattern. Co-Authored-By: Claude Opus 4.8 --- internal/httpapi/phase3.go | 5 ++++- internal/mcp/server.go | 15 +++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/internal/httpapi/phase3.go b/internal/httpapi/phase3.go index 1e64b65..7bb483d 100644 --- a/internal/httpapi/phase3.go +++ b/internal/httpapi/phase3.go @@ -1103,7 +1103,10 @@ func (s *Server) RequestExecution(ctx context.Context, req gen.RequestExecutionR q := sqlcgen.New(tx) - execSlug := "exec:" + id.String()[:8] + // Full UUID, not a truncated prefix — an 8-char prefix of a UUIDv7 + // collides for real under back-to-back requests since the leading bytes + // encode a millisecond timestamp (observed live via the MCP run tool). + execSlug := "exec:" + id.String() if _, err := q.InsertEntity(ctx, sqlcgen.InsertEntityParams{ ID: id, Slug: execSlug, diff --git a/internal/mcp/server.go b/internal/mcp/server.go index bc95888..c4fe008 100644 --- a/internal/mcp/server.go +++ b/internal/mcp/server.go @@ -310,8 +310,12 @@ func newServer(pool *db.Pool, agentID uuid.UUID) *mcp.Server { id, _ := uuid.NewV7() correlationID := uuid.New().String() - execName := action + " on " + targetSlug + " (" + id.String()[:8] + ")" - execSlug := "exec:" + targetSlug + ":" + id.String()[:8] + // Full UUID, not a truncated prefix: UUIDv7's leading bytes encode a + // millisecond timestamp, so an 8-char prefix collides for real under + // back-to-back requests (observed live: two `run` calls seconds + // apart hit entities_slug_key). The full string is guaranteed unique. + execName := action + " on " + targetSlug + " (" + id.String() + ")" + execSlug := "exec:" + targetSlug + ":" + id.String() _, err := pool.Exec(ctx, `INSERT INTO entities (id, slug, type, name, attributes) VALUES ($1, $2, 'execution', $3, '{}')`, id, execSlug, execName) if err != nil { @@ -452,8 +456,11 @@ func newServer(pool *db.Pool, agentID uuid.UUID) *mcp.Server { id, _ := uuid.NewV7() correlationID := uuid.New().String() - execName := "run on " + targetSlug + " (" + id.String()[:8] + ")" - execSlug := "exec:" + targetSlug + ":" + id.String()[:8] + // Full UUID, not a truncated prefix — see the matching comment on + // request_execution's exec slug generation above; the 8-char prefix + // collided for real under back-to-back requests. + execName := "run on " + targetSlug + " (" + id.String() + ")" + execSlug := "exec:" + targetSlug + ":" + id.String() if _, err := pool.Exec(ctx, `INSERT INTO entities (id, slug, type, name, attributes) VALUES ($1, $2, 'execution', $3, '{}')`, id, execSlug, execName); err != nil { return textResult(fmt.Sprintf("error: failed to create execution: %v", err)), nil