Skip to content

Add Claude Code GitHub Workflow#378

Merged
reh3376 merged 2 commits intomainfrom
add-claude-github-actions-1778078180674
May 6, 2026
Merged

Add Claude Code GitHub Workflow#378
reh3376 merged 2 commits intomainfrom
add-claude-github-actions-1778078180674

Conversation

@reh3376
Copy link
Copy Markdown
Owner

@reh3376 reh3376 commented May 6, 2026

🤖 Installing Claude Code GitHub App

This PR adds a GitHub Actions workflow that enables Claude Code integration in our repository.

What is Claude Code?

Claude Code is an AI coding agent that can help with:

  • Bug fixes and improvements
  • Documentation updates
  • Implementing new features
  • Code reviews and suggestions
  • Writing tests
  • And more!

How it works

Once this PR is merged, we'll be able to interact with Claude by mentioning @claude in a pull request or issue comment.
Once the workflow is triggered, Claude will analyze the comment and surrounding context, and execute on the request in a GitHub action.

Important Notes

  • This workflow won't take effect until this PR is merged
  • @claude mentions won't work until after the merge is complete
  • The workflow runs automatically whenever Claude is mentioned in PR or issue comments
  • Claude gets access to the entire PR or issue context including files, diffs, and previous comments

Security

  • Our Anthropic API key is securely stored as a GitHub Actions secret
  • Only users with write access to the repository can trigger the workflow
  • All Claude runs are stored in the GitHub Actions run history
  • Claude's default tools are limited to reading/writing files and interacting with our repo by creating comments, branches, and commits.
  • We can add more allowed tools by adding them to the workflow file like:
allowed_tools: Bash(npm install),Bash(npm run build),Bash(npm run lint),Bash(npm run test)

There's more information in the Claude Code action repo.

After merging this PR, let's try mentioning @claude in a comment on any PR to get started!

Summary by CodeRabbit

  • Chores
    • Added automated code review workflows to enhance development automation and streamline the review process.

@reh3376 reh3376 self-assigned this May 6, 2026
@reh3376 reh3376 merged commit 9800c53 into main May 6, 2026
4 of 6 checks passed
@reh3376 reh3376 deleted the add-claude-github-actions-1778078180674 branch May 6, 2026 14:37
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 6, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1092be55-2458-47c9-8162-b951f62c7d4e

📥 Commits

Reviewing files that changed from the base of the PR and between de71b63 and 0761ab3.

📒 Files selected for processing (2)
  • .github/workflows/claude-code-review.yml
  • .github/workflows/claude.yml

📝 Walkthrough

Walkthrough

This PR introduces two new GitHub Actions workflows to automate Claude-based code review and execution features. The first triggers automatically on pull request events to run code reviews, while the second activates via comments containing "@claude" to execute code actions.

Changes

Automated PR Code Review Workflow

Layer / File(s) Summary
Trigger & Job Configuration
.github/workflows/claude-code-review.yml
Workflow triggers on PR events (opened, synchronize, ready_for_review, reopened) and defines a claude-review job with checkout and Claude Code Review action invocation using token, plugin marketplace, and PR context.

Comment-Triggered Claude Actions Workflow

Layer / File(s) Summary
Trigger & Permissions
.github/workflows/claude.yml
Workflow listens for issue comments, PR review comments, and issue/PR events containing "@claude" token. Defines explicit read permissions for actions, contents, pull-requests, issues, and id-token.
Job Execution
.github/workflows/claude.yml
Includes repository checkout and invocation of anthropics/claude-code-action@v1 with secret token handling and optional custom prompt configuration.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 Two workflows hop into the fold,
Claude reviews and codes, a story untold,
With triggers that fire on PR and on chat,
Our automation's nimble—imagine that! ✨

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch add-claude-github-actions-1778078180674

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

reh3376 pushed a commit that referenced this pull request May 6, 2026
Promote the Unreleased CHANGELOG block to v0.9.0 (2026-05-06) ahead of
release.yml / goreleaser tag push.

New ### Breaking subsection captures two operator-visible cutovers since
v0.8.5: (1) Phase 13.5 LLM runtime port 8101 -> 8102 + .env migration
required; (2) Phase 13.6 MLX_* -> LLM_* env-var rename (legacy aliases
retained for >= 1 release cycle).

