Rename ward-mcp to mcp-beaver #48
Labels
No labels
autonomy
async-consult
autonomy
epic
autonomy
headless
autonomy
live-collab
coherence-core
priority
P0
priority
P1
priority
P2
priority
P3
priority
P4
qa-fixture
role/ai
role/creator
role/design
role/director
role/engineer
role/exec
role/human
role/ops
role/qa
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
coilyco-flight-deck/mcp-beaver#48
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Decision
Rename this repo from
ward-mcptomcp-beaver.Why
ward-mcpreads as "the MCP server for Ward." It isn't. Ward's fixed broker and the guarded operator surface are independent, neither reads policy from the other, and no drift check forces them to match. The actual lineage is cli-guard and specgen: this repo eats the same KDL guardfiles and emits a Streamable HTTP service and image instead of a static binary.The name was wrong because the repo was miscategorized at creation. The flight deck's implicit rule is that thin wrappers around someone else's API get descriptive names (
bluesky-mcp,reddit-mcp,node-stats-mcp) and original products get evocative ones (Ward,Atlas,Lore). This is an original product that got a wrapper's name.mcp-beaverover the alternates:specgen-mcpandmcp-guardwere both correct and both stylistic outliers next to Ward and Atlaskdl-mcppromises a KDL manipulation server, which is a different real productmcp-lockcollides with lockfile convention, includingspecverb.lockandaosguard-lockin our own treeCastorlost on search competition. Castor oil alone buries itThe
mcp-prefix is load-bearing rather than indecisive, since it is the half carrying the search term.Copy to land
Repo description, and the first line of the README, identical because it goes on the banner:
Five words, house banner form: indefinite article, sentence case, noun phrase, no verb, no period, lands on a category noun. Matches
An agent role and personality framework(agent-compose) andA discord community agent harness(sirens-echo).README head:
Description line does the indexing, tagline does the arguing, paragraph does the explaining.
Verify before publishing — done, and it failed
The sentence "Nothing else is in the artifact" was drafted from the cli-guard and specgen docs rather than this repo's source. Checked against source. It does not hold, and the reason is a design feature rather than a defect.
The artifact is deliberately generic. From the Dockerfile's own header: "the ONE generic runtime image. It carries no guardfile: the spec is mounted or COPYed in by the consumer." That is mount-not-bake (#6), and deploy depends on it — every service in
deploy/services/*pins this one image and supplies a different spec. The image does not contain the declared operations and exclude the rest. It contains a runtime that will render any spec handed to it.Upstream proxy mode resolves at request time.
internal/mcpserver/upstream.goconnects to a live upstream MCP, snapshots its tool list, registers only allowlisted tools (upstream.go:107-109), then re-checks membership on every call (upstream.go:155-156). Sound defense-in-depth, but the undeclared operations exist, behind an endpoint the container holds credentials for. Unreachable, not absent.Inline mode is the strong case and still wants different wording. Tools come from the KDL spec and register at startup (
server.go:123); an undeclared operation has no handler at all. Real and defensible — but a claim about the running server, not the artifact.Two words do the damage: artifact and in. Constraint for the rewrite: claim the server, not the image, and claim unreachable, not absent. Direction, with wording left to Content:
Blast radius — measured, and far smaller than first written
grep -rIE "ward[-_]mcp"across the local checkouts finds 342 occurrences in 113 files across 6 repos (deploy 77 files, infrastructure 18, agentic-os-kai 13, singles elsewhere). None of it is required for the rename.scripts/publish-image.shhardcodes the image name rather than deriving it from the repo:It never reads
${{ github.repository }}. After the rename, CI keeps publishing to the existing package path and every consumer pin keeps resolving. Forgejo names packages by publisher, not by repo, so the package does not move when the repo does.Confirmed untouched by a Forgejo repo rename:
ward-mcp-ssmIAM user and its inline SSM policy — an AWS identity, unrelated to the Forgejo pathnameOverride: ward-mcpon the four live releases, and theward-mcpcontainer name iningress-public-authed— live cluster resources with no link to the repo nameimage.repositorypins acrossdeploy/services/*— see abovego.mod'sforgejo.coilysiren.me/coilyco-flight-deck/ward-mcp— satisfies in-repo imports only, and CI builds inside the checkout, so the path is never resolved over the networkansible/roles/tailscale-proxy/files/compose.yaml, infrastructure docs, skills, the profile README — all descriptive textVerify rather than edit: the scoped publisher runner.
infrastructure/deploy/forgejo-runners-scoped/publisher-ward-mcp-repository/kustomization.yamlcarriesward-mcpin anameSuffix, acoilyco.io/runner-scopelabel, resource names, and the SSM registration-token path/forgejo/runner-registration/repo/coilyco-flight-deck/ward-mcp. Those are names, not live bindings — the runner is registered and Forgejo binds it to the repo entity, which follows a rename. It should survive. Called out because #10 is exactly this failure and it is silent: a push matches no runner and the repo queues zero tasks.Breaks only if separately touched:
repo-ward-mcpskill pointer at~/projects/coilyco-flight-deck/ward-mcp/; the git remote follows the redirectCorrection to the original list: Homebrew tap and Scoop bucket appear to be template inheritance rather than real coupling. This repo has one workflow,
.forgejo/workflows/ci.yml, and no release or tag lane — it ships an image, not a binary. Confirm before spending time there.Unblocked
Namespace availability confirmed clear on all four:
mcp-beavermcp-beavermcp-beavermcp-beaverGitHub repo search for
mcp-beaver in:namereturns zero exact matches. Near-misses are inverted or qualified and none exceeds one star:alanko0511/beaver-mcp,eduard256/beaver-notes-mcp,Danissimode/BeaverBoardKanban-mcp.Post-rename verification
The whole hypothesis rides on the second one.
coilyco-flight-deck/ward-mcpredirects — confirmed live: an API call to the old path returned a redirect to/repos/coilyco-flight-deck/mcp-beaver/forgejo.coilysiren.me/coilyco-flight-deck/ward-mcpstill lists and pullsAcceptance criteria
mcp-beaverconfirmed free on GitHub, npm, PyPI, cratesStanding state after the rename
Repo
mcp-beaverpublishes a package namedward-mcpagainst a Go module namedward-mcp. That mismatch is stable indefinitely — deliberate cosmetic debt, not a broken state awaiting a follow-up. The sweep can happen later, in pieces, or never.Ownership
Copy and naming rationale are Content, including the rewrite the verification now requires. The rename itself is Engineer. The deferred sweep, if it is ever taken up, is Engineer and DevOps.
Engineer pass on the two blockers, plus a bare-rename finding
1. Namespace availability — clear on all four
mcp-beavermcp-beavermcp-beavermcp-beaverGitHub repo search for
mcp-beaver in:namereturns zero exact matches. Three near-misses exist, all inverted or qualified and none above one star:alanko0511/beaver-mcp,eduard256/beaver-notes-mcp,Danissimode/BeaverBoardKanban-mcp. Search competition holds up.Unblocks the first acceptance box.
2. "Nothing else is in the artifact" — does not survive contact with the source
The sentence is wrong on the plainest reading, and the reason is a feature, not a bug.
The artifact is deliberately generic. From the Dockerfile's own header: "the ONE generic runtime image. It carries no guardfile: the spec is mounted or COPYed in by the consumer." That is mount-not-bake (#6), and deploy depends on it — every service in
deploy/services/*pins the same image and supplies a different spec. So the image does not contain the declared operations and exclude the rest. It contains a runtime that will render any spec you hand it.In upstream proxy mode it is request-time filtering, which is the case this issue flagged.
internal/mcpserver/upstream.goconnects to a live upstream MCP, snapshots its tool list, and registers only allowlisted tools (upstream.go:107-109), then re-checks membership on every call (upstream.go:155-156). That double check is good defense-in-depth, but the undeclared operations plainly exist, behind an endpoint the container holds credentials for. They are unreachable, not absent.Inline mode is the strong case, and even it wants different wording. Tools come from the KDL spec and are registered at startup (
server.go:123); an undeclared operation has no handler at all. That is a real and defensible claim — but about the running server, not the artifact.Two words are doing the damage: artifact and in. The true version is about the server and about reachability. Something in the direction of:
Wording is Content's call. The constraint from source is: claim the server, not the image, and claim unreachable, not absent.
Second acceptance box is answered — the sentence needs a rewrite before it goes public.
3. A bare rename is very close to free, and cheaper than this issue assumes
Checked on the hypothesis that we rename the repo and change nothing else.
The image path does not derive from the repo name.
scripts/publish-image.shhardcodes it:It never reads
${{ github.repository }}. So after a rename CI keeps publishing to the existing package path, and every consumer pin keeps resolving. This is stronger than "the images can idle" — new publishes keep landing too. The Forgejo package is named by the publisher, not by the repo, so it does not move when the repo does.Untouched by a repo rename, confirmed rather than assumed:
ward-mcp-ssmIAM user and its policy — an AWS identity, unrelated to the Forgejo pathnameOverride: ward-mcpand theward-mcpcontainer name iningress-public-authed— live cluster resources, no link to repo nameimage.repositorypins in deploy — see abovego.mod'sforgejo.coilysiren.me/coilyco-flight-deck/ward-mcp— only satisfies in-repo imports, and CI builds inside the checkout, so the path is never resolved over the networkThe one thing to verify rather than edit is the scoped publisher runner.
infrastructure/deploy/forgejo-runners-scoped/publisher-ward-mcp-repository/kustomization.yamlcarriesward-mcpin anameSuffix, acoilyco.io/runner-scopelabel, resource names, and the SSM registration-token path/forgejo/runner-registration/repo/coilyco-flight-deck/ward-mcp. All of those are names, not live bindings — the runner is already registered and Forgejo binds it to the repo entity, which follows a rename. It should survive. It gets called out anyway because #10 is exactly the failure where a push matched no runner and the repo queued zero tasks, and that failure is silent.Cheap proof: rename, push a trivial commit, watch the publish lane produce an image. That single test covers the whole hypothesis.
Breaks only if separately touched:
repo-ward-mcpskill pointer at~/projects/coilyco-flight-deck/ward-mcp/— the git remote itself follows the redirectCorrection to the blast radius list above: Homebrew tap and Scoop bucket look like template inheritance rather than real coupling. This repo has one workflow,
.forgejo/workflows/ci.yml, and no release or tag lane — it ships an image, not a binary. Worth confirming before anyone spends time there.What this changes
A bare rename is not step one of the migration, it is a complete and safe step on its own. It leaves repo
mcp-beaverpublishing a package namedward-mcpagainst a Go module namedward-mcp, and that mismatch is stable indefinitely — it is cosmetic debt, taken deliberately, not a broken state waiting on a follow-up. The 342 occurrences across 113 files can be swept whenever, or never, without holding the name hostage.Post-rename verification: all four pass. The hypothesis holds.
Verified by outcome rather than by a green checkmark — the image is tagged with the full source SHA, so its existence proves runner match, build, and publish in one artifact.
1. Redirect. Confirmed at both layers. A PATCH to the old API path returned a redirect to
/repos/coilyco-flight-deck/mcp-beaver/(correctly refused rather than silently followed, since it was a mutating verb). At the git layer, a clone still carrying the old remote fetches fine:2. Publisher lane — the #10 check. Green. Post-rename push
6e8d2f5b9675ce05f9c6177b3eb662911a001a58("Update README.md", 10:52:35Z). Its manifest exists:The scoped runner matched a push to the renamed repo, built, and pushed. No silent zero-task queue. The runner registration follows the repo entity, as expected — repo id 75 was unchanged by the rename.
3. Package intact at the unchanged path.
GET /v2/coilyco-flight-deck/ward-mcp/tags/list→ HTTP 200, 17 tags, every historical SHA still present, with the new one appended. The package did not move when the repo did, exactly asscripts/publish-image.shhardcoding the name predicted.4. Zero disruption to live services. Ten pods run this image across nine namespaces. All
Running, allrestart_count: 0, all aged 12h55m–12h59m — comfortably older than the rename, so nothing rolled:All ten still pinned to
97a100274a72, a pre-rename SHA, still resolving fromforgejo.coilysiren.me/coilyco-flight-deck/ward-mcp. Pulls and pins both survive the rename untouched.Incidental confirmation of the deferred debt: the pod names show the split cleanly.
forgejo-ward-mcp,skillsmp-ward-mcp, andtrello-ward-mcpcarrynameOverride, while the rest sit oningress-public-authed. Those three names are the immutable-field cost documented above, and they remain a deliberate deferral.Conclusion
A bare rename cost nothing and broke nothing. What remains is copy and art, neither of which touches running infrastructure.
server-infoshould be on by default — the disclosure argument for opt-in does not survive reading the payload #61server-infoshould be on by default — the disclosure argument for opt-in does not survive reading the payload #61Worked the open acceptance items against the live forge. One of the issue's central predictions no longer holds, and it has a cross-repo consequence.
The package path DID move. The blast-radius section reasoned:
A later commit changed that deliberately -
61ee2b3, "chore(publish): move the runtime image to coilyco-flight-deck/mcp-beaver". The script now readsimage_name="coilyco-flight-deck/mcp-beaver". The forge carries two packages:ward-mcp- last published 2026-08-13 14:51, shaa5667056...mcp-beaver- carrying every build since, currentlyc61a7f29...So the "standing state after the rename" paragraph is superseded: the package no longer stays behind, and the deferred sweep is no longer purely cosmetic on this axis.
Cross-repo consequence, which is deploy's to action and is not taken here. 13
image.repositorypins still name.../ward-mcp:deploy/charts/ingress-public-authed/values.yamldeploy/services/{signoz-telegram,discord-mcp,aws-ssm-mcp,glama-mcp,signoz-mcp,skillsmp-mcp,forgejo-mcp}/values.yamldeploy/services/sirens-echo/deploy/Nothing is broken today - the old package is retained, so every existing pinned sha still resolves. The next bump to a post-rename sha will not pull.
Fixed in-repo (
21ab242), since these are this repo's own artifacts and were pointing at a package that receives no new tags:chart/values.yaml-image.repositorydefault now.../mcp-beaverdocs/FEATURES.md- the published-image referenceAGENTS.md- scope and boundary lines, plus an explicit note that the in-treeward-mcpnaming is deliberate debt and must not be swept as incidental cleanup. That note is there because an agent reading the tree will otherwise keep re-deciding this.Acceptance items I can now tick:
ci.ymlruns #79 through #88, all successful, including thepublishjob on every push tomain. That is the #10 check.ward-mcpis frozen,mcp-beaveris live.Still yours, not mine: