From f2a6398d6ea742cc10c96a8d460b4ecc8d95db2a Mon Sep 17 00:00:00 2001 From: Multica Eve Date: Thu, 29 Oct 2026 16:49:57 +0800 Subject: [PATCH] MUL-7257: refactor(server): remove unused compatibility queries and capabilities (#9671) * refactor(comments): drop redundant RFC3339 fallback parse RFC3339Nano parsing already accepts timestamps without fractional seconds, so the second time.Parse(time.RFC3339) attempt can never succeed where the first failed. Keep the existing 400 responses and add a test pinning second-precision since and the invalid since/before cases. Co-authored-by: multica-agent * refactor(daemon): stop advertising unread plugin capabilities execution-manifest-v1, agent-skill-v1 and remote-mcp-v1 have had no server-side reader since the Plugin V1 gates were removed in #7144. Drop the constants and the daemon advertisement. Co-authored-by: multica-agent * refactor(autopilot): remove legacy no-reservation terminal queries UpdateAutopilotRunCompleted, UpdateAutopilotRunFailed and UpdateAutopilotRunSkippedWithResult have no production callers; terminal transitions go through UpdateAutopilotRunTerminalWithQuota. Their generated Params structs were still used as service argument types, so replace them with service-local structs. The reconciler test now seeds its crash window with an explicit fixture UPDATE. Co-authored-by: multica-agent * refactor(tasks): remove unused StartAgentTask query Production start paths use StartAgentTaskWithSupplement. Move the seven test fixtures to the same query with both capability flags off, which performs the same running transition. The empty-body start handler branch is unchanged. Co-authored-by: multica-agent * refactor(channels): remove unused pending_fresh queries MarkChannelChatSessionPendingFresh, LockChannelChatSessionPendingFresh and ClearChannelChatSessionPendingFresh have no callers. The context generation path and ClearChannelChatSessionPendingFreshForRevision remain. Co-authored-by: multica-agent --------- Co-authored-by: multica-agent --- server/cmd/server/autopilot_listeners_test.go | 8 +- .../server/quick_create_subscriber_test.go | 20 +-- server/internal/daemon/client.go | 3 - server/internal/handler/comment.go | 16 +-- server/internal/handler/comment_list_test.go | 31 +++++ server/internal/service/autopilot.go | 14 +- server/internal/service/autopilot_quota.go | 18 ++- .../internal/service/autopilot_quota_test.go | 12 +- server/pkg/db/generated/agent.sql.go | 90 +------------ server/pkg/db/generated/autopilot.sql.go | 126 ------------------ server/pkg/db/generated/channel.sql.go | 44 ------ server/pkg/db/queries/agent.sql | 21 +-- server/pkg/db/queries/autopilot.sql | 28 ---- server/pkg/db/queries/channel.sql | 22 --- server/pkg/protocol/messages.go | 3 - 15 files changed, 86 insertions(+), 370 deletions(-) diff --git a/server/cmd/server/autopilot_listeners_test.go b/server/cmd/server/autopilot_listeners_test.go index 8bde24c6a..02a513936 100644 --- a/server/cmd/server/autopilot_listeners_test.go +++ b/server/cmd/server/autopilot_listeners_test.go @@ -117,9 +117,9 @@ func TestAutopilotRunOnlyTaskTerminalEventsUpdateRun(t *testing.T) { ); err != nil { t.Fatalf("mark task dispatched: %v", err) } - task, err := queries.StartAgentTask(ctx, run.TaskID) + task, err := queries.StartAgentTaskWithSupplement(ctx, db.StartAgentTaskWithSupplementParams{TaskID: run.TaskID}) if err != nil { - t.Fatalf("StartAgentTask: %v", err) + t.Fatalf("StartAgentTaskWithSupplement: %v", err) } tc.finalize(task) @@ -239,8 +239,8 @@ func runTaskWithBudget(t *testing.T, queries *db.Queries, taskID pgtype.UUID, ma ); err != nil { t.Fatalf("mark task dispatched: %v", err) } - if _, err := queries.StartAgentTask(context.Background(), taskID); err != nil { - t.Fatalf("StartAgentTask: %v", err) + if _, err := queries.StartAgentTaskWithSupplement(context.Background(), db.StartAgentTaskWithSupplementParams{TaskID: taskID}); err != nil { + t.Fatalf("StartAgentTaskWithSupplement: %v", err) } } diff --git a/server/cmd/server/quick_create_subscriber_test.go b/server/cmd/server/quick_create_subscriber_test.go index b14cf03cb..55a2b2613 100644 --- a/server/cmd/server/quick_create_subscriber_test.go +++ b/server/cmd/server/quick_create_subscriber_test.go @@ -69,8 +69,8 @@ func TestQuickCreateFailure_DoesNotSubscribeRequester(t *testing.T) { ); err != nil { t.Fatalf("dispatch task: %v", err) } - if _, err := queries.StartAgentTask(ctx, task.ID); err != nil { - t.Fatalf("StartAgentTask: %v", err) + if _, err := queries.StartAgentTaskWithSupplement(ctx, db.StartAgentTaskWithSupplementParams{TaskID: task.ID}); err != nil { + t.Fatalf("StartAgentTaskWithSupplement: %v", err) } // No issue with origin_type=quick_create + this task id exists. Completion @@ -139,8 +139,8 @@ func TestQuickCreateFailure_SurfacesAgentOutput(t *testing.T) { ); err != nil { t.Fatalf("dispatch task: %v", err) } - if _, err := queries.StartAgentTask(ctx, task.ID); err != nil { - t.Fatalf("StartAgentTask: %v", err) + if _, err := queries.StartAgentTaskWithSupplement(ctx, db.StartAgentTaskWithSupplementParams{TaskID: task.ID}); err != nil { + t.Fatalf("StartAgentTaskWithSupplement: %v", err) } // No issue is created; the agent exits with the CLI duplicate error as its @@ -211,8 +211,8 @@ func TestQuickCreateLookupFault_WritesUnconfirmedInbox(t *testing.T) { ); err != nil { t.Fatalf("dispatch task: %v", err) } - if _, err := queries.StartAgentTask(ctx, task.ID); err != nil { - t.Fatalf("StartAgentTask: %v", err) + if _, err := queries.StartAgentTaskWithSupplement(ctx, db.StartAgentTaskWithSupplementParams{TaskID: task.ID}); err != nil { + t.Fatalf("StartAgentTaskWithSupplement: %v", err) } // Completion runs against a Queries whose origin lookup always faults. The @@ -292,8 +292,8 @@ func TestQuickCreateFailure_RedactsAgentOutput(t *testing.T) { ); err != nil { t.Fatalf("dispatch task: %v", err) } - if _, err := queries.StartAgentTask(ctx, task.ID); err != nil { - t.Fatalf("StartAgentTask: %v", err) + if _, err := queries.StartAgentTaskWithSupplement(ctx, db.StartAgentTaskWithSupplementParams{TaskID: task.ID}); err != nil { + t.Fatalf("StartAgentTaskWithSupplement: %v", err) } // Fake, non-functional token shaped like a real GitHub PAT. @@ -361,8 +361,8 @@ func TestQuickCreateLookupCancelled_StillWritesUnconfirmedInbox(t *testing.T) { ); err != nil { t.Fatalf("dispatch task: %v", err) } - if _, err := queries.StartAgentTask(setupCtx, task.ID); err != nil { - t.Fatalf("StartAgentTask: %v", err) + if _, err := queries.StartAgentTaskWithSupplement(setupCtx, db.StartAgentTaskWithSupplementParams{TaskID: task.ID}); err != nil { + t.Fatalf("StartAgentTaskWithSupplement: %v", err) } // The completion transaction runs on a healthy context; the context dies at diff --git a/server/internal/daemon/client.go b/server/internal/daemon/client.go index 2643ff7df..4be7d41c5 100644 --- a/server/internal/daemon/client.go +++ b/server/internal/daemon/client.go @@ -204,9 +204,6 @@ func daemonCommonCapabilities() []string { return []string{ protocol.DaemonCapabilitySkillBundlesV1, protocol.DaemonCapabilityCoalescedCommentsV1, - protocol.DaemonCapabilityExecutionManifestV1, - protocol.DaemonCapabilityAgentSkillV1, - protocol.DaemonCapabilityRemoteMCPV1, protocol.DaemonCapabilityLocalWorktreeV1, protocol.DaemonCapabilitySourceContextQuickCreateV1, protocol.DaemonCapabilityRPCV1, diff --git a/server/internal/handler/comment.go b/server/internal/handler/comment.go index c10ba14fc..1f3f7c8f4 100644 --- a/server/internal/handler/comment.go +++ b/server/internal/handler/comment.go @@ -353,14 +353,11 @@ func (h *Handler) ListComments(w http.ResponseWriter, r *http.Request) { var sinceTime pgtype.Timestamptz if v := q.Get("since"); v != "" { + // RFC3339Nano parsing also accepts timestamps without fractional seconds. t, err := time.Parse(time.RFC3339Nano, v) if err != nil { - // Fall back to RFC3339 for backwards-compat with the original CLI. - t, err = time.Parse(time.RFC3339, v) - if err != nil { - writeError(w, http.StatusBadRequest, "invalid since parameter; expected RFC3339 format") - return - } + writeError(w, http.StatusBadRequest, "invalid since parameter; expected RFC3339 format") + return } sinceTime = pgtype.Timestamptz{Time: t, Valid: true} } @@ -489,11 +486,8 @@ func (h *Handler) ListComments(w http.ResponseWriter, r *http.Request) { if beforeTimeStr != "" { t, err := time.Parse(time.RFC3339Nano, beforeTimeStr) if err != nil { - t, err = time.Parse(time.RFC3339, beforeTimeStr) - if err != nil { - writeError(w, http.StatusBadRequest, "invalid before parameter; expected RFC3339 format") - return - } + writeError(w, http.StatusBadRequest, "invalid before parameter; expected RFC3339 format") + return } beforeCursor = pgtype.Timestamptz{Time: t, Valid: true} uuid, perr := util.ParseUUID(beforeIDStr) diff --git a/server/internal/handler/comment_list_test.go b/server/internal/handler/comment_list_test.go index 3d1bd7393..80fe3ee78 100644 --- a/server/internal/handler/comment_list_test.go +++ b/server/internal/handler/comment_list_test.go @@ -229,6 +229,37 @@ func TestListComments_RootsOnlyWithSinceReturnsNewTopLevelComments(t *testing.T) } } +// TestListComments_TimestampParamsAcceptSecondPrecision pins that RFC3339 +// timestamps without fractional seconds still parse after the single +// RFC3339Nano parse, and that malformed values keep their 400 responses. +func TestListComments_TimestampParamsAcceptSecondPrecision(t *testing.T) { + if testHandler == nil || testPool == nil { + t.Skip("database not available") + } + fx := newCommentListFixture(t) + + v := url.Values{} + v.Set("roots_only", "true") + v.Set("since", fx.Base.Add(5*time.Minute).UTC().Format(time.RFC3339)) + w, rows := listComments(t, fx.IssueID, v.Encode()) + if w.Code != http.StatusOK { + t.Fatalf("second-precision since: status %d body %s", w.Code, w.Body.String()) + } + eqIDs(t, ids(rows), []string{fx.Root2}, "second-precision since") + + for _, tc := range []struct { + name, query, wantMessage string + }{ + {"since", "since=2026-01-02+03:04:05", "invalid since parameter"}, + {"before", cursorQuery(1, "2026-01-02T03:04", fx.Root1), "invalid before parameter"}, + } { + w, _ := listComments(t, fx.IssueID, tc.query) + if w.Code != http.StatusBadRequest || !strings.Contains(w.Body.String(), tc.wantMessage) { + t.Fatalf("invalid %s: status %d body %s", tc.name, w.Code, w.Body.String()) + } + } +} + // TestListComments_RootsOnlyReturnsThreadStats pins the orientation metadata: // each root returned by roots_only carries reply_count (recursive descendant // count) and last_activity_at (MAX created_at over the subtree), so an agent diff --git a/server/internal/service/autopilot.go b/server/internal/service/autopilot.go index bb68b91dc..5b7de029d 100644 --- a/server/internal/service/autopilot.go +++ b/server/internal/service/autopilot.go @@ -1166,7 +1166,7 @@ func (s *AutopilotService) SyncRunFromIssue(ctx context.Context, issue db.Issue) // settles the run; do not treat "not yet dispatched" as success. return } - updatedRun, err := s.completeAutopilotRun(ctx, db.UpdateAutopilotRunCompletedParams{ + updatedRun, err := s.completeAutopilotRun(ctx, autopilotRunCompletion{ ID: run.ID, }) if err != nil { @@ -1177,7 +1177,7 @@ func (s *AutopilotService) SyncRunFromIssue(ctx context.Context, issue db.Issue) s.publishRunDone(wsID, updatedRun, "completed") case "cancelled": reason := "issue " + issue.Status - updatedRun, err := s.failAutopilotRun(ctx, db.UpdateAutopilotRunFailedParams{ + updatedRun, err := s.failAutopilotRun(ctx, autopilotRunFailure{ ID: run.ID, FailureReason: pgtype.Text{String: reason, Valid: true}, }) @@ -1209,7 +1209,7 @@ func (s *AutopilotService) SyncRunFromTask(ctx context.Context, task db.AgentTas switch task.Status { case "completed": - updatedRun, err := s.completeAutopilotRun(ctx, db.UpdateAutopilotRunCompletedParams{ + updatedRun, err := s.completeAutopilotRun(ctx, autopilotRunCompletion{ ID: run.ID, Result: task.Result, }) @@ -1221,7 +1221,7 @@ func (s *AutopilotService) SyncRunFromTask(ctx context.Context, task db.AgentTas s.publishRunDone(wsID, updatedRun, "completed") case "failed", "cancelled": reason := taskFailureReasonForAutopilotRun(task) - updatedRun, err := s.failAutopilotRun(ctx, db.UpdateAutopilotRunFailedParams{ + updatedRun, err := s.failAutopilotRun(ctx, autopilotRunFailure{ ID: run.ID, FailureReason: pgtype.Text{String: reason, Valid: reason != ""}, }) @@ -1311,7 +1311,7 @@ func (s *AutopilotService) settleRunFromDispatchTask(ctx context.Context, run db } reason := taskFailureReasonForAutopilotRun(task) - updatedRun, err := s.failAutopilotRun(ctx, db.UpdateAutopilotRunFailedParams{ + updatedRun, err := s.failAutopilotRun(ctx, autopilotRunFailure{ ID: run.ID, FailureReason: pgtype.Text{String: reason, Valid: reason != ""}, }) @@ -1336,7 +1336,7 @@ func (s *AutopilotService) completeRunFromLinkedIssueTask(ctx context.Context, r if err != nil { return } - updatedRun, err := s.completeAutopilotRun(ctx, db.UpdateAutopilotRunCompletedParams{ + updatedRun, err := s.completeAutopilotRun(ctx, autopilotRunCompletion{ ID: run.ID, }) if err != nil { @@ -1525,7 +1525,7 @@ func (s *AutopilotService) handleDispatchSkip(ctx context.Context, ap db.Autopil } func (s *AutopilotService) failRun(ctx context.Context, runID pgtype.UUID, reason string) { - if _, err := s.failAutopilotRun(ctx, db.UpdateAutopilotRunFailedParams{ + if _, err := s.failAutopilotRun(ctx, autopilotRunFailure{ ID: runID, FailureReason: pgtype.Text{String: reason, Valid: true}, ReasonCode: pgtype.Text{String: string(dispatch.ReasonInternalError), Valid: true}, diff --git a/server/internal/service/autopilot_quota.go b/server/internal/service/autopilot_quota.go index 8a547989e..e3310574e 100644 --- a/server/internal/service/autopilot_quota.go +++ b/server/internal/service/autopilot_quota.go @@ -245,7 +245,21 @@ func settleAutopilotQuota(ctx context.Context, q *db.Queries, reservationID pgty return err == nil, err } -func (s *AutopilotService) completeAutopilotRun(ctx context.Context, params db.UpdateAutopilotRunCompletedParams) (db.AutopilotRun, error) { +// autopilotRunCompletion and autopilotRunFailure carry the terminal inputs +// that UpdateAutopilotRunTerminalWithQuota settles atomically with the run's +// quota reservation. +type autopilotRunCompletion struct { + ID pgtype.UUID + Result []byte +} + +type autopilotRunFailure struct { + ID pgtype.UUID + FailureReason pgtype.Text + ReasonCode pgtype.Text +} + +func (s *AutopilotService) completeAutopilotRun(ctx context.Context, params autopilotRunCompletion) (db.AutopilotRun, error) { row, err := s.Queries.UpdateAutopilotRunTerminalWithQuota(ctx, db.UpdateAutopilotRunTerminalWithQuotaParams{ TerminalStatus: "completed", Result: params.Result, @@ -255,7 +269,7 @@ func (s *AutopilotService) completeAutopilotRun(ctx context.Context, params db.U return autopilotRunFromTerminalRow(row), err } -func (s *AutopilotService) failAutopilotRun(ctx context.Context, params db.UpdateAutopilotRunFailedParams) (db.AutopilotRun, error) { +func (s *AutopilotService) failAutopilotRun(ctx context.Context, params autopilotRunFailure) (db.AutopilotRun, error) { row, err := s.Queries.UpdateAutopilotRunTerminalWithQuota(ctx, db.UpdateAutopilotRunTerminalWithQuotaParams{ TerminalStatus: "failed", FailureReason: params.FailureReason, diff --git a/server/internal/service/autopilot_quota_test.go b/server/internal/service/autopilot_quota_test.go index a9803aad8..ed647da6c 100644 --- a/server/internal/service/autopilot_quota_test.go +++ b/server/internal/service/autopilot_quota_test.go @@ -707,7 +707,7 @@ func TestAutopilotQuotaObserveToEnforceAndOffFinalization(t *testing.T) { // under observe/enforce. Terminal finalization relies on the persisted slot. fixture.setPolicy(entitlement.ActionOff, fixture.periodStart, fixture.periodEnd, 1) for _, run := range runs { - if _, err := fixture.service.failAutopilotRun(ctx, db.UpdateAutopilotRunFailedParams{ + if _, err := fixture.service.failAutopilotRun(ctx, autopilotRunFailure{ ID: run.ID, FailureReason: pgtype.Text{String: "test cleanup", Valid: true}, }); err != nil { t.Fatalf("finalize while policy off: %v", err) @@ -738,7 +738,7 @@ func TestAutopilotQuotaConsumedCreateIssueCannotBeRefunded(t *testing.T) { if _, err := settleAutopilotQuota(ctx, fixture.queries, cancelled.QuotaReservationID, true); err != nil { t.Fatalf("consume cancelled run: %v", err) } - if _, err := fixture.service.failAutopilotRun(ctx, db.UpdateAutopilotRunFailedParams{ + if _, err := fixture.service.failAutopilotRun(ctx, autopilotRunFailure{ ID: cancelled.ID, FailureReason: pgtype.Text{String: "issue cancelled", Valid: true}, }); err != nil { t.Fatalf("fail consumed run: %v", err) @@ -777,7 +777,7 @@ func TestAutopilotQuotaTerminalUpdateNeedsNoTransactionStarter(t *testing.T) { t.Fatalf("create off-path run: %v", err) } serviceWithoutTx := &AutopilotService{Queries: fixture.queries} - updated, err := serviceWithoutTx.completeAutopilotRun(ctx, db.UpdateAutopilotRunCompletedParams{ID: run.ID}) + updated, err := serviceWithoutTx.completeAutopilotRun(ctx, autopilotRunCompletion{ID: run.ID}) if err != nil { t.Fatalf("complete off-path run: %v", err) } @@ -798,7 +798,7 @@ func TestAutopilotQuotaSettlementStaysInOriginalPeriod(t *testing.T) { secondStart := fixture.periodEnd secondEnd := secondStart.Add(19 * time.Hour) fixture.setPolicy(entitlement.ActionEnforce, secondStart, secondEnd, 1) - if _, err := fixture.service.completeAutopilotRun(ctx, db.UpdateAutopilotRunCompletedParams{ID: run.ID}); err != nil { + if _, err := fixture.service.completeAutopilotRun(ctx, autopilotRunCompletion{ID: run.ID}); err != nil { t.Fatalf("complete after period rollover: %v", err) } @@ -861,7 +861,9 @@ func TestAutopilotQuotaReconcilerSettlesTerminalOnlyOnce(t *testing.T) { } // Simulate a crash after the run reached terminal state but before the old // two-step finalizer settled its reservation. - if _, err := fixture.queries.UpdateAutopilotRunCompleted(ctx, db.UpdateAutopilotRunCompletedParams{ID: terminal.ID}); err != nil { + if _, err := fixture.pool.Exec(ctx, ` + UPDATE autopilot_run SET status = 'completed', completed_at = now() WHERE id = $1 + `, terminal.ID); err != nil { t.Fatalf("seed terminal crash window: %v", err) } diff --git a/server/pkg/db/generated/agent.sql.go b/server/pkg/db/generated/agent.sql.go index 140a2f1f2..72c5d4b56 100644 --- a/server/pkg/db/generated/agent.sql.go +++ b/server/pkg/db/generated/agent.sql.go @@ -6917,8 +6917,9 @@ type MarkAgentTaskWaitingLocalDirectoryParams struct { // alongside the status. // // The CHECK only allows the transition from 'dispatched' so a daemon can't -// mark an already-running or terminal task as waiting; the StartAgentTask -// mutation handles the reverse transition once the lock is acquired. +// mark an already-running or terminal task as waiting; the +// StartAgentTaskWithSupplement mutation handles the reverse transition once +// the lock is acquired. func (q *Queries) MarkAgentTaskWaitingLocalDirectory(ctx context.Context, arg MarkAgentTaskWaitingLocalDirectoryParams) (AgentTaskQueue, error) { row := q.db.QueryRow(ctx, markAgentTaskWaitingLocalDirectory, arg.ID, arg.WaitReason, arg.PrepareLeaseSecs) var i AgentTaskQueue @@ -8710,91 +8711,6 @@ func (q *Queries) SettleDelegatedFailureRecoveryComment(ctx context.Context, com return result.RowsAffected(), nil } -const startAgentTask = `-- name: StartAgentTask :one -UPDATE agent_task_queue -SET status = 'running', - started_at = now(), - wait_reason = NULL, - prepare_lease_expires_at = NULL -WHERE agent_task_queue.id = $1 AND agent_task_queue.status IN ('dispatched', 'waiting_local_directory') -RETURNING id, agent_id, issue_id, status, priority, dispatched_at, started_at, completed_at, result, error, created_at, context, runtime_id, session_id, work_dir, trigger_comment_id, chat_session_id, autopilot_run_id, attempt, max_attempts, parent_task_id, failure_reason, trigger_summary, force_fresh_session, is_leader_task, wait_reason, initiator_user_id, handoff_note, prepare_lease_expires_at, squad_id, runtime_mcp_overlay, escalation_for_task_id, fire_at, originator_user_id, runtime_connected_apps, coalesced_comment_ids, delivered_comment_ids, chat_input_task_id, chat_finalize_deferred_at, originator_source, delegated_from_task_id, retry_of_task_id, rerun_of_task_id, rule_version_id, trigger_evidence_kind, trigger_evidence_ref_id, accountable_user_id, session_rollout_missing, retired_session_id, quick_actions_disabled, regenerate_quick_actions_for, branch_name, durable_work_dir, channel_context_revision, comment_thread_id, cancelled_by_type, cancelled_by_id, cancelled_by_name, issue_snapshot, workflow_step -` - -// Legacy single-winner transition; generation-aware callers lock the claim first. -// Transitions a task to running. Accepts either 'dispatched' (the normal -// claim → run flow) or 'waiting_local_directory' (the daemon held the row in -// a wait state while another task owned the local_directory path lock; once -// the lock was acquired the daemon flips here). wait_reason is cleared on -// the transition so a future read can't conflate "currently waiting" with -// "previously waited". -func (q *Queries) StartAgentTask(ctx context.Context, id pgtype.UUID) (AgentTaskQueue, error) { - row := q.db.QueryRow(ctx, startAgentTask, id) - var i AgentTaskQueue - err := row.Scan( - &i.ID, - &i.AgentID, - &i.IssueID, - &i.Status, - &i.Priority, - &i.DispatchedAt, - &i.StartedAt, - &i.CompletedAt, - &i.Result, - &i.Error, - &i.CreatedAt, - &i.Context, - &i.RuntimeID, - &i.SessionID, - &i.WorkDir, - &i.TriggerCommentID, - &i.ChatSessionID, - &i.AutopilotRunID, - &i.Attempt, - &i.MaxAttempts, - &i.ParentTaskID, - &i.FailureReason, - &i.TriggerSummary, - &i.ForceFreshSession, - &i.IsLeaderTask, - &i.WaitReason, - &i.InitiatorUserID, - &i.HandoffNote, - &i.PrepareLeaseExpiresAt, - &i.SquadID, - &i.RuntimeMcpOverlay, - &i.EscalationForTaskID, - &i.FireAt, - &i.OriginatorUserID, - &i.RuntimeConnectedApps, - &i.CoalescedCommentIds, - &i.DeliveredCommentIds, - &i.ChatInputTaskID, - &i.ChatFinalizeDeferredAt, - &i.OriginatorSource, - &i.DelegatedFromTaskID, - &i.RetryOfTaskID, - &i.RerunOfTaskID, - &i.RuleVersionID, - &i.TriggerEvidenceKind, - &i.TriggerEvidenceRefID, - &i.AccountableUserID, - &i.SessionRolloutMissing, - &i.RetiredSessionID, - &i.QuickActionsDisabled, - &i.RegenerateQuickActionsFor, - &i.BranchName, - &i.DurableWorkDir, - &i.ChannelContextRevision, - &i.CommentThreadID, - &i.CancelledByType, - &i.CancelledByID, - &i.CancelledByName, - &i.IssueSnapshot, - &i.WorkflowStep, - ) - return i, err -} - const updateAgent = `-- name: UpdateAgent :one UPDATE agent SET name = COALESCE($2, name), diff --git a/server/pkg/db/generated/autopilot.sql.go b/server/pkg/db/generated/autopilot.sql.go index 72c3e5f94..726943208 100644 --- a/server/pkg/db/generated/autopilot.sql.go +++ b/server/pkg/db/generated/autopilot.sql.go @@ -2267,88 +2267,6 @@ func (q *Queries) UpdateAutopilotLastRunAt(ctx context.Context, id pgtype.UUID) return err } -const updateAutopilotRunCompleted = `-- name: UpdateAutopilotRunCompleted :one -UPDATE autopilot_run -SET status = 'completed', completed_at = now(), result = $2 -WHERE id = $1 -RETURNING id, autopilot_id, trigger_id, source, status, issue_id, task_id, triggered_at, completed_at, failure_reason, trigger_payload, result, created_at, squad_id, planned_at, webhook_delivery_id, quota_reservation_id, reason_code -` - -type UpdateAutopilotRunCompletedParams struct { - ID pgtype.UUID `json:"id"` - Result []byte `json:"result"` -} - -// Quota safety: only use for a run known to have no reservation. Normal -// terminal paths must call UpdateAutopilotRunTerminalWithQuota instead. -func (q *Queries) UpdateAutopilotRunCompleted(ctx context.Context, arg UpdateAutopilotRunCompletedParams) (AutopilotRun, error) { - row := q.db.QueryRow(ctx, updateAutopilotRunCompleted, arg.ID, arg.Result) - var i AutopilotRun - err := row.Scan( - &i.ID, - &i.AutopilotID, - &i.TriggerID, - &i.Source, - &i.Status, - &i.IssueID, - &i.TaskID, - &i.TriggeredAt, - &i.CompletedAt, - &i.FailureReason, - &i.TriggerPayload, - &i.Result, - &i.CreatedAt, - &i.SquadID, - &i.PlannedAt, - &i.WebhookDeliveryID, - &i.QuotaReservationID, - &i.ReasonCode, - ) - return i, err -} - -const updateAutopilotRunFailed = `-- name: UpdateAutopilotRunFailed :one -UPDATE autopilot_run -SET status = 'failed', completed_at = now(), failure_reason = $2, - reason_code = $3 -WHERE id = $1 -RETURNING id, autopilot_id, trigger_id, source, status, issue_id, task_id, triggered_at, completed_at, failure_reason, trigger_payload, result, created_at, squad_id, planned_at, webhook_delivery_id, quota_reservation_id, reason_code -` - -type UpdateAutopilotRunFailedParams struct { - ID pgtype.UUID `json:"id"` - FailureReason pgtype.Text `json:"failure_reason"` - ReasonCode pgtype.Text `json:"reason_code"` -} - -// Quota safety: only use for a run known to have no reservation. Normal -// terminal paths must call UpdateAutopilotRunTerminalWithQuota instead. -func (q *Queries) UpdateAutopilotRunFailed(ctx context.Context, arg UpdateAutopilotRunFailedParams) (AutopilotRun, error) { - row := q.db.QueryRow(ctx, updateAutopilotRunFailed, arg.ID, arg.FailureReason, arg.ReasonCode) - var i AutopilotRun - err := row.Scan( - &i.ID, - &i.AutopilotID, - &i.TriggerID, - &i.Source, - &i.Status, - &i.IssueID, - &i.TaskID, - &i.TriggeredAt, - &i.CompletedAt, - &i.FailureReason, - &i.TriggerPayload, - &i.Result, - &i.CreatedAt, - &i.SquadID, - &i.PlannedAt, - &i.WebhookDeliveryID, - &i.QuotaReservationID, - &i.ReasonCode, - ) - return i, err -} - const updateAutopilotRunIssueCreated = `-- name: UpdateAutopilotRunIssueCreated :one UPDATE autopilot_run SET status = 'issue_created', issue_id = $2 @@ -2473,50 +2391,6 @@ func (q *Queries) UpdateAutopilotRunSkipped(ctx context.Context, arg UpdateAutop return i, err } -const updateAutopilotRunSkippedWithResult = `-- name: UpdateAutopilotRunSkippedWithResult :one -UPDATE autopilot_run -SET status = 'skipped', - completed_at = now(), - failure_reason = $2, - result = $3 -WHERE id = $1 -RETURNING id, autopilot_id, trigger_id, source, status, issue_id, task_id, triggered_at, completed_at, failure_reason, trigger_payload, result, created_at, squad_id, planned_at, webhook_delivery_id, quota_reservation_id, reason_code -` - -type UpdateAutopilotRunSkippedWithResultParams struct { - ID pgtype.UUID `json:"id"` - FailureReason pgtype.Text `json:"failure_reason"` - Result []byte `json:"result"` -} - -// Quota safety: legacy no-reservation helper. New terminal paths must call -// UpdateAutopilotRunTerminalWithQuota instead. -func (q *Queries) UpdateAutopilotRunSkippedWithResult(ctx context.Context, arg UpdateAutopilotRunSkippedWithResultParams) (AutopilotRun, error) { - row := q.db.QueryRow(ctx, updateAutopilotRunSkippedWithResult, arg.ID, arg.FailureReason, arg.Result) - var i AutopilotRun - err := row.Scan( - &i.ID, - &i.AutopilotID, - &i.TriggerID, - &i.Source, - &i.Status, - &i.IssueID, - &i.TaskID, - &i.TriggeredAt, - &i.CompletedAt, - &i.FailureReason, - &i.TriggerPayload, - &i.Result, - &i.CreatedAt, - &i.SquadID, - &i.PlannedAt, - &i.WebhookDeliveryID, - &i.QuotaReservationID, - &i.ReasonCode, - ) - return i, err -} - const updateAutopilotRunTerminalWithQuota = `-- name: UpdateAutopilotRunTerminalWithQuota :one WITH updated_run AS ( UPDATE autopilot_run AS ar diff --git a/server/pkg/db/generated/channel.sql.go b/server/pkg/db/generated/channel.sql.go index f33620b7d..b8f7dc1a6 100644 --- a/server/pkg/db/generated/channel.sql.go +++ b/server/pkg/db/generated/channel.sql.go @@ -420,17 +420,6 @@ func (q *Queries) ClearChannelChatContextPendingFresh(ctx context.Context, arg C return err } -const clearChannelChatSessionPendingFresh = `-- name: ClearChannelChatSessionPendingFresh :exec -UPDATE channel_chat_session_binding -SET pending_fresh = FALSE -WHERE chat_session_id = $1 -` - -func (q *Queries) ClearChannelChatSessionPendingFresh(ctx context.Context, chatSessionID pgtype.UUID) error { - _, err := q.db.Exec(ctx, clearChannelChatSessionPendingFresh, chatSessionID) - return err -} - const clearChannelChatSessionPendingFreshForRevision = `-- name: ClearChannelChatSessionPendingFreshForRevision :exec UPDATE channel_chat_session_binding SET pending_fresh = FALSE @@ -2476,22 +2465,6 @@ func (q *Queries) LockChannelChatSessionBindingForContext(ctx context.Context, c return i, err } -const lockChannelChatSessionPendingFresh = `-- name: LockChannelChatSessionPendingFresh :one -SELECT pending_fresh FROM channel_chat_session_binding -WHERE chat_session_id = $1 -FOR UPDATE -` - -// EnqueueChatTask reads this under the same row lock and transaction that -// creates the task. A concurrent `/clear` therefore lands either before this -// task and is consumed by it, or after this task and remains for the next one. -func (q *Queries) LockChannelChatSessionPendingFresh(ctx context.Context, chatSessionID pgtype.UUID) (bool, error) { - row := q.db.QueryRow(ctx, lockChannelChatSessionPendingFresh, chatSessionID) - var pending_fresh bool - err := row.Scan(&pending_fresh) - return pending_fresh, err -} - const lockChannelInstallationAppIDSlot = `-- name: LockChannelInstallationAppIDSlot :exec SELECT pg_advisory_xact_lock( hashtext($1::text), @@ -2592,23 +2565,6 @@ func (q *Queries) LockCurrentChannelChatSessionBindingBySession(ctx context.Cont return i, err } -const markChannelChatSessionPendingFresh = `-- name: MarkChannelChatSessionPendingFresh :one -UPDATE channel_chat_session_binding -SET pending_fresh = TRUE -WHERE chat_session_id = $1 AND retired_at IS NULL -RETURNING pending_fresh -` - -// Persists a channel `/clear` intent until the next chat task is successfully -// created. RETURNING makes a missing binding an error instead of silently -// acknowledging a fresh start that was never stored. -func (q *Queries) MarkChannelChatSessionPendingFresh(ctx context.Context, chatSessionID pgtype.UUID) (bool, error) { - row := q.db.QueryRow(ctx, markChannelChatSessionPendingFresh, chatSessionID) - var pending_fresh bool - err := row.Scan(&pending_fresh) - return pending_fresh, err -} - const markChannelInboundDedupProcessed = `-- name: MarkChannelInboundDedupProcessed :execrows UPDATE channel_inbound_message_dedup SET processed_at = now() diff --git a/server/pkg/db/queries/agent.sql b/server/pkg/db/queries/agent.sql index 4f3bcdc48..16b9d2080 100644 --- a/server/pkg/db/queries/agent.sql +++ b/server/pkg/db/queries/agent.sql @@ -963,22 +963,6 @@ WHERE id = $1 AND started_at IS NULL RETURNING *; --- name: StartAgentTask :one --- Legacy single-winner transition; generation-aware callers lock the claim first. --- Transitions a task to running. Accepts either 'dispatched' (the normal --- claim → run flow) or 'waiting_local_directory' (the daemon held the row in --- a wait state while another task owned the local_directory path lock; once --- the lock was acquired the daemon flips here). wait_reason is cleared on --- the transition so a future read can't conflate "currently waiting" with --- "previously waited". -UPDATE agent_task_queue -SET status = 'running', - started_at = now(), - wait_reason = NULL, - prepare_lease_expires_at = NULL -WHERE agent_task_queue.id = $1 AND agent_task_queue.status IN ('dispatched', 'waiting_local_directory') -RETURNING *; - -- name: LockAgentTaskStartClaim :one -- Serialize start/replay with reclaim and cancellation. A stale delivery must -- never start or acknowledge a newer claim, even on the same runtime. @@ -995,8 +979,9 @@ FOR UPDATE; -- alongside the status. -- -- The CHECK only allows the transition from 'dispatched' so a daemon can't --- mark an already-running or terminal task as waiting; the StartAgentTask --- mutation handles the reverse transition once the lock is acquired. +-- mark an already-running or terminal task as waiting; the +-- StartAgentTaskWithSupplement mutation handles the reverse transition once +-- the lock is acquired. UPDATE agent_task_queue SET status = 'waiting_local_directory', wait_reason = $2, diff --git a/server/pkg/db/queries/autopilot.sql b/server/pkg/db/queries/autopilot.sql index f1c0bc2b3..cb10c2ec5 100644 --- a/server/pkg/db/queries/autopilot.sql +++ b/server/pkg/db/queries/autopilot.sql @@ -441,23 +441,6 @@ SET status = 'running', task_id = $2 WHERE id = $1 RETURNING *; --- name: UpdateAutopilotRunCompleted :one --- Quota safety: only use for a run known to have no reservation. Normal --- terminal paths must call UpdateAutopilotRunTerminalWithQuota instead. -UPDATE autopilot_run -SET status = 'completed', completed_at = now(), result = sqlc.narg('result') -WHERE id = $1 -RETURNING *; - --- name: UpdateAutopilotRunFailed :one --- Quota safety: only use for a run known to have no reservation. Normal --- terminal paths must call UpdateAutopilotRunTerminalWithQuota instead. -UPDATE autopilot_run -SET status = 'failed', completed_at = now(), failure_reason = $2, - reason_code = sqlc.narg('reason_code') -WHERE id = $1 -RETURNING *; - -- name: UpdateAutopilotRunSkipped :one -- Quota safety: this is only for pre-admission skipped rows, which never have -- reservations. Post-admission skips use UpdateAutopilotRunTerminalWithQuota. @@ -532,17 +515,6 @@ WITH updated_run AS ( ) SELECT * FROM updated_run; --- name: UpdateAutopilotRunSkippedWithResult :one --- Quota safety: legacy no-reservation helper. New terminal paths must call --- UpdateAutopilotRunTerminalWithQuota instead. -UPDATE autopilot_run -SET status = 'skipped', - completed_at = now(), - failure_reason = $2, - result = sqlc.narg('result') -WHERE id = $1 -RETURNING *; - -- ===================== -- Scheduler Queries -- ===================== diff --git a/server/pkg/db/queries/channel.sql b/server/pkg/db/queries/channel.sql index e7a7f4659..83078a404 100644 --- a/server/pkg/db/queries/channel.sql +++ b/server/pkg/db/queries/channel.sql @@ -791,23 +791,6 @@ SET last_message_id = sqlc.narg('last_message_id'), FROM current_route WHERE binding.id = current_route.id; --- name: MarkChannelChatSessionPendingFresh :one --- Persists a channel `/clear` intent until the next chat task is successfully --- created. RETURNING makes a missing binding an error instead of silently --- acknowledging a fresh start that was never stored. -UPDATE channel_chat_session_binding -SET pending_fresh = TRUE -WHERE chat_session_id = $1 AND retired_at IS NULL -RETURNING pending_fresh; - --- name: LockChannelChatSessionPendingFresh :one --- EnqueueChatTask reads this under the same row lock and transaction that --- creates the task. A concurrent `/clear` therefore lands either before this --- task and is consumed by it, or after this task and remains for the next one. -SELECT pending_fresh FROM channel_chat_session_binding -WHERE chat_session_id = $1 -FOR UPDATE; - -- name: LockChannelChatSessionBindingForContext :one -- Context mutations acquire this row after chat_session and before any -- channel_chat_context_generation row. Keeping the statements separate makes @@ -816,11 +799,6 @@ SELECT * FROM channel_chat_session_binding WHERE chat_session_id = $1 FOR UPDATE; --- name: ClearChannelChatSessionPendingFresh :exec -UPDATE channel_chat_session_binding -SET pending_fresh = FALSE -WHERE chat_session_id = $1; - -- name: ClearChannelChatSessionPendingFreshForRevision :exec UPDATE channel_chat_session_binding SET pending_fresh = FALSE diff --git a/server/pkg/protocol/messages.go b/server/pkg/protocol/messages.go index 147c2898f..a98b435d7 100644 --- a/server/pkg/protocol/messages.go +++ b/server/pkg/protocol/messages.go @@ -5,9 +5,6 @@ import "encoding/json" const ( DaemonCapabilitySkillBundlesV1 = "skill-bundles-v1" DaemonCapabilityCoalescedCommentsV1 = "coalesced-comments-v1" - DaemonCapabilityExecutionManifestV1 = "execution-manifest-v1" - DaemonCapabilityAgentSkillV1 = "agent-skill-v1" - DaemonCapabilityRemoteMCPV1 = "remote-mcp-v1" // DaemonCapabilityLocalWorktreeV1 advertises that the daemon implements // worktree mode for local_directory resources (execution_mode=worktree). //