fix(runtime): replace remaining /app/ legacy paths in agent prompts + docstrings

Comprehensive sweep follow-up to the MCP server path fix. Audited every
/app/ reference in the runtime source against the live claude-code
template image and confirmed the actual /app/ contents post-#87 are
ONLY: __init__.py, adapter.py, claude_sdk_executor.py, requirements.txt
— every other workspace module ships in the wheel under
site-packages/molecule_runtime/. Two more leaks found:

1. executor_helpers.py:_A2A_INSTRUCTIONS_CLI — inter-agent system prompt
   for non-MCP runtimes (Ollama, custom) had 5 lines telling the model
   `python3 /app/a2a_cli.py X`. Models copy these examples verbatim, so
   every CLI-runtime delegation would fail at the shell layer (no such
   file). Replaced with `python3 -m molecule_runtime.a2a_cli` form,
   which works regardless of where the wheel is installed.

2. molecule_ai_status.py docstring — usage examples invoked
   `python3 /app/molecule_ai_status.py` and claimed a
   `molecule-monorepo-status` shell alias. Both broken in current
   templates: the file's at site-packages, and `which
   molecule-monorepo-status` errors (the legacy symlink only existed
   in the dev-only workspace/Dockerfile base image, not in the
   standalone template Dockerfiles that ship to production).
   Updated docstring + the __main__ usage banner + the stderr error
   prefix to use the same `python3 -m molecule_runtime.X` form.

Plugins audited and clean: WORKSPACE_PLUGINS_DIR=/configs/plugins,
SHARED_PLUGINS_DIR=$PLUGINS_DIR fallback /plugins. No /app/
assumptions.

Regression test: `test_a2a_cli_instructions_use_module_invocation_not_legacy_app_path`
asserts the legacy /app/a2a_cli.py path can't drift back into the CLI
system prompt and that the canonical module form is present.

The legacy workspace/Dockerfile + workspace/entrypoint.sh + workspace/scripts/
still contain /app/-shaped paths but are dev-only base-image scaffolding
(per workspace/build-all.sh's own header comment) — not shipped to the
standalone template images. Out of scope here; can be cleaned up in a
separate dead-code pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hongming Wang 2026-04-27 11:22:00 -07:00
parent 203a4f0f91
commit 28fc7a8cbd
3 changed files with 29 additions and 15 deletions

View File

@ -299,11 +299,11 @@ Instead: (1) try delegating to a different peer, (2) handle the task yourself, o
_A2A_INSTRUCTIONS_CLI = """## Inter-Agent Communication
You can delegate tasks to other workspaces using the a2a command:
python3 /app/a2a_cli.py peers # List available peers
python3 /app/a2a_cli.py delegate <workspace_id> <task> # Sync: wait for response
python3 /app/a2a_cli.py delegate --async <workspace_id> <task> # Async: return task_id
python3 /app/a2a_cli.py status <workspace_id> <task_id> # Check async task
python3 /app/a2a_cli.py info # Your workspace info
python3 -m molecule_runtime.a2a_cli peers # List available peers
python3 -m molecule_runtime.a2a_cli delegate <workspace_id> <task> # Sync: wait for response
python3 -m molecule_runtime.a2a_cli delegate --async <workspace_id> <task> # Async: return task_id
python3 -m molecule_runtime.a2a_cli status <workspace_id> <task_id> # Check async task
python3 -m molecule_runtime.a2a_cli info # Your workspace info
For quick questions, use sync delegate. For long tasks, use --async + status.
Only delegate to peers listed by the peers command (access control enforced)."""

View File

@ -4,14 +4,10 @@
Usage (from any script, cron job, or shell inside the container):
# Set current task (shows on canvas card)
python3 /app/molecule_ai_status.py "Running weekly SEO audit..."
python3 -m molecule_runtime.molecule_ai_status "Running weekly SEO audit..."
# Clear task (removes banner from canvas)
python3 /app/molecule_ai_status.py ""
# Or use the shell alias:
molecule-monorepo-status "Analyzing competitor data..."
molecule-monorepo-status ""
python3 -m molecule_runtime.molecule_ai_status ""
The status appears as an amber banner on the workspace card in the canvas,
visible to the project owner in real-time.
@ -63,13 +59,13 @@ def set_status(task: str):
timeout=5.0,
)
except Exception as e:
print(f"molecule-monorepo-status: failed to update: {e}", file=sys.stderr)
print(f"molecule_ai_status: failed to update: {e}", file=sys.stderr)
if __name__ == "__main__": # pragma: no cover
if len(sys.argv) < 2:
print("Usage: molecule-monorepo-status 'task description'")
print(" molecule-monorepo-status '' # clear")
print("Usage: python3 -m molecule_runtime.molecule_ai_status 'task description'")
print(" python3 -m molecule_runtime.molecule_ai_status '' # clear")
sys.exit(1)
set_status(sys.argv[1])

View File

@ -445,10 +445,28 @@ def test_get_a2a_instructions_mcp_default():
def test_get_a2a_instructions_cli_variant():
out = get_a2a_instructions(mcp=False)
assert "a2a_cli.py" in out
assert "a2a_cli" in out
assert "MCP tools" not in out
def test_a2a_cli_instructions_use_module_invocation_not_legacy_app_path():
# The CLI variant of the a2a instructions ships in the agent system
# prompt for non-MCP runtimes (Ollama, custom). The model copies the
# invocation form verbatim into shell calls, so any path drift here
# silently breaks delegation. The legacy /app/a2a_cli.py path was
# correct under the pre-#87 monolithic-template Docker layout but
# stops resolving once the runtime ships as a wheel — pin the
# canonical `python3 -m molecule_runtime.a2a_cli` form so future
# refactors can't silently regress it.
out = get_a2a_instructions(mcp=False)
assert "/app/a2a_cli.py" not in out, (
"Legacy /app/a2a_cli.py path leaked back into the CLI-variant "
"system prompt — agents on Ollama/custom runtimes would copy "
"this verbatim and every delegation would fail."
)
assert "python3 -m molecule_runtime.a2a_cli" in out
def test_a2a_mcp_instructions_reference_existing_tools():
"""The MCP instructions text must only reference tools that are actually
registered in a2a_mcp_server.py. If someone renames a server tool, the