-- Migration: 008_machine_uid.sql
-- Purpose: Give connect_machines a deterministic, recomputable hardware identity
--          (machine_uid) and dedup registrations on it (SPEC-004 / v2-stable-identity
--          Task 2).
--
--   Today connect_machines is keyed only on `agent_id` — a random UUID the agent
--   persists in its config (generate_agent_id()). A lost/missing config produces a
--   fresh UUID, and because upsert_machine dedups `ON CONFLICT (agent_id)`, that
--   fresh id inserts a NEW row: the duplicate-registration bug (15 rows for 5 real
--   hosts in production). The agent (Task 1) now derives a stable `machine_uid` from
--   durable hardware/OS identifiers (Windows MachineGuid, hashed; recomputable) and
--   reports it on the connect handshake and on AgentStatus. This migration persists
--   it and adds the uniqueness needed to dedup the un-keyed / shared-key / config-loss
--   fleet on that stable identity.
--
--   SECURITY (SPEC-004): a client-asserted machine_uid is spoofable and is therefore
--   NOT a trust boundary. For per-agent (`cak_`) keyed agents the key's machine
--   binding stays authoritative (the server dedups those on their authenticated
--   agent_id, never on a claimed uid). machine_uid is a *correctness* aid for the
--   un-keyed path only. The UNIQUE index below enforces "one row per machine_uid"
--   at the schema level so a concurrent/racing un-keyed insert cannot create a
--   duplicate behind the application-level ON CONFLICT.
--
-- Idempotent: ADD COLUMN IF NOT EXISTS + CREATE UNIQUE INDEX IF NOT EXISTS. The
-- column is NULLABLE: legacy rows and agents that do not report a uid (older agents,
-- some support-code clients) carry NULL, and the partial index excludes NULLs so any
-- number of un-keyed rows may coexist without a uid. Applied on server startup by
-- sqlx::migrate!(); never pre-applied via psql. Ordered after 007.
-- See .claude/standards/gururmm/sqlx-migrations.md.

-- 1. machine_uid: deterministic hardware identity reported by the agent. NULLABLE
--    so legacy rows and non-reporting agents are unaffected.
ALTER TABLE connect_machines ADD COLUMN IF NOT EXISTS machine_uid TEXT;

-- 2. Enforce one row per machine_uid, but ONLY for rows that actually have one.
--    A partial UNIQUE index (WHERE machine_uid IS NOT NULL) lets unlimited legacy
--    NULL rows coexist while making a non-null machine_uid a true dedup key — this
--    is what upsert_machine's `ON CONFLICT (machine_uid)` arbiter binds to.
CREATE UNIQUE INDEX IF NOT EXISTS idx_connect_machines_machine_uid
    ON connect_machines (machine_uid)
    WHERE machine_uid IS NOT NULL;
