feat(registry): open native library projects at schema 29; repoint ex-mcp #1649

Merged
binjovi-bot merged 2 commits from feat/ex-mcp-project into trunk 2026-09-12 14:40:56 +00:00
Owner

Why

sean/ex-mcp is the fork Binjovi builds its own MCP server from, and it is not a Binjovi project. It cannot become one today: schema 21 pins the project to four namesbytey, hello-lib, libseanfarm-operator, zot-src — so a new library cannot be added by a plans-only pull request at all.

Schema 29

Widening v21 would change what a durable generation means for records already written against it, which its own comment forbids. So 29 is a new generation: v21 with the allowlist replaced by the two properties the allowlist was standing in for.

defp parse(%{"version" => 29} = values) do
  with true <- valid_dns_name?(values["project"]),
       true <- valid_repo?(values["repo"]),
       true <- values["repo"] == "sean/" <> values["project"] do
    parse_native_project(values, @v21_keys, nil)
  else
    _ -> {:error, :invalid_project_fields}
  end
end

Everything else is @v21_keys and parse_native_project unchanged: no taxonomy key (a library delivers nothing, so it names no delivery shape), executor native, recipe rooted at recipes/<project>.

valid_dns_name? is what rejects ex_mcp, and why the repository was renamed to sean/ex-mcp.

29, not 28 — the suite caught this

I first wrote it as 28. 28 is already taken by the binjovi-plans catalog clause, whose head also pins "project" => "binjovi-plans". A generic clause at 28 silently swallowed every other project at that version and turned :invalid_project_version into :invalid_project_fields.

object_store_project_test.exs:105 — "no other project may declare an object-store target" — failed, which is exactly its job.

