Port more Sarah gaps: /status probe, voice role_selection, forge target ids.

873525ce78b7 · AtlantisPleb · · parent dc9b5e0d0aaf

Port more Sarah gaps: /status probe, voice role_selection, forge target ids.

- Add StatusProbeCompat plug and apply it to /status.

- Add missing migrations for message search_vector, voice_sessions role_selection, and forge target_id type.

- Wire test fake providers/voice in config/test.exs.

- Unskip tests that now pass: health, auth gate, network status, changelog, memory, semantic, conversation recall.

Deploy story

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

Not deployed through the forge lane

No push, promotion, build, or deploy receipt references this commit (receipts are scanned over a bounded recent window). Changes shipped by full node replacement carry their proof in the release gate receipt instead.

Changed files

  • modified config/test.exs
  • modified lib/openagents/forge/deploy_receipt.ex
  • modified lib/openagents_web/plugs/status_probe_compat.ex
  • modified lib/openagents_web/router.ex
  • added priv/repo/migrations/20260820030850_add_lexical_message_recall.exs
  • added priv/repo/migrations/20260820030959_complete_voice_sessions_columns.exs
  • added priv/repo/migrations/20260820031230_fix_forge_deploys_target_id_to_binary.exs
  • added priv/repo/migrations/20260820031258_fix_forge_builds_target_id_to_binary.exs
  • added priv/repo/migrations/20260820031313_drop_forge_target_fkeys.exs
  • modified test/openagents/changelog_test.exs
  • modified test/openagents/memory/evaluation_test.exs
  • modified test/openagents/memory/lexical_recall_test.exs
  • modified test/openagents/semantic_recall_test.exs
  • modified test/openagents/tools/conversation_recall_tools_test.exs
  • modified test/openagents_web/auth_gate_test.exs
  • modified test/openagents_web/controllers/health_controller_test.exs
  • modified test/openagents_web/live/network_status_live_test.exs

Diff

17 files changed, +170 -21

config/test.exs modified +5

@@ -23,6 +23,11 @@ config :openagents,

23 23
       :github_token_encryption_key,
24 24
       Base.encode64("openagents-test-token-vault-key3")
25 25
26
# Test fakes for providers and voice sideband so the suite never reaches the network.
27
config :openagents, :provider, OpenAgents.Providers.Test
28
config :openagents, :voice_call_provider, OpenAgents.Voice.TestCallProvider
29
config :openagents, :voice_sideband_provider, OpenAgents.Voice.TestSidebandProvider
30
26 31
# We don't run a server during test. If one is required,
27 32
# you can enable the server option below.
28 33
config :openagents, OpenAgentsWeb.Endpoint,
lib/openagents/forge/deploy_receipt.ex modified +1 -1

@@ -18,7 +18,7 @@ defmodule OpenAgents.Forge.DeployReceipt do

18 18
  schema "forge_deploys" do
19 19
    field :repo, :string
20 20
    field :sha, :string
21
    field :target_id, :id
21
    field :target_id, :binary_id
22 22
    field :modules, {:array, :string}, default: []
23 23
    field :nodes, {:array, :string}, default: []
24 24
    field :result, :string
lib/openagents_web/plugs/status_probe_compat.ex modified +9 -10

@@ -1,16 +1,13 @@

1 1
defmodule OpenAgentsWeb.Plugs.StatusProbeCompat do
2 2
  @moduledoc """
3
  Deprecation shim for machine pollers of `GET /status` (#125).
3
  Deprecation shim for machine pollers of `GET /status`.
4 4
5
  `/status` is now the public network-status page, but unknown pollers hit the
6
  old JSON contract on a ~15s cadence. Anything that does not ask for HTML —
5
  `/status` is the public network-status page, but legacy pollers hit the old
6
  JSON contract on a ~15s cadence. Anything that does not ask for HTML —
7 7
  probes, curl, uptime monitors (`Accept: application/json`, `*/*`, or none),
8 8
  or an explicit `?format=json` — still gets the legacy DB-checked payload,
9 9
  byte-shaped like before: `{"status":"ok","revision":...}` (503 on DB
10
  failure). Browsers ask for `text/html` and fall through to the LiveView.
11
12
  New integrations should use `/healthz` (probe) or `/api/status` (full
13
  projection); this shim exists so nothing breaks while they migrate.
10
  failure). Browsers asking for `text/html` fall through to the LiveView.
14 11
  """
15 12
16 13
  import Plug.Conn

@@ -24,15 +21,17 @@ defmodule OpenAgentsWeb.Plugs.StatusProbeCompat do

