Hold the three machine_id and expires_at indexes to having a reader

b5eb4ee268af · AtlantisPleb · · parent 7a58c2ffeaff

Hold the three machine_id and expires_at indexes to having a reader

Issue #184 named three indexes with no reader. Two were given one while it was
open, by lanes that closed other issues: `inference_grants_machine_id_index`
by the revocation sweep in #183, and `repository_machine_grants_machine_id_index`
by the per-computer grant listing in #182. Both readings are confirmed against
a run rather than a grep, and both verdicts are leave-as-wired.

Neither lane asserted that the planner reaches for the index, though — only
that a query exists. So a reader can narrow again and leave the index exactly
as #184 found it, with every test still green. That is the same rot this issue
was filed about, one step further along.

The plan is read from the SQL each production function emits, captured off
`Ecto.Repo` query telemetry while the real function runs, rather than from a
predicate written in the test to resemble it. A copy would keep passing while
the call site changed underneath it. The telemetry prefix is derived from
`Repo.config/0` for the same reason: the guessed `[:openagents, :repo]` never
fires, and a handler attached to an event that never fires is a test that
cannot fail.

Sequential scans are priced out with `SET LOCAL enable_seqscan = off`, because
a cold table is small enough that a sequential scan is genuinely cheaper; the
question asked is which index serves the predicate, not whether the planner
bothers yet. `SET LOCAL` dies with the sandbox transaction rather than
following the connection back into the pool.

Mutations, each aimed at a different way the reader can disappear: dropping
`machine_id` from `revoke_active_for_machine/1`'s filter turns the captured
plan into `Seq Scan on inference_grants`; adding a `repository_id` filter to
`list_machine_grants/2` moves it onto the composite index and trips the
matching refute; dropping `inference_grants_machine_id_index` from the database
fails both this proof and CANON-002's ledger.

Refs #184.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DTmy4SEXrHXouw5sZbs3f4
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>

Deploy story

What this commit did to the running system — joined from the forge receipt chain, the part a commit page elsewhere cannot show.

pushed
by user · WAL seq 278 · 2026-08-24T04:46:55.682559Z

Changed files

  • modified INVARIANTS.md
  • added test/openagents/machines/index_reach_test.exs

Diff

2 files changed, +115 -2

INVARIANTS.md modified +2 -2

@@ -4678,10 +4678,10 @@ contract; the invariant prose above defines the assertion, not the filename.

4678 4678
| IDENTITY-005 | `test/openagents_web/controllers/box_controller_test.exs` |
4679 4679
| IDENTITY-006 | `test/openagents/forge/assignment_test.exs` |
4680 4680
| IDENTITY-007 | `test/openagents/agents_test.exs` |
4681
| IDENTITY-008 | `test/openagents_web/controllers/computer_control_api_test.exs`, `test/openagents/inference/computer_revocation_test.exs`, `test/openagents/computer_projection_test.exs` |
4681
| IDENTITY-008 | `test/openagents_web/controllers/computer_control_api_test.exs`, `test/openagents/inference/computer_revocation_test.exs`, `test/openagents/computer_projection_test.exs`, `test/openagents/machines/index_reach_test.exs` |
4682 4682
| IDENTITY-009 | `test/openagents_web/controllers/delegations_controller_test.exs` |
4683 4683
| IDENTITY-010 | `test/openagents/forge/assignment_test.exs`, `test/openagents/forge/assignment_credential_reach_test.exs` |
4684
| IDENTITY-011 | `test/openagents/machines/pairing_expiry_test.exs` |
4684
| IDENTITY-011 | `test/openagents/machines/pairing_expiry_test.exs`, `test/openagents/machines/index_reach_test.exs` |
4685 4685
| IDENTITY-012 | `test/openagents/machines/constraint_reach_test.exs` |
4686 4686
| CAPACITY-002 | `test/openagents/box_fanout_test.exs` |
4687 4687
| CAPACITY-003 | `test/openagents/box_reconciler_test.exs` |
test/openagents/machines/index_reach_test.exs added +113

@@ -0,0 +1,113 @@