Worth recording, so the next person does not repeat it: my version survey grepped defp parse(%{"version" => N, and 28's literal lives inside a multi-line with pattern at line 1229, not a clause head. The comment now says to grep every "version" => N literal in the file.

Dependency repoint

apps/binjovi_api, apps/binjovi_events and mix.lock now name code.sean.farm/sean/ex-mcp.git. The pin is by SHA (2bbb22ba…), so only the URL moved. Forgejo's post-rename redirect kept the old URL resolving — this stops depending on it. Verified with a clean deps.get against the new URL.

Also

One assertion in my new tests was wrong and is removed rather than worked around: parse_native_project legitimately allows deployment_targets, and bytey is the same generation shipping configmap/workflows/bytey-floorplan. ex-mcp simply has none.

Verification

891 tests pass, full umbrella suite, no failures. Postgres-tagged tests are excluded locally and run in CI.

Sequencing: this must be released before the binjovi-plans catalog record, because the decoder has to exist in the running image before a bundle naming version 29 can activate. The seanfarm side is PR #674.

https://claude.ai/code/session_01XdBRc9CHgFhAxCyHbdVtt6

## Why `sean/ex-mcp` is the fork Binjovi builds its own MCP server from, and it is not a Binjovi project. It cannot become one today: **schema 21 pins the project to four names** — `bytey`, `hello-lib`, `libseanfarm-operator`, `zot-src` — so a new library cannot be added by a plans-only pull request at all. ## Schema 29 Widening v21 would change what a durable generation means for records already written against it, which its own comment forbids. So 29 is a new generation: v21 with the allowlist replaced by the two properties the allowlist was standing in for. ```elixir defp parse(%{"version" => 29} = values) do with true <- valid_dns_name?(values["project"]), true <- valid_repo?(values["repo"]), true <- values["repo"] == "sean/" <> values["project"] do parse_native_project(values, @v21_keys, nil) else _ -> {:error, :invalid_project_fields} end end ``` Everything else is `@v21_keys` and `parse_native_project` unchanged: no taxonomy key (a library delivers nothing, so it names no delivery shape), executor `native`, recipe rooted at `recipes/<project>`. `valid_dns_name?` is what rejects `ex_mcp`, and why the repository was renamed to `sean/ex-mcp`. ## 29, not 28 — the suite caught this I first wrote it as 28. **28 is already taken** by the `binjovi-plans` catalog clause, whose head also pins `"project" => "binjovi-plans"`. A generic clause at 28 silently swallowed every *other* project at that version and turned `:invalid_project_version` into `:invalid_project_fields`. `object_store_project_test.exs:105` — "no other project may declare an object-store target" — failed, which is exactly its job. Worth recording, so the next person does not repeat it: my version survey grepped `defp parse(%{"version" => N`, and 28's literal lives inside a multi-line `with` pattern at line 1229, not a clause head. The comment now says to grep **every** `"version" => N` literal in the file. ## Dependency repoint `apps/binjovi_api`, `apps/binjovi_events` and `mix.lock` now name `code.sean.farm/sean/ex-mcp.git`. The pin is by SHA (`2bbb22ba…`), so only the URL moved. Forgejo's post-rename redirect kept the old URL resolving — this stops depending on it. Verified with a clean `deps.get` against the new URL. ## Also One assertion in my new tests was wrong and is removed rather than worked around: `parse_native_project` legitimately allows `deployment_targets`, and `bytey` is the same generation shipping `configmap/workflows/bytey-floorplan`. `ex-mcp` simply has none. ## Verification `891 tests pass`, full umbrella suite, no failures. Postgres-tagged tests are excluded locally and run in CI. Sequencing: this must be **released before** the `binjovi-plans` catalog record, because the decoder has to exist in the running image before a bundle naming version 29 can activate. The seanfarm side is [PR #674](https://code.sean.farm/sean/seanfarm/pulls/674). https://claude.ai/code/session_01XdBRc9CHgFhAxCyHbdVtt6
feat(registry): open native library projects at schema 29; repoint ex-mcp
Some checks failed
binjovi/ci Binjovi failed the frozen plan
a01ba24ea0
Two changes that together let sean/ex-mcp become an ordinary Binjovi project.

SCHEMA 29. Version 21 pins the project to four names -- bytey, hello-lib,
libseanfarm-operator, zot-src -- so a new library could not be added by a
plans-only pull request at all. Widening v21 would change what a durable
generation means for records already written against it, which its own comment
forbids. So 29 is a new generation: v21 with the allowlist replaced by the two
properties the allowlist stood in for.

  * the project is a DNS label -- this is what rejects `ex_mcp`, and why
    sean/ex_mcp was renamed to sean/ex-mcp;
  * the repository is exactly sean/<project>.

Everything else is @v21_keys and parse_native_project unchanged: no taxonomy
key, executor native, recipe rooted at recipes/<project>.

29, NOT 28. 28 is already the binjovi-plans catalog clause, whose head also
pins "project" => "binjovi-plans". A generic clause at 28 swallowed every other
project at that version and turned :invalid_project_version into
:invalid_project_fields. object_store_project_test.exs:105 caught it. The
version survey that missed it grepped clause heads; the literal lives inside a
multi-line `with` pattern. A comment now says to grep every `"version" => N`.

DEPENDENCY. apps/binjovi_api, apps/binjovi_events and mix.lock now name
code.sean.farm/sean/ex-mcp.git. The pin is by SHA, so only the URL moved.
Forgejo's post-rename redirect kept the old URL resolving, and this stops
depending on it. Verified with a clean deps.get against the new URL.

A deployment_targets assertion in the new tests was wrong and was removed, not
worked around: parse_native_project allows targets, and bytey is the same
generation with "configmap/workflows/bytey-floorplan". ex-mcp simply has none.

891 tests pass.

Claude-Session: https://claude.ai/code/session_01XdBRc9CHgFhAxCyHbdVtt6
fix(contract): pin the ExMCP dependency at its renamed URL
All checks were successful
binjovi/ci Binjovi completed the frozen plan
22e988e97a
The build failed at `make contract-check`, in tests/mcp-dependency-contract.sh.
It asserts the locked ex_mcp URL by hand, and still named
code.sean.farm/sean/ex_mcp.git after the repository was renamed to ex-mcp.

The local suite could not have caught this: `mix test` does not run
contract-check, and `mix deps.get --locked` succeeds either way because Forgejo
redirects the old URL. Pinning the URL by hand is exactly the right design --
a redirect is what would otherwise let the pin rot in silence -- so the guard
is corrected rather than loosened, and now says why the name changed.

Found with `binjovictl attempt-log`, which is the tool for this and which I
should have reached for before reasoning about the failure.

Claude-Session: https://claude.ai/code/session_01XdBRc9CHgFhAxCyHbdVtt6
binjovi-bot deleted branch feat/ex-mcp-project 2026-09-12 14:40:57 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
sean/binjovi!1649
No description provided.