Keep every compiled module packageable by the build lane

edccc45b8d74 · Devin AI · · parent 6c086d4a625f

Keep every compiled module packageable by the build lane

The forge build failed with invalid_module_name because two modules on
main fall outside the artifact allowlist: Mix.Tasks.Forum.Import and
the derived Jason.Encoder implementation for
OpenAgents.Deployments.Execution.

Rename the import task to Mix.Tasks.Openagents.Forum.Import so it sits
in the allowlisted Mix task namespace, and admit protocol
implementations for application structs (Inspect, Jason.Encoder, and
String.Chars) in the artifact module pattern.

Add a regression test that runs BuildArtifact.beam_module/1 over every
compiled application beam, so a module outside the deployable
namespaces fails precommit instead of failing the forge build after
promotion.

Co-Authored-By: Christopher David <chris@openagents.com>
Co-Authored-By
Christopher David <chris@openagents.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.

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 docs/evidence/forum-port-migration.md
  • modified docs/forum-port.md
  • modified docs/taxonomy.md
  • renamed lib/mix/tasks/openagents.forum.import.ex
  • modified lib/openagents/forge/build_artifact.ex
  • added test/openagents/forge/build_artifact_namespace_test.exs
  • modified test/openagents/forge/build_artifact_test.exs

Diff

7 files changed, +34 -8

docs/evidence/forum-port-migration.md modified +1 -1

@@ -6,7 +6,7 @@ Source of truth for the one-time import of the legacy Effect forum into the

6 6
Phoenix application database. Run with:
7 7
8 8
```sh
9
FORUM_IMPORT_PASSWORD=... mix forum.import
9
FORUM_IMPORT_PASSWORD=... mix openagents.forum.import
10 10
```
11 11
12 12
## Results
docs/forum-port.md modified +1 -1

@@ -12,7 +12,7 @@ The legacy forum was a TanStack application backed by the Effect stack, with

12 12
its data in the `khala_sync_prod` Postgres instance. The port moves that data
13 13
into the Phoenix application database and serves it through
14 14
`OpenAgents.Forum` and the LiveViews under `/forum`. The move is a one-time
15
import (`mix forum.import`, `lib/mix/tasks/forum_import.ex`), not a sync:
15
import (`mix openagents.forum.import`, `lib/mix/tasks/openagents.forum.import.ex`), not a sync:
16 16
after the import, nothing reads from `khala_sync_prod`.
17 17
18 18
## Schema mapping
docs/taxonomy.md modified +1 -1

@@ -148,7 +148,7 @@ surface via `openagents api`.

148 148
149 149
**Forum** — the first-party discussion surface at `/forum`: boards, topics,
150 150
and posts backed by `OpenAgents.Forum`, ported from the legacy Effect forum
151
by a one-time import (`mix forum.import`). Browser reads and writes happen
151
by a one-time import (`mix openagents.forum.import`). Browser reads and writes happen
152 152
signed in; the `/api/v3/forum` reads are public. `docs/forum-port.md`
153 153
describes the port; `docs/evidence/forum-port-migration.md` records the
154 154
import.
lib/mix/tasks/forum_import.ex → lib/mix/tasks/openagents.forum.import.ex renamed +3 -3

@@ -1,4 +1,4 @@

1
defmodule Mix.Tasks.Forum.Import do
1
defmodule Mix.Tasks.Openagents.Forum.Import do
2 2
  @moduledoc """
3 3
  One-time import of the legacy Effect forum from `khala_sync_prod`.
4 4

@@ -13,9 +13,9 @@ defmodule Mix.Tasks.Forum.Import do

13 13
14 14
  ## Run
15 15
16
      mix forum.import
16
      mix openagents.forum.import
17 17
18
      FORUM_IMPORT_DATABASE_URL=... FORUM_IMPORT_PASSWORD=... mix forum.import
18
      FORUM_IMPORT_DATABASE_URL=... FORUM_IMPORT_PASSWORD=... mix openagents.forum.import
19 19
  """
20 20
21 21
  use Mix.Task
lib/openagents/forge/build_artifact.ex modified +3 -2

@@ -24,8 +24,9 @@ defmodule OpenAgents.Forge.BuildArtifact do

24 24
  @classification ~w(direct_candidate needs_rolling_replace)
25 25
  # Keep atom creation bounded to module namespaces owned by this OTP
26 26
  # application. The release contains the two application roots, generated
27
  # Inspect implementations, and Mix tasks in addition to OpenAgents.*.
28
  @module_pattern ~r/^Elixir\.(?:(?:OpenAgents|OpenAgentsWeb)(?:\.[A-Za-z][A-Za-z0-9_]*)*|Inspect\.(?:OpenAgents|OpenAgentsWeb)(?:\.[A-Za-z][A-Za-z0-9_]*)+|Mix\.Tasks\.(?:OpenAgents|Openagents)(?:\.[A-Za-z][A-Za-z0-9_]*)+)$/
27
  # protocol implementations (Inspect, Jason.Encoder, and String.Chars) for
28
  # application structs, and Mix tasks in addition to OpenAgents.*.
29
  @module_pattern ~r/^Elixir\.(?:(?:OpenAgents|OpenAgentsWeb)(?:\.[A-Za-z][A-Za-z0-9_]*)*|(?:Inspect|Jason\.Encoder|String\.Chars)\.(?:OpenAgents|OpenAgentsWeb)(?:\.[A-Za-z][A-Za-z0-9_]*)+|Mix\.Tasks\.(?:OpenAgents|Openagents)(?:\.[A-Za-z][A-Za-z0-9_]*)+)$/
29 30
30 31
  @type beam :: %{module: String.t(), binary: binary()}
31 32
  @type verified :: %{
test/openagents/forge/build_artifact_namespace_test.exs added +23

@@ -0,0 +1,23 @@

1
defmodule OpenAgents.Forge.BuildArtifactNamespaceTest do
2
  use ExUnit.Case, async: true
3
4
  alias OpenAgents.Forge.BuildArtifact
5
6
  test "every compiled application module is packageable by the build lane" do
7
    ebin = Application.app_dir(:openagents, "ebin")
8
9
    rejected =
10
      ebin
11
      |> Path.join("*.beam")
12
      |> Path.wildcard()
13
      |> Enum.reject(fn path ->
14
        match?({:ok, _module}, path |> File.read!() |> BuildArtifact.beam_module())
15
      end)
16
      |> Enum.map(&Path.basename(&1, ".beam"))
17
18
    assert rejected == [],
19
           "these modules fall outside the deployable namespaces " <>
20
             "(OpenAgents, OpenAgentsWeb, Mix.Tasks.Openagents) and would fail " <>
21
             "forge artifact packaging: #{inspect(rejected)}"
22
  end
23
end
test/openagents/forge/build_artifact_test.exs modified +2

@@ -199,6 +199,7 @@ defmodule OpenAgents.Forge.BuildArtifactTest do

199 199
      loaded_beam(OpenAgents),
200 200
      loaded_beam(OpenAgentsWeb),
201 201
      loaded_beam(Inspect.OpenAgents.Accounts.User),
202
      loaded_beam(Jason.Encoder.OpenAgents.Deployments.Execution),
202 203
      loaded_beam(Mix.Tasks.Openagents.Config.Readiness)
203 204
    ]
204 205

@@ -210,6 +211,7 @@ defmodule OpenAgents.Forge.BuildArtifactTest do

210 211
    assert artifact.manifest["changes"]["added"] ==
211 212
             Enum.sort([
212 213
               "Elixir.Inspect.OpenAgents.Accounts.User",
214
               "Elixir.Jason.Encoder.OpenAgents.Deployments.Execution",
213 215
               "Elixir.Mix.Tasks.Openagents.Config.Readiness",
214 216
               "Elixir.OpenAgents",
215 217
               "Elixir.OpenAgentsWeb"

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