When your "extensible" feature never works: trace the root, not just the hook
The my-ai-team relay has a .my-ai-team/<mode>.md prompt override mechanism — drop a markdown file into a directory and it replaces the shipped system prompt for that agent mode. It never worked in practice. The code was there, the tests passed, but real users' override files were never found.
The bug was a root resolution mismatch, not a logic error.
The mechanism
_mux_prompt_override() in lib/relay-runtime.sh decides which prompt file an agent gets:
relay_root="$(_mux_relay_root)"
override_path="${relay_root}/.my-ai-team/${mode}.md"
It looks for .my-ai-team/<mode>.md under the relay root, then falls back to agents/<mode>.md.
What went wrong
_mux_relay_root() returns the install root (~/.local/share/my-ai-team/), not the project root. Meanwhile mux-agent-launch correctly receives MUX_PROJECT_ROOT (the user's project directory) and even cds into it — but _mux_prompt_override() never sees it.
Users put .my-ai-team/ in their project. The code only looks in the install dir. Ship mismatch.
Two problems stacked
- Project root never checked — the override path only searches the install root
- Install wipes install-root overrides —
install.shdoes a full directory swap on every run (prepare_install_root_swaprenames the old install root, moves staging in, deletes backup). A.my-ai-team/manually placed at the install root is destroyed on reinstall
The fix: move global overrides out of managed territory
Instead of trying to preserve user data inside the install root (which install.sh owns and swaps), put global overrides somewhere install.sh never touches:
Resolution order:
1. ${MUX_PROJECT_ROOT}/.my-ai-team/<mode>.md — project-level (highest priority)
2. ~/.config/my-ai-team/<mode>.md — user-global (XDG config)
3. ${relay_root}/agents/<mode>.md — shipped default
No install.sh changes needed. The relay root stays a clean install-managed directory. User data lives in ~/.config/, which is user-owned by convention.
Why tests didn't catch it
The existing tests in test/prompt-override_test.sh test _mux_prompt_override() in isolation — they set up a fake relay root with .my-ai-team/ under it and verify the function finds it. The tests pass because the function's internal logic is correct. What's wrong is the root that gets passed in at runtime: the function is called with the install root when it should also check the project root. Tests that mock only one root won't catch a missing root.