Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -1043,6 +1043,15 @@ MANAGED_SOFTWARE_POLICY_MODE=compat
# unattended until a later release even when true.
# BREEZE_AI_AGENTS_ENABLED=false

# AI Operator task delegation (#5205). Both default false — decision D2 keeps
# the feature to internal and test orgs until it graduates. TASKS_ENABLED gates
# the admission route POST /ai/operator/tasks and the "Delegate to Operator"
# action; the RECIPE flag additionally gates the one recipe that exists
# (service recovery). With either off, admission answers 422 with an
# actionable reason and the web action does not render.
# AI_OPERATOR_TASKS_ENABLED=false
# AI_OPERATOR_RECIPE_SERVICE_RECOVERY_ENABLED=false

# Wave 5 Part B (#3827) sub-flag of BREEZE_AI_AGENTS_ENABLED above. Gates
# attemptPolicyDecision: an agent-originated, supervised-scope action-intent
# whose operation is in the operator's per-agent actAssets.supervisedActionKeys
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
-- AI Operator: admission idempotency key (#5205 W08, #5246), spec §12.
--
-- `POST /api/v1/ai/operator/tasks` carries a client idempotency key. A
-- duplicate POST must return the SAME task id and must never admit a second
-- task, because each task can dispatch a real service-restart command to a
-- customer machine — a second admission is a duplicate remediation effect.
--
-- The guarantee is a PARTIAL UNIQUE INDEX rather than a route-level
-- read-then-insert: two concurrent clicks (or a client retry racing its own
-- first request) both pass a SELECT and both insert. Only the database can
-- serialize them.
--
-- Partial on `client_idempotency_key IS NOT NULL` so every pre-existing row
-- and every internally-admitted task (the coordinator's successors, recovery
-- scans) keeps a NULL and never collides. Scoped by `org_id` so one tenant can
-- neither probe nor squat another tenant's keys.
--
-- No RLS change: `ai_operator_tasks` is tenancy shape 1 (direct NOT NULL
-- `org_id`) and its policies are column-agnostic. The new column IS registered
-- in CORE_TENANT_EXPORT_POLICY in the same PR — adding a column to an
-- org-cascade table breaks that contract test, which is exactly the point.
--
-- No DML in this file, so no `breeze.scope` elevation is required.

ALTER TABLE ai_operator_tasks
ADD COLUMN IF NOT EXISTS client_idempotency_key text;

CREATE UNIQUE INDEX IF NOT EXISTS ai_operator_tasks_client_idempotency_uq
ON ai_operator_tasks (org_id, client_idempotency_key)
WHERE client_idempotency_key IS NOT NULL;
Loading
Loading