1
defmodule OpenAgents.Machines.IndexReachTest do
2
  @moduledoc """
3
  Issue #184 named three indexes with no reader. Two gained one while it was
4
  open — `inference_grants_machine_id_index` from the revocation sweep in #183,
5
  and `repository_machine_grants_machine_id_index` from the per-computer grant
6
  listing in #182 — and `machine_pairings_expires_at_index` gained one here.
7
8
  Neither of those lanes asserted that the planner reaches for the index, only
9
  that a query exists, so a reader could narrow again and leave the index dead
10
  exactly as it was found. This is the standing check that it has not.
11
12
  The plan is read from the SQL the production function emits, captured off
13
  `Ecto.Repo` telemetry while it runs, rather than from a predicate written
14
  here to look like it. A copy would keep passing while the call site changed
15
  underneath it, which is the failure this file exists to prevent.
16
  """
17
18
  use OpenAgents.DataCase, async: false
19
20
  alias OpenAgents.Accounts.User
21
  alias OpenAgents.Inference
22
  alias OpenAgents.Repo
23
  alias OpenAgents.Repositories
24
25
  # A cold table is small enough that a sequential scan is genuinely the cheaper
26
  # plan, so the planner is asked which index serves the predicate, not whether
27
  # it is worth using yet. `SET LOCAL` dies with the sandbox's transaction and
28
  # cannot follow this connection back into the pool.
29
  defp plan_of(matching, run) do
30
    handler = {__MODULE__, System.unique_integer()}
31
    test = self()
32
33
    # Derived, not spelled: the prefix is `[:open_agents, :repo]`, and a
34
    # hardcoded guess silently attaches to an event that never fires.
35
    event = Repo.config() |> Keyword.fetch!(:telemetry_prefix) |> Kernel.++([:query])
36
37
    :telemetry.attach(
38
      handler,
39
      event,
40
      fn _event, _measure, %{query: query, params: params}, _config ->
41
        if String.contains?(query, matching), do: send(test, {:sql, query, params})
42
      end,
43
      nil
44
    )
45
46
    try do
47
      run.()
48
    after
49
      :telemetry.detach(handler)
50
    end
51
52
    receive do
53
      {:sql, sql, params} ->
54
        Repo.query!("SET LOCAL enable_seqscan = off")
55
        Repo.query!("EXPLAIN " <> sql, params).rows |> Enum.map_join("\n", &hd/1)
56
    after
57
      0 -> flunk("no query matching #{inspect(matching)} was emitted")
58
    end
59
  end
60
61
  test "the computer revocation sweep is served by inference_grants_machine_id_index" do
62
    plan =
63
      plan_of("inference_grants", fn ->
64
        Inference.revoke_active_for_machine(Ecto.UUID.generate())
65
      end)
66
67
    assert plan =~ "inference_grants_machine_id_index", plan
68
  end
69
70
  test "the per-computer grant listing is served by repository_machine_grants_machine_id_index" do
71
    owner = owner()
72
    machine = computer(owner)
73
74
    plan =
75
      plan_of("repository_machine_grants", fn ->
76
        Repositories.list_machine_grants(owner, machine.id)
77
      end)
78
79
    # The composite unique index leads on `repository_id`, which this filter
80
    # does not name, so it cannot serve here — which is why the single-column
81
    # index stopped being redundant the moment the listing landed.
82
    assert plan =~ "repository_machine_grants_machine_id_index", plan
83
    refute plan =~ "repository_machine_grants_repository_id_machine_id_index"
84
  end
85
86
  test "the pairing sweep is served by machine_pairings_expires_at_index" do
87
    plan =
88
      plan_of("machine_pairings", fn ->
89
        OpenAgents.Machines.expire_elapsed_pairings()
90
      end)
91
92
    assert plan =~ "machine_pairings_expires_at_index", plan
93
  end
94
95
  defp owner do
96
    {:ok, %User{} = user} =
97
      OpenAgents.Accounts.upsert_github_user(%{
98
        github_id: :erlang.phash2({__MODULE__, System.unique_integer()}),
99
        github_login: "index-reach-#{System.unique_integer([:positive])}",
100
        github_avatar_url: "https://avatars.githubusercontent.com/u/1?v=4"
101
      })
102
103
    user
104
  end
105
106
  defp computer(owner) do
107
    {:ok, %{code: code}} =
108
      OpenAgents.Machines.start_pairing(%{"name" => "box", "tier" => "probe"})
109
110
    {:ok, machine} = OpenAgents.Machines.approve_pairing(owner, code)
111
    machine
112
  end
113
end

This page updates live while a promote is in flight · changelog