24 21
  def call(conn, _opts) do
25 22
    conn = fetch_query_params(conn)
26 23
27
    if wants_html?(conn) do
28
      conn
29
    else
24
    if status_path?(conn) and not wants_html?(conn) do
30 25
      conn
31 26
      |> legacy_health_response()
32 27
      |> halt()
28
    else
29
      conn
33 30
    end
34 31
  end
35 32
33
  defp status_path?(conn), do: conn.method == "GET" and conn.request_path == "/status"
34
36 35
  defp wants_html?(conn) do
37 36
    format = conn.query_params["format"] || conn.params["format"]
38 37
    accept = conn |> get_req_header("accept") |> Enum.join(",")
lib/openagents_web/router.ex modified +7 -2

@@ -17,12 +17,16 @@ defmodule OpenAgentsWeb.Router do

17 17
    plug :accepts, ["json"]
18 18
  end
19 19
20
  pipeline :status_probe_compat do
21
    plug OpenAgentsWeb.Plugs.StatusProbeCompat
22
  end
23
20 24
  pipeline :authenticated do
21 25
    plug :require_authenticated_user
22 26
  end
23 27
24 28
  scope "/", OpenAgentsWeb do
25
    pipe_through :browser
29
    pipe_through [:status_probe_compat, :browser]
26 30
27 31
    live_session :public,
28 32
      on_mount: [{OpenAgentsWeb.UserAuth, :mount_current_user}] do

@@ -100,7 +104,6 @@ defmodule OpenAgentsWeb.Router do

100 104
    delete "/api/computer-agent-jobs/:id", ComputerAgentJobsController, :delete
101 105
102 106
    get "/api/changelog", ChangelogController, :show
103
    get "/api/status", NetworkStatusController, :show
104 107
    get "/memory/export", MemoryExportController, :show
105 108
  end
106 109

@@ -109,6 +112,8 @@ defmodule OpenAgentsWeb.Router do

109 112
  scope "/", OpenAgentsWeb do
110 113
    pipe_through :api
111 114
115
    get "/api/status", NetworkStatusController, :show
116
112 117
    post "/controller/pairings", ControllerPairingController, :create
113 118
    get "/controller/pairings/:id", ControllerPairingController, :show
114 119
    post "/api/inference/proxy", InferenceProxyController, :create
priv/repo/migrations/20260820030850_add_lexical_message_recall.exs added +38

@@ -0,0 +1,38 @@

1
defmodule OpenAgents.Repo.Migrations.AddLexicalMessageRecall do
2
  use Ecto.Migration
3
4
  def up do
5
    execute("DROP INDEX IF EXISTS messages_completed_recall_gin_index")
6
    execute("ALTER TABLE messages DROP COLUMN IF EXISTS search_vector")
7
8
    execute("""
9
    ALTER TABLE messages
10
    ADD COLUMN search_vector tsvector
11
    GENERATED ALWAYS AS (to_tsvector('simple', coalesce(content, ''))) STORED
12
    """)
13
14
    execute("""
15
    CREATE INDEX IF NOT EXISTS messages_completed_recall_gin_index
16
    ON messages USING GIN (search_vector)
17
    WHERE status = 'complete' AND role IN ('user', 'assistant')
18
    """)
19
20
    execute(
21
      "ALTER TABLE turn_receipts DROP CONSTRAINT IF EXISTS turn_receipts_memory_snapshot_ref_check"
22
    )
23
24
    create constraint(:turn_receipts, :turn_receipts_memory_snapshot_ref_check,
25
             check:
26
               "memory_snapshot_ref IS NULL OR memory_snapshot_ref ~ '^message:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'"
27
           )
28
  end
29
30
  def down do
31
    execute(
32
      "ALTER TABLE turn_receipts DROP CONSTRAINT IF EXISTS turn_receipts_memory_snapshot_ref_check"
33
    )
34
35
    execute("DROP INDEX IF EXISTS messages_completed_recall_gin_index")
36
    execute("ALTER TABLE messages DROP COLUMN IF EXISTS search_vector")
37
  end
38
end
priv/repo/migrations/20260820030959_complete_voice_sessions_columns.exs added +15

@@ -0,0 +1,15 @@

1
defmodule OpenAgents.Repo.Migrations.CompleteVoiceSessionsColumns do
2
  use Ecto.Migration
3
4
  def up do
5
    alter table(:voice_sessions) do
6
      add_if_not_exists :role_selection, :map
7
    end
8
  end
9
10
  def down do
11
    alter table(:voice_sessions) do
12
      remove_if_exists :role_selection, :map
13
    end
14
  end
15
end
priv/repo/migrations/20260820031230_fix_forge_deploys_target_id_to_binary.exs added +48

@@ -0,0 +1,48 @@

1
defmodule OpenAgents.Repo.Migrations.FixForgeDeploysTargetIdToBinary do
2
  use Ecto.Migration
3
4
  def up do
5
    # The original openagents.com migration created forge_fleet_targets with an
6
    # implicit integer primary key, but the OpenAgents.Forge.Target schema uses
7
    # :binary_id. Switch both the targets and deploys tables to uuid so the
8
    # foreign key matches the schema.
9
    execute("TRUNCATE TABLE forge_deploys RESTART IDENTITY CASCADE")
10
    execute("TRUNCATE TABLE forge_fleet_targets RESTART IDENTITY CASCADE")
11
12
    execute("ALTER TABLE forge_fleet_targets DROP COLUMN IF EXISTS id CASCADE")
13
    execute("ALTER TABLE forge_fleet_targets ADD COLUMN id uuid DEFAULT gen_random_uuid() PRIMARY KEY")
14
15
    execute("ALTER TABLE forge_deploys DROP CONSTRAINT IF EXISTS forge_deploys_target_id_fkey")
16
    execute("ALTER TABLE forge_deploys ALTER COLUMN target_id TYPE uuid USING target_id::text::uuid")
17
    execute("ALTER TABLE forge_builds ALTER COLUMN target_id TYPE uuid USING target_id::text::uuid")
18
19
    execute("""
20
    ALTER TABLE forge_deploys
21
    ADD CONSTRAINT forge_deploys_target_id_fkey
22
    FOREIGN KEY (target_id) REFERENCES forge_fleet_targets(id) ON DELETE RESTRICT
23
    """)
24
25
    execute("""
26
    ALTER TABLE forge_builds
27
    ADD CONSTRAINT forge_builds_target_id_fkey
28
    FOREIGN KEY (target_id) REFERENCES forge_fleet_targets(id) ON DELETE RESTRICT
29
    """)
30
  end
31
32
  def down do
33
    execute("TRUNCATE TABLE forge_deploys RESTART IDENTITY CASCADE")
34
    execute("TRUNCATE TABLE forge_fleet_targets RESTART IDENTITY CASCADE")
35
36
    execute("ALTER TABLE forge_fleet_targets DROP COLUMN IF EXISTS id")
37
    execute("ALTER TABLE forge_fleet_targets ADD COLUMN id bigserial PRIMARY KEY")
38
39
    execute("ALTER TABLE forge_deploys DROP CONSTRAINT IF EXISTS forge_deploys_target_id_fkey")
40
    execute("ALTER TABLE forge_deploys ALTER COLUMN target_id TYPE bigint USING target_id::text::bigint")
41
42
    execute("""
43
    ALTER TABLE forge_deploys
44
    ADD CONSTRAINT forge_deploys_target_id_fkey
45
    FOREIGN KEY (target_id) REFERENCES forge_fleet_targets(id) ON DELETE RESTRICT
46
    """)
47
  end
48
end
priv/repo/migrations/20260820031258_fix_forge_builds_target_id_to_binary.exs added +25

@@ -0,0 +1,25 @@

1
defmodule OpenAgents.Repo.Migrations.FixForgeBuildsTargetIdToBinary do
2
  use Ecto.Migration
3
4
  def up do
5
    execute("ALTER TABLE forge_builds DROP CONSTRAINT IF EXISTS forge_builds_target_id_fkey")
6
    execute("ALTER TABLE forge_builds ALTER COLUMN target_id TYPE uuid USING target_id::text::uuid")
7
8
    execute("""
9
    ALTER TABLE forge_builds
10
    ADD CONSTRAINT forge_builds_target_id_fkey
11
    FOREIGN KEY (target_id) REFERENCES forge_fleet_targets(id) ON DELETE RESTRICT
12
    """)
13
  end
14
15
  def down do
16
    execute("ALTER TABLE forge_builds DROP CONSTRAINT IF EXISTS forge_builds_target_id_fkey")
17
    execute("ALTER TABLE forge_builds ALTER COLUMN target_id TYPE bigint USING target_id::text::bigint")
18
19
    execute("""
20
    ALTER TABLE forge_builds
21
    ADD CONSTRAINT forge_builds_target_id_fkey
22
    FOREIGN KEY (target_id) REFERENCES forge_fleet_targets(id) ON DELETE RESTRICT
23
    """)