New ### Added entries: Phase 10.5 closeout (UBENCH framework promotion,
commit 0389b49) and Claude Code GitHub App workflows (PRs #378, #379).

All previously-Unreleased entries (Phase 14.2.3, 14.2.x, 14.1.x, 14, 13.6,
13.5, 13.2, 13.1) carried forward unchanged into the v0.9.0 block. Fresh
empty Unreleased section seeded above.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
reh3376 pushed a commit that referenced this pull request May 6, 2026
Promote the Unreleased CHANGELOG block to v0.9.0 (2026-05-06) ahead of
release.yml / goreleaser tag push.

New ### Breaking subsection captures two operator-visible cutovers since
v0.8.5: (1) Phase 13.5 LLM runtime port 8101 -> 8102 + .env migration
required; (2) Phase 13.6 MLX_* -> LLM_* env-var rename (legacy aliases
retained for >= 1 release cycle).

New ### Added entries: Phase 10.5 closeout (UBENCH framework promotion,
commit 0389b49) and Claude Code GitHub App workflows (PRs #378, #379).

All previously-Unreleased entries (Phase 14.2.3, 14.2.x, 14.1.x, 14, 13.6,
13.5, 13.2, 13.1) carried forward unchanged into the v0.9.0 block. Fresh
empty Unreleased section seeded above.

Co-authored-by: Roger Henley <rogerhenley345@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
reh3376 pushed a commit that referenced this pull request May 6, 2026
* docs(release): promote Unreleased -> v0.9.0

Promote the Unreleased CHANGELOG block to v0.9.0 (2026-05-06) ahead of
release.yml / goreleaser tag push.

New ### Breaking subsection captures two operator-visible cutovers since
v0.8.5: (1) Phase 13.5 LLM runtime port 8101 -> 8102 + .env migration
required; (2) Phase 13.6 MLX_* -> LLM_* env-var rename (legacy aliases
retained for >= 1 release cycle).

New ### Added entries: Phase 10.5 closeout (UBENCH framework promotion,
commit 0389b49) and Claude Code GitHub App workflows (PRs #378, #379).

All previously-Unreleased entries (Phase 14.2.3, 14.2.x, 14.1.x, 14, 13.6,
13.5, 13.2, 13.1) carried forward unchanged into the v0.9.0 block. Fresh
empty Unreleased section seeded above.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* chore(submodule): bump homebrew-mdemg to v0.9.0 formula + docs

Bumps packaging/homebrew-mdemg pointer a235977 -> 6077097, which incorporates:
- f9358cd  Brew formula update for mdemg version v0.8.5 (goreleaser, prior)
- b4a0d2c  Brew formula update for mdemg version v0.9.0 (goreleaser, this release)
- 6077097  docs: v0.9.0 -- CHANGELOG, README What's New, beta-testing version pin

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Roger Henley <rogerhenley345@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
@PPPDUD
Copy link
Copy Markdown
Collaborator

PPPDUD commented May 6, 2026

@reh3376 Out of curiosity, what is Claude going to be used for in mdemg? Is it going to write code, do code reviews, or something else?

@reh3376
Copy link
Copy Markdown
Owner Author

reh3376 commented May 6, 2026 via email

@PPPDUD
Copy link
Copy Markdown
Collaborator

PPPDUD commented May 6, 2026

Give me a day then I will set up a meeting with you to discuss and review my workflows using the MDEMG framework. I am adding my local model to the installer now and it will be easier to understand if I have it fully functional before we review. That way you can use it.

Sent from my iPhone On May 6, 2026, at 12:44, PPPDUD @.> wrote:  EXTERNAL EMAIL - This email was sent by a person from outside your organization. Exercise caution when clicking links, opening attachments or taking further action, before validating its authenticity. Secured by Check Point [https://avatars.githubusercontent.com/u/107440101?s=20&v=4]PPPDUD left a comment (reh3376/mdemg#378)<#378 (comment)> @reh3376https://github.com/reh3376 Out of curiosity, what is Claude going to be used for in mdemg? Is it going to write code, do code reviews, or something else? — Reply to this email directly, view it on GitHub<#378 (comment)>, or unsubscribehttps://github.com/notifications/unsubscribe-auth/BAYW6EEJZMX444TSF2HY3BT4ZNTW5AVCNFSM6AAAAACYTGJL6OVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHM2DGOJQGE3TSNBUGY. You are receiving this because you were mentioned.Message ID: @.>

What platform shall the meeting be held on? I don't use Discord or Teams, and I will most likely be unavailable circa 10 AM and 12 PM tomorrow.

P.S. I had to disable Docker on my primary Linux machine because one of my containers (not sure which) was messing up my Internet connection and causing weird errors in Chromium, so I can't test on Linux at the moment, but I might be able to test on the Mac Studio.

@PPPDUD
Copy link
Copy Markdown
Collaborator

PPPDUD commented May 6, 2026

@claude Can people other than @reh3376 invoke you over GitHub in this repository, in the format used in this comment?

@claude
Copy link
Copy Markdown

claude Bot commented May 6, 2026

Claude finished @PPPDUD's task in 51s —— View job


Who can invoke Claude in this repository?

  • Read workflow files to check access controls
  • Answer the question

Short answer: Yes — and your mention proves it. The fact that your @claude comment triggered this response is itself the demonstration.

Here's what the workflow files actually show:

claude.yml (the @claude mention workflow, lines 15–19) has this if condition:

if: |
  (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
  (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
  (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
  (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || ...))

There is no user filter — it only checks whether the comment contains @claude. No check for github.actor, no allowlist of usernames.

claude-code-review.yml (auto-review on PR open/sync) similarly has no user restriction — the PR author filter is present but commented out (lines 15–18).

The PR description mentions "Only users with write access to the repository can trigger the workflow" — this is a claim about how anthropics/claude-code-action is supposed to behave by default internally. However, based on the evidence (your mention triggered this run), you appear to have sufficient access.

In practice, the access model is:

Scenario Who can trigger
@claude in issue/PR comment Anyone who can comment (depends on repo visibility + collaborator settings)
@claude in PR review Anyone with PR review permission
Automatic review on PR open All PRs (no author filter currently active)

If @reh3376 wants to restrict invocations to specific users, they could add a check like github.actor == 'reh3376' or contains(fromJson('["user1","user2"]'), github.actor) to the if condition in claude.yml.

@PPPDUD
Copy link
Copy Markdown
Collaborator

PPPDUD commented May 6, 2026

Who can invoke Claude in this repository?

  • Read workflow files to check access controls
  • Answer the question

Short answer: Yes — and your mention proves it. The fact that your @claude comment triggered this response is itself the demonstration.

Here's what the workflow files actually show:

claude.yml (the @claude mention workflow, lines 15–19) has this if condition:

if: |
  (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
  (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
  (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
  (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || ...))

There is no user filter — it only checks whether the comment contains @claude. No check for github.actor, no allowlist of usernames.

claude-code-review.yml (auto-review on PR open/sync) similarly has no user restriction — the PR author filter is present but commented out (lines 15–18).

The PR description mentions "Only users with write access to the repository can trigger the workflow" — this is a claim about how anthropics/claude-code-action is supposed to behave by default internally. However, based on the evidence (your mention triggered this run), you appear to have sufficient access.

In practice, the access model is:

Scenario Who can trigger
@claude in issue/PR comment Anyone who can comment (depends on repo visibility + collaborator settings)
@claude in PR review Anyone with PR review permission
Automatic review on PR open All PRs (no author filter currently active)
If @reh3376 wants to restrict invocations to specific users, they could add a check like github.actor == 'reh3376' or contains(fromJson('["user1","user2"]'), github.actor) to the if condition in claude.yml.

View job run

Good bot. @reh3376 Are you sure that it's wise to let other people invoke Claude directly? That seems like a great way for a bad actor to use up all of your tokens.

@reh3376
Copy link
Copy Markdown
Owner Author

reh3376 commented May 6, 2026 via email

@claude
Copy link
Copy Markdown

claude Bot commented May 6, 2026

Claude finished @reh3376's task in 53s —— View job


Restricting Claude invocation to authorized users

  • Understand the request (restrict @claude access to authorized users only)
  • Read the current workflow files
  • Provide the exact changes needed

You're right to be concerned — right now anyone who can comment on this repo can invoke Claude and consume your API tokens. Here's exactly what to fix.

⚠️ Note: I'm unable to modify .github/workflows/ files directly due to GitHub App permission restrictions. You'll need to apply these changes manually. See the FAQ for more info.


Fix 1: .github/workflows/claude.yml — restrict @claude mentions

Add a github.actor check as the first condition in the if block (line 15). Replace the current if: with:

    if: |
      contains(fromJson('["reh3376"]'), github.actor) && (
        (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
        (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
        (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
        (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
      )

To allow additional trusted users, add them to the JSON array:

      contains(fromJson('["reh3376", "trusted-user2"]'), github.actor) && (

Fix 2: .github/workflows/claude-code-review.yml — restrict auto-reviews

The author filter is currently commented out (lines 16–19). Enable it by adding an if: that limits auto-reviews to PRs from authorized authors:

  claude-review:
    if: |
      contains(fromJson('["reh3376"]'), github.event.pull_request.user.login)

Or if you want auto-review on all PRs (lower risk since it's read-only), you can leave this one unrestricted.


Summary of risk levels

Workflow Risk Recommended action
claude.yml (@claude mentions) High — anyone can trigger arbitrary Claude tasks Add user allowlist (Fix 1 above)
claude-code-review.yml (auto PR review) Lower — read-only, runs on PR open Optionally restrict to known authors

@reh3376
Copy link
Copy Markdown
Owner Author

reh3376 commented May 6, 2026 via email

@claude
Copy link
Copy Markdown

claude Bot commented May 6, 2026

Claude finished @reh3376's task in 1m 3s —— View job


Generate actor-restriction sed one-liner

  • Read .github/workflows/claude.yml
  • Craft the sed command (no file modification)

Here's the one-liner. It matches every condition line that starts with 6 spaces + ( and prepends github.actor == 'reh3376' && — one prefix per || branch, which is semantically equivalent to wrapping the whole block.

Linux (GNU sed):

sed -i "s/^      (/      github.actor == 'reh3376' \&\& (/g" .github/workflows/claude.yml

macOS (BSD sed):

sed -i '' "s/^      (/      github.actor == 'reh3376' \&\& (/g" .github/workflows/claude.yml

After running it, your if: block will look like:

    if: |
      github.actor == 'reh3376' && (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
      github.actor == 'reh3376' && (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
      github.actor == 'reh3376' && (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
      github.actor == 'reh3376' && (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))

Why this is correct: && binds tighter than ||, so A && B || A && C evaluates as (A && B) || (A && C). The actor check is applied to every branch.

Preview with diff before committing:

git diff .github/workflows/claude.yml

@PPPDUD
Copy link
Copy Markdown
Collaborator

PPPDUD commented May 6, 2026

Try adding this to a comment in the repo: @claude generate a bash one-liner using sed (or yq) that prepends github.actor == 'reh3376' && to the if: condition of the claude job in .github/workflows/claude.yml. Post the command in a reply so I can run it locally. Don’t modify the file yourself.

Sent from my iPhone On May 6, 2026, at 14:24, claude[bot] @.> wrote:  EXTERNAL EMAIL - This email was sent by a person from outside your organization. Exercise caution when clicking links, opening attachments or taking further action, before validating its authenticity. Secured by Check Point [https://avatars.githubusercontent.com/in/1236702?s=20&v=4]claude[bot] left a comment (reh3376/mdemg#378)<#378 (comment)> Claude Code is working… claude-thinking.gif (view on web)https://github.com/user-attachments/assets/5ac382c7-e004-429b-8e35-7feb3e8f9c6f I'll analyze this and get back to you. View job runhttps://github.com/reh3376/mdemg/actions/runs/25453311031 — Reply to this email directly, view it on GitHub<#378 (comment)>, or unsubscribehttps://github.com/notifications/unsubscribe-auth/BAYW6EECZD753L7NIPFH4TD4ZN7LJAVCNFSM6AAAAACYTGJL6OVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHM2DGOJQHAYTQMZUGU. You are receiving this because you were mentioned.Message ID: @.>

Well, I think that you already did that accidentally.

@reh3376
Copy link
Copy Markdown
Owner Author

reh3376 commented May 6, 2026 via email

reh3376 pushed a commit that referenced this pull request May 7, 2026
* docs(release): promote Unreleased -> v0.9.0

Promote the Unreleased CHANGELOG block to v0.9.0 (2026-05-06) ahead of
release.yml / goreleaser tag push.

New ### Breaking subsection captures two operator-visible cutovers since
v0.8.5: (1) Phase 13.5 LLM runtime port 8101 -> 8102 + .env migration
required; (2) Phase 13.6 MLX_* -> LLM_* env-var rename (legacy aliases
retained for >= 1 release cycle).

New ### Added entries: Phase 10.5 closeout (UBENCH framework promotion,
commit 0389b49) and Claude Code GitHub App workflows (PRs #378, #379).

All previously-Unreleased entries (Phase 14.2.3, 14.2.x, 14.1.x, 14, 13.6,
13.5, 13.2, 13.1) carried forward unchanged into the v0.9.0 block. Fresh
empty Unreleased section seeded above.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* chore(submodule): bump homebrew-mdemg to v0.9.0 formula + docs

Bumps packaging/homebrew-mdemg pointer a235977 -> 6077097, which incorporates:
- f9358cd  Brew formula update for mdemg version v0.8.5 (goreleaser, prior)
- b4a0d2c  Brew formula update for mdemg version v0.9.0 (goreleaser, this release)
- 6077097  docs: v0.9.0 -- CHANGELOG, README What's New, beta-testing version pin

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(api): /healthz returns build-time version, not stale literal "0.6.0"

`config.FromEnv()` defaulted MdemgVersion/MdemgCommit to literal "0.6.0"/
"unknown" when MDEMG_VERSION/MDEMG_COMMIT envs were unset. Both /healthz
and /readyz serialize cfg.MdemgVersion, so they reported "0.6.0" forever
regardless of the actual binary's ldflags-injected cli.Version.

Fix: defaults to "" in config; cli/config_loader.go injects cli.Version /
cli.Commit (the build-time vars set by goreleaser ldflags) when the env
override is unset. Operators can still pin via MDEMG_VERSION env.

Live-verified: dev build (no ldflags) now reports {"version":"dev"} on
/healthz instead of the lying "0.6.0". Production builds via goreleaser
will report the real semver tag.

TestHandleHealthz unaffected (sets cfg.MdemgVersion directly).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Roger Henley <rogerhenley345@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
reh3376 pushed a commit that referenced this pull request May 7, 2026
* docs(release): promote Unreleased -> v0.9.0

Promote the Unreleased CHANGELOG block to v0.9.0 (2026-05-06) ahead of
release.yml / goreleaser tag push.

New ### Breaking subsection captures two operator-visible cutovers since
v0.8.5: (1) Phase 13.5 LLM runtime port 8101 -> 8102 + .env migration
required; (2) Phase 13.6 MLX_* -> LLM_* env-var rename (legacy aliases
retained for >= 1 release cycle).

New ### Added entries: Phase 10.5 closeout (UBENCH framework promotion,
commit 0389b49) and Claude Code GitHub App workflows (PRs #378, #379).

All previously-Unreleased entries (Phase 14.2.3, 14.2.x, 14.1.x, 14, 13.6,
13.5, 13.2, 13.1) carried forward unchanged into the v0.9.0 block. Fresh
empty Unreleased section seeded above.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* chore(submodule): bump homebrew-mdemg to v0.9.0 formula + docs

Bumps packaging/homebrew-mdemg pointer a235977 -> 6077097, which incorporates:
- f9358cd  Brew formula update for mdemg version v0.8.5 (goreleaser, prior)
- b4a0d2c  Brew formula update for mdemg version v0.9.0 (goreleaser, this release)
- 6077097  docs: v0.9.0 -- CHANGELOG, README What's New, beta-testing version pin

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(api): /healthz returns build-time version, not stale literal "0.6.0"

`config.FromEnv()` defaulted MdemgVersion/MdemgCommit to literal "0.6.0"/
"unknown" when MDEMG_VERSION/MDEMG_COMMIT envs were unset. Both /healthz
and /readyz serialize cfg.MdemgVersion, so they reported "0.6.0" forever
regardless of the actual binary's ldflags-injected cli.Version.

Fix: defaults to "" in config; cli/config_loader.go injects cli.Version /
cli.Commit (the build-time vars set by goreleaser ldflags) when the env
override is unset. Operators can still pin via MDEMG_VERSION env.

Live-verified: dev build (no ldflags) now reports {"version":"dev"} on
/healthz instead of the lying "0.6.0". Production builds via goreleaser
will report the real semver tag.

TestHandleHealthz unaffected (sets cfg.MdemgVersion directly).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(service): replace decommissioned mlx-server LaunchAgent with llama-server

Phase 13.5 cutover (2026-05-03) replaced mlx_lm.server (port 8101) with
llama.cpp llama-server (port 8102) as the production LLM runtime, but the
embedded launchd plist template + service install code paths were never
updated. Any operator running 'mdemg service install' from a fresh checkout
got the decommissioned mlx_lm.server agent — mdemg's startup preflight then
failed because LLM_ENDPOINT=http://127.0.0.1:8102/v1 wasn't reachable.

Changes:
- New packaging/launchd/com.mdemg.llama-server.plist with the Phase 13.5
  production flags (--ctx-size 32768 --parallel 4 --cont-batching --metrics
  --jinja). Byte-identical mirror at internal/cli/launchd_templates/ for
  the embed.FS (CI sync-check enforced).
- Removed packaging/launchd/com.mdemg.mlx-server.plist + embed.FS mirror.
  mlx_lm.server is decommissioned and known-broken on M5 + macOS 26.3.x;
  keeping the template would just risk re-deploying it.
- internal/cli/service_darwin.go: launchdServices entry replaced with
  com.mdemg.llama-server. resolveMLXLMBin renamed to resolveLlamaServerBin
  with primary env MDEMG_LLAMA_SERVER_BIN, deprecation alias for
  MDEMG_MLX_LM_BIN (slog.Warn at boot, retained ≥1 release cycle per the
  Phase 13.6 deprecation pattern), PATH lookup of `llama-server`.
  resolveMDEMGModelPath default updated to the canonical Phase 13.5 GGUF
  filepath (.local-models/mdemg-llm-v1-gguf/mdemg-llm-v1.Q5_K_M.gguf) since
  llama-server takes a `.gguf` filepath, not an HF-format directory like
  mlx_lm.server. Install error message updated for the new env var name +
  remediation steps (`brew install llama.cpp`).
- migrateLegacyMLXServerPlist() added: if a pre-cutover com.mdemg.mlx-server
  plist is bootstrapped on the operator's machine, Install() boots it out
  and renames the file to .disabled-phase13_5 (matches the manual operator
  convention from Phase 13.5 rollout). Best-effort: failures don't block
  the install.
- internal/cli/service_darwin_test.go fully rewritten:
    * TestLaunchdServicesIncludesLlamaServer asserts the new entry exists
      and is Optional=false (production matches Hotfix 11.6.3.1; the old
      test asserted Optional=true, a latent lie since 2026-05-02 that
      Linux CI never caught because of //go:build darwin)
    * TestLlamaServerPlistEmbedded replaces TestMLXServerPlistEmbedded;
      additionally asserts mlx-server.plist is NOT in embed.FS
    * Two resolver tests for the primary env var
    * New TestResolveLlamaServerBinFallsBackToMLXAlias proves the
      Phase 13.6 deprecation alias path works
    * resolveMDEMGModelPath tests updated for the new GGUF default
- internal/cli/watchdog.go: help text references com.mdemg.llama-server
  (instead of com.mdemg.mlx-server) and llama-server (instead of
  mlx_lm.server). Notes that mdemg_mlx_health_state metric name is
  retained for dashboard compatibility.

Tested:
- Tier 1 unit: 7/7 new tests pass; full ./internal/cli/... suite green
  (61s wall-clock).
- Tier 2 integration: golangci-lint run ./internal/cli/ — 0 issues.
  CI plist sync-check (diff -q packaging/launchd/*.plist
  internal/cli/launchd_templates/) — 6/6 byte-identical.
- Tier 3 live e2e: deferred. Running mdemg service install on the
  operator's currently-serving machine would briefly bootout the running
  llama-server LaunchAgent (PID 20527 actively serving production
  inference). The hand-installed llama-server plist on the operator's
  machine is byte-equivalent (modulo template substitutions) to what
  this commit will install via `mdemg service install` on a fresh
  operator setup, so the operator can verify on next planned redeploy.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Roger Henley <rogerhenley345@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants