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 _A2A_INSTRUCTIONS_CLI = """## Inter-Agent Communication
You can delegate tasks to other workspaces using the a2a command: You can delegate tasks to other workspaces using the a2a command:
python3 /app/a2a_cli.py peers # List available peers python3 -m molecule_runtime.a2a_cli peers # List available peers
python3 /app/a2a_cli.py delegate <workspace_id> <task> # Sync: wait for response python3 -m molecule_runtime.a2a_cli delegate <workspace_id> <task> # Sync: wait for response
python3 /app/a2a_cli.py delegate --async <workspace_id> <task> # Async: return task_id python3 -m molecule_runtime.a2a_cli delegate --async <workspace_id> <task> # Async: return task_id
python3 /app/a2a_cli.py status <workspace_id> <task_id> # Check async task python3 -m molecule_runtime.a2a_cli status <workspace_id> <task_id> # Check async task
python3 /app/a2a_cli.py info # Your workspace info python3 -m molecule_runtime.a2a_cli info # Your workspace info
For quick questions, use sync delegate. For long tasks, use --async + status. For quick questions, use sync delegate. For long tasks, use --async + status.
Only delegate to peers listed by the peers command (access control enforced).""" 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): Usage (from any script, cron job, or shell inside the container):
# Set current task (shows on canvas card) # 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) # Clear task (removes banner from canvas)
python3 /app/molecule_ai_status.py "" python3 -m molecule_runtime.molecule_ai_status ""
# Or use the shell alias:
molecule-monorepo-status "Analyzing competitor data..."
molecule-monorepo-status ""
The status appears as an amber banner on the workspace card in the canvas, The status appears as an amber banner on the workspace card in the canvas,
visible to the project owner in real-time. visible to the project owner in real-time.
@ -63,13 +59,13 @@ def set_status(task: str):
timeout=5.0, timeout=5.0,
) )
except Exception as e: 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 __name__ == "__main__": # pragma: no cover
if len(sys.argv) < 2: if len(sys.argv) < 2:
print("Usage: molecule-monorepo-status 'task description'") print("Usage: python3 -m molecule_runtime.molecule_ai_status 'task description'")
print(" molecule-monorepo-status '' # clear") print(" python3 -m molecule_runtime.molecule_ai_status '' # clear")
sys.exit(1) sys.exit(1)
set_status(sys.argv[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(): def test_get_a2a_instructions_cli_variant():
out = get_a2a_instructions(mcp=False) out = get_a2a_instructions(mcp=False)
assert "a2a_cli.py" in out assert "a2a_cli" in out
assert "MCP tools" not 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(): def test_a2a_mcp_instructions_reference_existing_tools():
"""The MCP instructions text must only reference tools that are actually """The MCP instructions text must only reference tools that are actually
registered in a2a_mcp_server.py. If someone renames a server tool, the registered in a2a_mcp_server.py. If someone renames a server tool, the