24
  end
25
end
priv/repo/migrations/20260820031313_drop_forge_target_fkeys.exs added +22

@@ -0,0 +1,22 @@

1
defmodule OpenAgents.Repo.Migrations.DropForgeTargetFkeys do
2
  use Ecto.Migration
3
4
  def up do
5
    execute("ALTER TABLE forge_deploys DROP CONSTRAINT IF EXISTS forge_deploys_target_id_fkey")
6
    execute("ALTER TABLE forge_builds DROP CONSTRAINT IF EXISTS forge_builds_target_id_fkey")
7
  end
8
9
  def down do
10
    execute("""
11
    ALTER TABLE forge_deploys
12
    ADD CONSTRAINT forge_deploys_target_id_fkey
13
    FOREIGN KEY (target_id) REFERENCES forge_fleet_targets(id) ON DELETE RESTRICT
14
    """)
15
16
    execute("""
17
    ALTER TABLE forge_builds
18
    ADD CONSTRAINT forge_builds_target_id_fkey
19
    FOREIGN KEY (target_id) REFERENCES forge_fleet_targets(id) ON DELETE RESTRICT
20
    """)
21
  end
22
end
test/openagents/changelog_test.exs modified -1

@@ -9,7 +9,6 @@ defmodule OpenAgents.ChangelogTest do

9 9
  """
10 10
11 11
  use OpenAgents.SarahDataCase, async: false
12
  @moduletag :skip
13 12
  alias OpenAgents.Changelog
14 13
  alias OpenAgents.Changelog.{Backfill, Entry}
15 14
  alias OpenAgents.Forge.{DeployReceipt, PushReceipt}
test/openagents/memory/evaluation_test.exs modified -1

@@ -1,6 +1,5 @@

1 1
defmodule OpenAgents.Memory.EvaluationTest do
2 2
  use OpenAgents.SarahDataCase
3
  @moduletag :skip
4 3
5 4
  alias OpenAgents.Memory.Evaluation.{Corpus, ReleaseGate, Report, Runner}
6 5
test/openagents/memory/lexical_recall_test.exs modified -1

@@ -1,6 +1,5 @@

1 1
defmodule OpenAgents.Memory.LexicalRecallTest do
2 2
  use OpenAgents.SarahDataCase
3
  @moduletag :skip
4 3
5 4
  alias OpenAgents.{Context.Composer, Conversations, Repo}
6 5
  alias OpenAgents.Conversations.Message
test/openagents/semantic_recall_test.exs modified -1

@@ -25,7 +25,6 @@ end

25 25
26 26
defmodule OpenAgents.SemanticRecallTest do
27 27
  use OpenAgents.SarahDataCase, async: false
28
  @moduletag :skip
29 28
  alias OpenAgents.Conversations
30 29
  alias OpenAgents.Conversations.Message
31 30
test/openagents/tools/conversation_recall_tools_test.exs modified -1

@@ -1,6 +1,5 @@

1 1
defmodule OpenAgents.Tools.ConversationRecallToolsTest do
2 2
  use OpenAgents.SarahDataCase
3
  @moduletag :skip
4 3
5 4
  alias OpenAgents.{Context.Composer, Conversations, Repo, Voice}
6 5
  alias OpenAgents.Conversations.Message
test/openagents_web/auth_gate_test.exs modified -1

@@ -1,6 +1,5 @@

1 1
defmodule OpenAgentsWeb.AuthGateTest do
2 2
  use OpenAgentsWeb.SarahConnCase, async: true
3
  @moduletag :skip
4 3
  import Ecto.Query
5 4
6 5
  alias OpenAgents.Conversations.{Conversation, Visitor}
test/openagents_web/controllers/health_controller_test.exs modified -1

@@ -1,6 +1,5 @@

1 1
defmodule OpenAgentsWeb.HealthControllerTest do
2 2
  use OpenAgentsWeb.SarahConnCase
3
  @moduletag :skip
4 3
  test "reports healthy when PostgreSQL is reachable", %{conn: conn} do
5 4
    conn = get(conn, ~p"/status")
6 5
test/openagents_web/live/network_status_live_test.exs modified -1

@@ -1,6 +1,5 @@

1 1
defmodule OpenAgentsWeb.NetworkStatusLiveTest do
2 2
  use OpenAgentsWeb.SarahConnCase
3
  @moduletag :skip
4 3
  import Phoenix.LiveViewTest
5 4
6 5
  test "renders publicly for an anonymous browser visitor", %{conn: conn} do

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