Files
multica/server/pkg/db/generated/runtime_profile.sql.go
T
Bohan Jiang b425073cae MUL-7409 fix(runtime): stop steering stale-instance cleanup at the shared profile (#8483)
Deleting a profile-backed runtime instance is refused while its profile exists,
and the refusal said "delete its runtime profile instead". For the case that
produces it most often — a retired machine's leftover row in a profile other,
healthy machines still use — that points at a workspace-wide delete which takes
those machines' runtimes too, and which a bound agent refuses anyway with a
second message naming no agent and no machine.

Retention GC already deletes these rows after 7 days offline, ignoring
profile_id, so the refusals now describe that instead of recommending the
destructive route:

- Instance refusal is status-aware, and only promises automatic cleanup when it
  matches the gate gcRuntime actually applies: the runtime's own undrained
  tasks plus those owned by every user agent bound to it, archived included.
- Profile refusal names the blocking agents and the machine each sits on, with
  a per-class remedy — ordinary agents can be reassigned or archived; Mika
  cannot be archived but can be rebound; a Builder session is reachable only by
  its creator. Those clauses come from counts over the whole blocker set, so a
  class outside the 20-row sample is still reported.
- Response bounded at 20 entries with an exact total, capping rows read inside
  the locked delete transaction.
- CLI surfaces the server's sentence instead of dumping the raw JSON body.

When a delete is allowed or refused is unchanged; the guard predicates and the
instance-refusal error code are untouched. The remaining capability gap —
retiring an offline instance on demand — is tracked separately.

Fixes MUL-7409
2026-09-17 17:11:49 +08:00

577 lines
19 KiB
Go

// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.31.1
// source: runtime_profile.sql
package db
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
const createRuntimeProfile = `-- name: CreateRuntimeProfile :one
INSERT INTO runtime_profile (
workspace_id,
display_name,
protocol_family,
command_name,
description,
fixed_args,
visibility,
created_by,
enabled
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
RETURNING id, workspace_id, display_name, protocol_family, command_name, description, fixed_args, visibility, created_by, enabled, created_at, updated_at
`
type CreateRuntimeProfileParams struct {
WorkspaceID pgtype.UUID `json:"workspace_id"`
DisplayName string `json:"display_name"`
ProtocolFamily string `json:"protocol_family"`
CommandName string `json:"command_name"`
Description pgtype.Text `json:"description"`
FixedArgs []byte `json:"fixed_args"`
Visibility string `json:"visibility"`
CreatedBy pgtype.UUID `json:"created_by"`
Enabled bool `json:"enabled"`
}
// Custom Runtime profiles (MUL-3284). Workspace-level definitions of a custom
// runtime; see migration 120 for the table. Relational integrity (workspace,
// created_by) is enforced in the application layer — there are no DB FKs.
func (q *Queries) CreateRuntimeProfile(ctx context.Context, arg CreateRuntimeProfileParams) (RuntimeProfile, error) {
row := q.db.QueryRow(ctx, createRuntimeProfile,
arg.WorkspaceID,
arg.DisplayName,
arg.ProtocolFamily,
arg.CommandName,
arg.Description,
arg.FixedArgs,
arg.Visibility,
arg.CreatedBy,
arg.Enabled,
)
var i RuntimeProfile
err := row.Scan(
&i.ID,
&i.WorkspaceID,
&i.DisplayName,
&i.ProtocolFamily,
&i.CommandName,
&i.Description,
&i.FixedArgs,
&i.Visibility,
&i.CreatedBy,
&i.Enabled,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const deleteAgentRuntimesByProfile = `-- name: DeleteAgentRuntimesByProfile :many
DELETE FROM agent_runtime
WHERE profile_id = $1 AND workspace_id = $2
RETURNING id, workspace_id, owner_id, daemon_id, provider
`
type DeleteAgentRuntimesByProfileParams struct {
ProfileID pgtype.UUID `json:"profile_id"`
WorkspaceID pgtype.UUID `json:"workspace_id"`
}
type DeleteAgentRuntimesByProfileRow struct {
ID pgtype.UUID `json:"id"`
WorkspaceID pgtype.UUID `json:"workspace_id"`
OwnerID pgtype.UUID `json:"owner_id"`
DaemonID pgtype.Text `json:"daemon_id"`
Provider string `json:"provider"`
}
// Application-layer cascade: migration 120 dropped the DB ON DELETE CASCADE, so
// the profile-delete path must remove the profile's registered runtime
// instances itself. Returns the deleted rows so the caller can broadcast /
// audit. Runs inside the same transaction as DeleteRuntimeProfile.
func (q *Queries) DeleteAgentRuntimesByProfile(ctx context.Context, arg DeleteAgentRuntimesByProfileParams) ([]DeleteAgentRuntimesByProfileRow, error) {
rows, err := q.db.Query(ctx, deleteAgentRuntimesByProfile, arg.ProfileID, arg.WorkspaceID)
if err != nil {
return nil, err
}
defer rows.Close()
items := []DeleteAgentRuntimesByProfileRow{}
for rows.Next() {
var i DeleteAgentRuntimesByProfileRow
if err := rows.Scan(
&i.ID,
&i.WorkspaceID,
&i.OwnerID,
&i.DaemonID,
&i.Provider,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const deleteRuntimeProfile = `-- name: DeleteRuntimeProfile :exec
DELETE FROM runtime_profile
WHERE id = $1 AND workspace_id = $2
`
type DeleteRuntimeProfileParams struct {
ID pgtype.UUID `json:"id"`
WorkspaceID pgtype.UUID `json:"workspace_id"`
}
func (q *Queries) DeleteRuntimeProfile(ctx context.Context, arg DeleteRuntimeProfileParams) error {
_, err := q.db.Exec(ctx, deleteRuntimeProfile, arg.ID, arg.WorkspaceID)
return err
}
const getRuntimeProfile = `-- name: GetRuntimeProfile :one
SELECT id, workspace_id, display_name, protocol_family, command_name, description, fixed_args, visibility, created_by, enabled, created_at, updated_at FROM runtime_profile
WHERE id = $1
`
func (q *Queries) GetRuntimeProfile(ctx context.Context, id pgtype.UUID) (RuntimeProfile, error) {
row := q.db.QueryRow(ctx, getRuntimeProfile, id)
var i RuntimeProfile
err := row.Scan(
&i.ID,
&i.WorkspaceID,
&i.DisplayName,
&i.ProtocolFamily,
&i.CommandName,
&i.Description,
&i.FixedArgs,
&i.Visibility,
&i.CreatedBy,
&i.Enabled,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const getRuntimeProfileForWorkspace = `-- name: GetRuntimeProfileForWorkspace :one
SELECT id, workspace_id, display_name, protocol_family, command_name, description, fixed_args, visibility, created_by, enabled, created_at, updated_at FROM runtime_profile
WHERE id = $1 AND workspace_id = $2
`
type GetRuntimeProfileForWorkspaceParams struct {
ID pgtype.UUID `json:"id"`
WorkspaceID pgtype.UUID `json:"workspace_id"`
}
func (q *Queries) GetRuntimeProfileForWorkspace(ctx context.Context, arg GetRuntimeProfileForWorkspaceParams) (RuntimeProfile, error) {
row := q.db.QueryRow(ctx, getRuntimeProfileForWorkspace, arg.ID, arg.WorkspaceID)
var i RuntimeProfile
err := row.Scan(
&i.ID,
&i.WorkspaceID,
&i.DisplayName,
&i.ProtocolFamily,
&i.CommandName,
&i.Description,
&i.FixedArgs,
&i.Visibility,
&i.CreatedBy,
&i.Enabled,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const listActiveAgentsByProfile = `-- name: ListActiveAgentsByProfile :many
WITH blockers AS (
SELECT
a.id,
a.name,
a.kind,
a.system_key,
ar.id AS runtime_id,
ar.name AS runtime_name,
ar.custom_name AS runtime_custom_name,
ar.status AS runtime_status,
CASE
WHEN a.system_key IS NULL OR btrim(a.system_key) = '' THEN 'user'
WHEN btrim(a.system_key) = 'mika' THEN 'mika'
-- starts_with, not LIKE: '_' is a single-character wildcard in
-- LIKE, so 'agent_builder:%' also matches 'agent-builder:x' and
-- 'agentXbuilder:x'. Those would be classified here as carriers
-- and by the Go side as other_system, and the refusal would send
-- the user to an Agent Builder session that does not exist.
WHEN starts_with(btrim(a.system_key), 'agent_builder:') THEN 'agent_builder'
ELSE 'other_system'
END AS blocker_class
FROM agent a
JOIN agent_runtime ar ON ar.id = a.runtime_id
WHERE ar.profile_id = $1 AND ar.workspace_id = $2 AND a.archived_at IS NULL
)
SELECT
id,
name,
kind,
system_key,
runtime_id,
runtime_name,
runtime_custom_name,
runtime_status,
blocker_class,
count(*) OVER () AS total_count,
count(*) FILTER (WHERE blocker_class = 'user') OVER () AS user_count,
count(*) FILTER (WHERE blocker_class = 'mika') OVER () AS mika_count,
count(*) FILTER (WHERE blocker_class = 'agent_builder') OVER () AS agent_builder_count,
count(*) FILTER (WHERE blocker_class = 'other_system') OVER () AS other_system_count
FROM blockers
ORDER BY runtime_name ASC, name ASC
LIMIT $3::int
`
type ListActiveAgentsByProfileParams struct {
ProfileID pgtype.UUID `json:"profile_id"`
WorkspaceID pgtype.UUID `json:"workspace_id"`
MaxRows int32 `json:"max_rows"`
}
type ListActiveAgentsByProfileRow struct {
ID pgtype.UUID `json:"id"`
Name string `json:"name"`
Kind string `json:"kind"`
SystemKey pgtype.Text `json:"system_key"`
RuntimeID pgtype.UUID `json:"runtime_id"`
RuntimeName string `json:"runtime_name"`
RuntimeCustomName pgtype.Text `json:"runtime_custom_name"`
RuntimeStatus string `json:"runtime_status"`
BlockerClass string `json:"blocker_class"`
TotalCount int64 `json:"total_count"`
UserCount int64 `json:"user_count"`
MikaCount int64 `json:"mika_count"`
AgentBuilderCount int64 `json:"agent_builder_count"`
OtherSystemCount int64 `json:"other_system_count"`
}
// Active (non-archived) agents bound to any runtime instance of this profile.
// The profile-delete path uses this to refuse deletion (409) while agents
// still depend on it, mirroring the runtime-delete guard.
//
// It returns the rows rather than a count because the refusal has to name
// them: a profile spans every machine that registered it, so the agents
// blocking the delete are routinely bound to a different machine than the one
// the user was trying to clean up, and a bare count gives them no way to tell
// (GH #8456). Carrying the runtime is what lets the message say which machine.
//
// Deliberately not filtered by kind: it defines when deletion is refused, and
// narrowing it to user agents here would let a profile with a bound builder
// carrier through, whereupon TeardownRuntime would hard-delete that carrier.
//
// system_key rides along because it, not kind, decides what the user can
// actually do about a blocker. Mika is kind='user' with system_key='mika' and
// can be neither archived nor moved; a builder carrier is kind='system' and is
// released by its Builder session, not from the agent list.
//
// Bounded on purpose. The caller only needs to know that blockers exist, name a
// few, and report how many there are — it never needs every row. This runs
// inside the delete transaction while profile, runtime and agent rows are
// locked, and the response is buffered whole before it is written, so an
// unbounded read here would make both the time under lock and the response body
// grow with the number of agents a profile has accumulated across machines.
//
// The per-class counts are what let the refusal stay correct while bounded.
// Rows are ordered by machine and name, so which blockers land inside the LIMIT
// is arbitrary with respect to class: twenty ordinary agents can push the one
// Mika to position 21. A message whose advice came from the visible rows would
// then tell the user to archive all of them — the unactionable instruction this
// whole change removes, just deferred. So the recovery paths are chosen from
// these counts and only the names come from the rows.
//
// Every count is a window function over the pre-LIMIT result, so they stay
// exact no matter how small max_rows is. blocker_class is emitted per row as
// well, giving the classification one definition that the Go classifier is
// pinned against in tests.
func (q *Queries) ListActiveAgentsByProfile(ctx context.Context, arg ListActiveAgentsByProfileParams) ([]ListActiveAgentsByProfileRow, error) {
rows, err := q.db.Query(ctx, listActiveAgentsByProfile, arg.ProfileID, arg.WorkspaceID, arg.MaxRows)
if err != nil {
return nil, err
}
defer rows.Close()
items := []ListActiveAgentsByProfileRow{}
for rows.Next() {
var i ListActiveAgentsByProfileRow
if err := rows.Scan(
&i.ID,
&i.Name,
&i.Kind,
&i.SystemKey,
&i.RuntimeID,
&i.RuntimeName,
&i.RuntimeCustomName,
&i.RuntimeStatus,
&i.BlockerClass,
&i.TotalCount,
&i.UserCount,
&i.MikaCount,
&i.AgentBuilderCount,
&i.OtherSystemCount,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listAgentRuntimeIDsByProfile = `-- name: ListAgentRuntimeIDsByProfile :many
SELECT id FROM agent_runtime
WHERE profile_id = $1 AND workspace_id = $2
ORDER BY id
FOR UPDATE
`
type ListAgentRuntimeIDsByProfileParams struct {
ProfileID pgtype.UUID `json:"profile_id"`
WorkspaceID pgtype.UUID `json:"workspace_id"`
}
// Enumerates the runtime instance rows registered against a profile. The
// profile-delete cascade walks these so it can run the same archived-agent /
// archived-squad / autopilot teardown the runtime-delete path uses before
// removing each runtime row — agent.runtime_id is ON DELETE RESTRICT, so a
// bare delete would 500 whenever an archived agent still references the row.
func (q *Queries) ListAgentRuntimeIDsByProfile(ctx context.Context, arg ListAgentRuntimeIDsByProfileParams) ([]pgtype.UUID, error) {
rows, err := q.db.Query(ctx, listAgentRuntimeIDsByProfile, arg.ProfileID, arg.WorkspaceID)
if err != nil {
return nil, err
}
defer rows.Close()
items := []pgtype.UUID{}
for rows.Next() {
var id pgtype.UUID
if err := rows.Scan(&id); err != nil {
return nil, err
}
items = append(items, id)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listEnabledRuntimeProfilesForWorkspace = `-- name: ListEnabledRuntimeProfilesForWorkspace :many
SELECT id, workspace_id, display_name, protocol_family, command_name, description, fixed_args, visibility, created_by, enabled, created_at, updated_at FROM runtime_profile
WHERE workspace_id = $1 AND enabled = true
ORDER BY created_at ASC
`
// Daemon-facing list: only enabled profiles are candidates for a daemon to
// resolve on PATH and register. Ordered for stable output.
func (q *Queries) ListEnabledRuntimeProfilesForWorkspace(ctx context.Context, workspaceID pgtype.UUID) ([]RuntimeProfile, error) {
rows, err := q.db.Query(ctx, listEnabledRuntimeProfilesForWorkspace, workspaceID)
if err != nil {
return nil, err
}
defer rows.Close()
items := []RuntimeProfile{}
for rows.Next() {
var i RuntimeProfile
if err := rows.Scan(
&i.ID,
&i.WorkspaceID,
&i.DisplayName,
&i.ProtocolFamily,
&i.CommandName,
&i.Description,
&i.FixedArgs,
&i.Visibility,
&i.CreatedBy,
&i.Enabled,
&i.CreatedAt,
&i.UpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listRuntimeProfiles = `-- name: ListRuntimeProfiles :many
SELECT id, workspace_id, display_name, protocol_family, command_name, description, fixed_args, visibility, created_by, enabled, created_at, updated_at FROM runtime_profile
WHERE workspace_id = $1
ORDER BY created_at ASC
`
func (q *Queries) ListRuntimeProfiles(ctx context.Context, workspaceID pgtype.UUID) ([]RuntimeProfile, error) {
rows, err := q.db.Query(ctx, listRuntimeProfiles, workspaceID)
if err != nil {
return nil, err
}
defer rows.Close()
items := []RuntimeProfile{}
for rows.Next() {
var i RuntimeProfile
if err := rows.Scan(
&i.ID,
&i.WorkspaceID,
&i.DisplayName,
&i.ProtocolFamily,
&i.CommandName,
&i.Description,
&i.FixedArgs,
&i.Visibility,
&i.CreatedBy,
&i.Enabled,
&i.CreatedAt,
&i.UpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const lockRuntimeProfileForDelete = `-- name: LockRuntimeProfileForDelete :one
SELECT id, workspace_id, display_name, protocol_family, command_name, description, fixed_args, visibility, created_by, enabled, created_at, updated_at FROM runtime_profile
WHERE id = $1 AND workspace_id = $2
FOR UPDATE
`
type LockRuntimeProfileForDeleteParams struct {
ID pgtype.UUID `json:"id"`
WorkspaceID pgtype.UUID `json:"workspace_id"`
}
// See LockRuntimeProfileForRegistration. The stronger lock prevents a daemon
// from registering another instance between the delete plan and commit.
func (q *Queries) LockRuntimeProfileForDelete(ctx context.Context, arg LockRuntimeProfileForDeleteParams) (RuntimeProfile, error) {
row := q.db.QueryRow(ctx, lockRuntimeProfileForDelete, arg.ID, arg.WorkspaceID)
var i RuntimeProfile
err := row.Scan(
&i.ID,
&i.WorkspaceID,
&i.DisplayName,
&i.ProtocolFamily,
&i.CommandName,
&i.Description,
&i.FixedArgs,
&i.Visibility,
&i.CreatedBy,
&i.Enabled,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const lockRuntimeProfileForRegistration = `-- name: LockRuntimeProfileForRegistration :one
SELECT id, workspace_id, display_name, protocol_family, command_name, description, fixed_args, visibility, created_by, enabled, created_at, updated_at FROM runtime_profile
WHERE id = $1 AND workspace_id = $2
FOR KEY SHARE
`
type LockRuntimeProfileForRegistrationParams struct {
ID pgtype.UUID `json:"id"`
WorkspaceID pgtype.UUID `json:"workspace_id"`
}
// Serializes daemon registration with profile deletion. Registration holds a
// KEY SHARE lock until its runtime row is committed; profile deletion takes an
// UPDATE lock, then locks the profile's runtime rows. Whichever starts first
// wins, so deletion cannot miss a runtime inserted from a stale profile read.
func (q *Queries) LockRuntimeProfileForRegistration(ctx context.Context, arg LockRuntimeProfileForRegistrationParams) (RuntimeProfile, error) {
row := q.db.QueryRow(ctx, lockRuntimeProfileForRegistration, arg.ID, arg.WorkspaceID)
var i RuntimeProfile
err := row.Scan(
&i.ID,
&i.WorkspaceID,
&i.DisplayName,
&i.ProtocolFamily,
&i.CommandName,
&i.Description,
&i.FixedArgs,
&i.Visibility,
&i.CreatedBy,
&i.Enabled,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const updateRuntimeProfile = `-- name: UpdateRuntimeProfile :one
UPDATE runtime_profile
SET display_name = COALESCE($1, display_name),
command_name = COALESCE($2, command_name),
description = COALESCE($3, description),
fixed_args = COALESCE($4, fixed_args),
visibility = COALESCE($5, visibility),
enabled = COALESCE($6, enabled),
updated_at = now()
WHERE id = $7 AND workspace_id = $8
RETURNING id, workspace_id, display_name, protocol_family, command_name, description, fixed_args, visibility, created_by, enabled, created_at, updated_at
`
type UpdateRuntimeProfileParams struct {
DisplayName pgtype.Text `json:"display_name"`
CommandName pgtype.Text `json:"command_name"`
Description pgtype.Text `json:"description"`
FixedArgs []byte `json:"fixed_args"`
Visibility pgtype.Text `json:"visibility"`
Enabled pgtype.Bool `json:"enabled"`
ID pgtype.UUID `json:"id"`
WorkspaceID pgtype.UUID `json:"workspace_id"`
}
// Partial update via COALESCE: NULL args leave the column unchanged. The
// protocol_family is intentionally NOT updatable — changing the underlying
// backend of an existing profile would silently repoint every agent bound to
// it onto a different protocol; callers create a new profile instead.
func (q *Queries) UpdateRuntimeProfile(ctx context.Context, arg UpdateRuntimeProfileParams) (RuntimeProfile, error) {
row := q.db.QueryRow(ctx, updateRuntimeProfile,
arg.DisplayName,
arg.CommandName,
arg.Description,
arg.FixedArgs,
arg.Visibility,
arg.Enabled,
arg.ID,
arg.WorkspaceID,
)
var i RuntimeProfile
err := row.Scan(
&i.ID,
&i.WorkspaceID,
&i.DisplayName,
&i.ProtocolFamily,
&i.CommandName,
&i.Description,
&i.FixedArgs,
&i.Visibility,
&i.CreatedBy,
&i.Enabled,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}