Skip to content

claude-native: agent prompt/instructions are never delivered to the harness (affects bundled polly sub-agents) #3269

Description

@sonsay

Summary

Agents running on claude-native never receive their own prompt / instructions. The agent spec is resolved and the composed instructions are built, but the native path never delivers them to Claude Code — so the session behaves as plain Claude Code with no role, scope or constraints.

This affects the bundled polly orchestrator directly: all six of its sub-agents are declared as *-native, and each ships a detailed role prompt that never arrives.

Controlled experiment

Parent agent on claude-sdk with one sub-agent. The sub-agent's prompt contains a code word:

# agents/worker/config.yaml
prompt: |
  You are a PlusTeam probe worker. Your code word is: SUBPROMPT-ALIVE.
  If asked for the code word or who you are, answer exactly:
  "SUBPROMPT-ALIVE, I am the PlusTeam probe worker".

The parent dispatches via sys_session_send and returns the reply verbatim. The only variable changed between runs is the sub-agent's harness:

sub-agent harness reply
claude-sdk SUBPROMPT-ALIVE, I am the PlusTeam probe worker
claude-native No code word is set — I don't see one. I am Claude Code (Sonnet 5)

Same parent, same spec, same dispatch. The prompt is present in the parsed spec in both cases (AgentSpec.instructions is populated — verified by parsing the bundle directly).

The same happens for a top-level (non-sub-agent) session: a claude-native agent asked to quote its own role answered "there is no such block in my context, neither as a file nor as an injection".

Where it is lost

augment_claude_args(...) already accepts append_system_prompt and forwards it to Claude Code's --append-system-prompt flag. But both call sites pass only the framework's session-rename instruction:

  • runner/app.py:5825-5840 (managed-host path) — append_system_prompt=session_rename_instruction(...)
  • claude_native.py:4166 / claude_native.py:3229 (local CLI path) — same

Meanwhile the composed instructions are produced: build_instructions(cached_spec, …) at runner/app.py:13822, placed into harness_body["instructions"] at runner/app.py:13874-13875. Nothing on the native bridge consumes that field — the only instructions hit in claude_native_bridge.py is MCP serverInfo, unrelated.

Inspecting the live bwrap command line of a running native session confirms it: the launch carries --mcp-config and --settings, and no --system-prompt / --append-system-prompt / --agent.

Impact on polly

resources/examples/polly/agents/{claude_code,codex,opencode,cursor,hermes}/config.yaml are all *-native and each carries ~1.7k characters of role: the IMPLEMENT / REVIEW / EXPLORE contract, "stay strictly within the named scope", the Co-authored-by: omnigent <noreply@omnigent.ai> commit trailer, "never push to a protected branch or force-push", and "report with file:line evidence".

None of that reaches the worker. Polly still functions because the orchestrator restates purpose and contract inside every dispatched task — but the sub-agent role definitions in the shipped example are inert, and any behaviour they are meant to guarantee (the commit trailer, the force-push ban, the review-never-edits rule) rests on the task text alone.

Proposed fix

Pass the session's resolved agent instructions into append_system_prompt, merged with the existing framework instruction rather than replacing it — append_framework_instructions() in runtime/prompt.py already implements exactly this ordering (user-authored first, framework metadata last).

Concretely, at runner/app.py:5834:

append_system_prompt=append_framework_instructions(
    resolved_instructions,                    # from build_instructions(cached_spec, …)
    [session_rename_instruction(initial_session=…)],
),

Notes:

  • AgentSpec.instructions is already resolved for sub-agents on this path — _find_spec_by_name swaps in the child's spec before dispatch (runner/app.py:13757+), so the correct prompt is in hand at that point.
  • If the omission is deliberate (the docstring calls the channel "framework-owned instructions"), it would help to say so in the docs and to mark the sub-agent prompt: field in examples/polly as non-functional for native harnesses — right now it reads as working configuration.

Environment

  • Omnigent 0.6.0, self-hosted server, Linux (Ubuntu 24.04)
  • Claude Code 2.1.215, subscription auth
  • Reproduced on both the local-runner path (omnigent run <bundle> --server …) and a top-level session

Test bundles and sessions were deleted after the runs. Happy to re-run the experiment against a patch.

Metadata

Metadata

Assignees

Labels

BugSomething isn't workingP1-highPriority: major feature broken, no workaroundcomp:harness-t1Highest-usage harnessescomp:harnessesComponent: SDK harnesses (Claude, Cursor, etc.)comp:runnerComponent: agent runner, execution enginetriagedIssue has been triaged by the botvalidated:reproducedRepro agent validated the issue can be reproduced

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions