Skip to content

feat(hermes): Phase 2d-i — system-prompt.md injection on all 3 dispatch paths - #276

Merged
HongmingWang-Rabbit merged 1 commit into
mainfrom
feat/hermes-phase2d-i-system-prompt
Apr 15, 2026
Merged

feat(hermes): Phase 2d-i — system-prompt.md injection on all 3 dispatch paths#276
HongmingWang-Rabbit merged 1 commit into
mainfrom
feat/hermes-phase2d-i-system-prompt

Conversation

@HongmingWang-Rabbit

Copy link
Copy Markdown
Contributor

Why

Pre-this-PR bug: the Hermes adapter never read `/configs/system-prompt.md`. Any workspace that switched to `runtime: hermes` silently lost its role identity because the system prompt wasn't passed to the model. Silent correctness regression that our live test suite wouldn't catch (no live workspace uses the hermes runtime today).

Change shape

  1. `HermesA2AExecutor.init` takes new optional `config_path` kwarg → stored on `self._config_path`
  2. `create_executor(config_path=...)` forwards to constructor
  3. `adapter.py` passes `config.config_path` through from `AdapterConfig`
  4. `execute()` reads `system-prompt.md` via `executor_helpers.get_system_prompt` (hot-reload — reads on every turn)
  5. `_do_inference(user_message, history, system_prompt)` — threads through to each path
  6. Each path uses the provider's NATIVE system field:
    • OpenAI-compat: `{"role":"system","content":...}` at head of messages
    • Anthropic: top-level `system=` kwarg (NOT inline — Anthropic requires top-level)
    • Gemini: `config=GenerateContentConfig(system_instruction=...)`

Phase scoreboard

Phase What Status
2a Native Anthropic dispatch infra ✅ in main (#240)
2b Native Gemini dispatch ✅ in main (#255)
2c Multi-turn history on all paths ✅ in main (#267)
2d-i system_prompt on all paths this PR
2d-ii Tool calling on native paths queued
2d-iii Vision content blocks queued
2d-iv Streaming queued

Test coverage

46/46 pass (20 Phase 2 dispatch + 26 Phase 1 registry). 4 new tests:

  • `dispatch_passes_system_prompt_to_anthropic/gemini/openai` — happy path flow
  • `executor_accepts_config_path_kwarg` — constructor stores config_path
  • `create_executor_forwards_config_path` — both back-compat + registry paths

Existing Phase 2c dispatch tests updated: `("hello", None)` → `("hello", None, None)`.

Back-compat

  • `config_path=None` (default) → `execute()` skips system-prompt injection, same behavior as pre-2d-i
  • Missing `/configs/system-prompt.md` → `get_system_prompt` returns `None` → passes through as skipped
  • The 13 OpenAI-compat providers work identically — adding a leading system message is standard
  • Anthropic + Gemini previously got zero system context; now they get the same prompt Claude-code workspaces get

Impact

With this PR, `runtime: hermes` becomes a true drop-in alternative to `runtime: claude-code`. A workspace flipping to Hermes keeps its role identity + project conventions + CLAUDE.md context because the system prompt actually reaches the model.

Related

  • `project_hermes_multi_provider.md` — Phase 2d-i was the next queued item after 2c
  • CEO 2026-04-15: "focus on supporting hermes agent"

…ch paths

The Hermes adapter never read /configs/system-prompt.md. Any role that
switched to runtime: hermes was silently losing its role identity because
the system prompt wasn't passed to the model. This PR fixes that by:

1. HermesA2AExecutor.__init__ takes new optional `config_path` kwarg
2. `create_executor(config_path=...)` forwards to the constructor
3. `adapter.py` passes `config.config_path` through from AdapterConfig
4. `execute()` reads system-prompt.md via executor_helpers.get_system_prompt
   (hot-reload-capable — reads on every turn, not just at startup)
5. `_do_inference(user_message, history, system_prompt)` — new arg threads
   through the dispatch to each native path
6. Each path uses the provider's NATIVE system field:
   - OpenAI-compat: prepends `{"role":"system", "content":...}` to messages
   - Anthropic: top-level `system=` kwarg (NOT in messages — Anthropic
     requires system at the top level)
   - Gemini: `config=GenerateContentConfig(system_instruction=...)`

## Phase scoreboard
- 2a (in main) — native Anthropic dispatch infra
- 2b (in main) — native Gemini dispatch
- 2c (in main) — multi-turn history on all paths
- **2d-i (this PR)** — system prompts on all paths
- 2d-ii (future) — tool calling on native paths
- 2d-iii (future) — vision content blocks on native paths
- 2d-iv (future) — streaming

## Test coverage

46/46 tests pass (20 Phase 2 dispatch + 26 Phase 1 registry):

- Existing dispatch tests updated to assert the 3-arg call shape
  `("hello", None, None)` — history + system_prompt both None
- 4 new tests:
  - `dispatch_passes_system_prompt_to_anthropic` — happy path, third arg flows
  - `dispatch_passes_system_prompt_to_gemini` — happy path
  - `dispatch_passes_system_prompt_to_openai` — happy path
  - `executor_accepts_config_path_kwarg` — constructor stores config_path
  - `create_executor_forwards_config_path` — both back-compat and registry
    resolution paths forward config_path through to the executor

## Back-compat

- `config_path=None` (default) → execute() skips system-prompt injection,
  same behavior as pre-2d-i
- Workspaces with `runtime: hermes` but no `/configs/system-prompt.md`
  file get `system_prompt=None` (get_system_prompt returns fallback),
  same as before
- The 13 OpenAI-compat providers work identically — system_prompt just
  adds a leading message, which every OpenAI-compat endpoint already
  supports
- Anthropic + Gemini previously got zero system context; now they get
  the same system prompt the workspace's system-prompt.md carries

## Why this matters

Before this PR: if someone flipped a workspace from `runtime: claude-code`
to `runtime: hermes`, the agent would act generically (no role identity,
no project conventions, no CLAUDE.md context) because the Hermes executor
never looked at system-prompt.md. That's a silent correctness regression
the test suite wouldn't catch because none of our live workspaces use
the hermes runtime today.

With this PR: Hermes workspaces get the same system prompt injection as
Claude-code workspaces, making the `runtime: hermes` switch a true drop-in
alternative.

## Related
- #267 Phase 2c (multi-turn history — in main)
- #255 Phase 2b (gemini native — in main)
- #240 Phase 2a (anthropic native — in main)
- #208 Phase 1 (provider registry — in main)
- project_hermes_multi_provider.md — Phase 2d-i was the next queued item
@HongmingWang-Rabbit
HongmingWang-Rabbit merged commit b31a5a4 into main Apr 15, 2026
6 checks passed
@HongmingWang-Rabbit
HongmingWang-Rabbit deleted the feat/hermes-phase2d-i-system-prompt branch April 15, 2026 23:53
molecule-ai Bot pushed a commit that referenced this pull request Apr 21, 2026
…-prompt

feat(hermes): Phase 2d-i — system-prompt.md injection on all 3 dispatch paths
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.

1 participant