---
phase: 29-default-provider
plan: 02
type: execute
wave: 2
depends_on: ["29-01"]
files_modified:
  - ui/src/components/NexusOnboardingWizard.tsx
  - server/src/__tests__/29-default-provider.test.ts
autonomous: true
requirements: [DFLT-03, DFLT-04]

must_haves:
  truths:
    - "A Hermes agent created via the wizard has a promptTemplate containing Nexus skill bundle content (HEARTBEAT.md, TOOLS.md)"
    - "The skill bundle is role-specific — a PM agent gets ceo/ bundle, an Engineer gets engineer/ bundle"
    - "The probe route returns available true/false based on adapter testEnvironment result"
    - "Agent creation with hermes_local and promptTemplate produces a valid agent record"
  artifacts:
    - path: "ui/src/components/NexusOnboardingWizard.tsx"
      provides: "Hermes promptTemplate with skill injection"
      contains: "promptTemplate"
    - path: "server/src/__tests__/29-default-provider.test.ts"
      provides: "Probe route + wizard flow validation tests"
      contains: "29-default-provider"
  key_links:
    - from: "ui/src/components/NexusOnboardingWizard.tsx"
      to: "server/src/routes/agents.ts"
      via: "agentsApi.create with promptTemplate in adapterConfig"
      pattern: "promptTemplate"
    - from: "server/src/routes/agents.ts"
      to: "server/src/services/default-agent-instructions.ts"
      via: "loadDefaultAgentInstructionsBundle resolves promptTemplate to skill files"
      pattern: "loadDefaultAgentInstructionsBundle"
---

<objective>
Inject Nexus skill bundles into Hermes agent promptTemplate so GSD workflows execute correctly, and add integration tests validating the full probe-to-agent-creation flow.

Purpose: Without skill injection, Hermes agents created via the wizard would not follow the Nexus heartbeat loop (HEARTBEAT.md, TOOLS.md). This plan closes the skill injection gap (DFLT-03) and validates the end-to-end flow (DFLT-04).
Output: promptTemplate injection in wizard, integration test file.
</objective>

<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/29-default-provider/29-RESEARCH.md
@.planning/phases/29-default-provider/29-01-SUMMARY.md

<interfaces>
<!-- From Plan 01 — probe route and wizard changes -->

From server/src/routes/agents.ts (Plan 01 addition):
```typescript
router.get("/adapters/:type/probe", async (req, res) => {
  // Board auth only, returns { available: boolean, status: string, checks?: ... }
});
```

From ui/src/api/agents.ts (Plan 01 addition):
```typescript
probeAdapter: (type: string) =>
  api.get<{ available: boolean; status: string }>(`/adapters/${encodeURIComponent(type)}/probe`),
```

From server/src/services/default-agent-instructions.ts:
```typescript
export async function loadDefaultAgentInstructionsBundle(
  role: DefaultAgentBundleRole
): Promise<Record<string, string>>;
// Returns { "AGENTS.md": "...", "HEARTBEAT.md": "...", "SOUL.md": "...", "TOOLS.md": "..." }

export function resolveDefaultAgentInstructionsBundleRole(role: string): DefaultAgentBundleRole;
// "ceo" => "ceo", "engineer" => "engineer", "general" => "general", else => "default"
```

From server/src/routes/agents.ts (lines 458-484) — agent creation skill injection:
```typescript
// When agent has no explicit instructions bundle and promptTemplate is non-empty:
// promptTemplate content goes to { "AGENTS.md": promptTemplate }
// Then materializeManagedBundle writes the files and updates adapterConfig
// Crucially: delete nextAdapterConfig.promptTemplate after materialization
```

From ui/src/components/NexusOnboardingWizard.tsx (Plan 01 state):
```typescript
const [defaultAdapter, setDefaultAdapter] = useState<"claude_local" | "hermes_local">("claude_local");
// handleSubmit creates 3 agents with adapterType: defaultAdapter
```
</interfaces>
</context>

<tasks>

<task type="auto">
  <name>Task 1: Add Hermes promptTemplate skill injection to NexusOnboardingWizard</name>
  <read_first>
    - ui/src/components/NexusOnboardingWizard.tsx (full file — post Plan 01 version)
    - server/src/services/default-agent-instructions.ts (full file, 34 lines)
    - server/src/routes/agents.ts (lines 455-485 for promptTemplate handling in ensureDefaultInstructionsBundle)
    - server/src/onboarding-assets/ceo/HEARTBEAT.md (to understand content structure)
  </read_first>
  <files>ui/src/components/NexusOnboardingWizard.tsx</files>
  <action>
The server-side agent creation flow in agents.ts already handles promptTemplate injection:
- If adapterConfig.promptTemplate is a non-empty string, it wraps it as {"AGENTS.md": promptTemplate} and materializes it
- This means the wizard just needs to pass a promptTemplate containing the Nexus skill content

For Hermes agents, the wizard should pass a `promptTemplate` field in adapterConfig that contains the Hermes-specific preamble + instructions for the Nexus heartbeat workflow. The server's `ensureDefaultInstructionsBundle` will then handle materialization.

**However**, looking at the server code more carefully: when `promptTemplate` is non-empty, it ONLY creates `AGENTS.md` from it — it does NOT load HEARTBEAT.md, TOOLS.md, SOUL.md. When `promptTemplate` is empty/absent, it loads the full bundle via `loadDefaultAgentInstructionsBundle`.

**Key insight:** For Hermes agents, we should NOT set promptTemplate in adapterConfig. Instead, we should let the server's default path handle it — `loadDefaultAgentInstructionsBundle` will load the full role-specific bundle (AGENTS.md + HEARTBEAT.md + SOUL.md + TOOLS.md) and materialize all files. The Hermes adapter's `execute.ts` does NOT read these files, but the heartbeat service's `ctx.context.skills` mechanism already populates them from the DB — the skill content is served to the agent via the `/api/agents/me` endpoint which the HEARTBEAT.md workflow instructs the agent to call.

**Therefore:** The wizard does NOT need to inject a custom promptTemplate. The existing server-side flow already works for Hermes:
1. Agent created with no promptTemplate => server loads full bundle via `loadDefaultAgentInstructionsBundle`
2. Bundle materialized as managed files in the DB
3. When heartbeat runs, `ctx.context.skills` contains the skill content
4. The HEARTBEAT.md content instructs the agent to call `GET /api/agents/me` to get its instructions
5. The Hermes adapter has `supportsLocalAgentJwt: true` so the PAPERCLIP_API_KEY is injected automatically

The one addition needed: a Hermes-specific system prompt that tells the agent to follow the Nexus workflow. The Hermes DEFAULT_PROMPT_TEMPLATE already handles task assignment and API calls — but it may not include the "consult your managed instructions" step.

**Approach:** Add a `promptTemplate` in adapterConfig ONLY for hermes_local agents that contains a system-level instruction to follow the Nexus heartbeat workflow. This goes through the server's promptTemplate path, creating an AGENTS.md that supplements the Hermes default prompt:

```typescript
// In handleSubmit, after the adapterConfig line, before the create calls:
const hermesPromptTemplate = [
  `You are "{{agentName}}", an AI agent managed by ${VOCAB.appName}.`,
  "",
  "Your identity:",
  "  Agent ID: {{agentId}}",
  "  Company ID: {{companyId}}",
  "  API Base: {{paperclipApiUrl}}",
  "  Run ID: {{runId}}",
  "",
  "IMPORTANT: Use the `terminal` tool with `curl` for ALL API calls.",
  'IMPORTANT: Always include `-H "X-Paperclip-Run-Id: {{runId}}"` on API calls that modify data.',
  "",
  "Before starting any task:",
  "1. Call `GET {{paperclipApiUrl}}/api/agents/me` to retrieve your managed instructions",
  "2. Follow the HEARTBEAT.md workflow from your instructions",
  "3. Use TOOLS.md for available API endpoints",
  "",
  "{{#taskId}}",
  "Assigned task: {{taskId}} - {{taskTitle}}",
  "{{/taskId}}",
].join("\n");

// Then for each create call, when defaultAdapter === "hermes_local":
const finalAdapterConfig = defaultAdapter === "hermes_local"
  ? { ...adapterConfig, promptTemplate: hermesPromptTemplate, persistSession: true }
  : adapterConfig;

// Pass finalAdapterConfig instead of adapterConfig in all three agentsApi.create calls
```

Make sure the Generalist agent's metadata `pendingSkillGroups: ["Creative"]` is preserved regardless of adapter type (line 118).
  </action>
  <verify>
    <automated>cd /opt/nexus && pnpm --filter ui exec tsc --noEmit 2>&1 | head -20</automated>
  </verify>
  <acceptance_criteria>
    - grep -q 'promptTemplate' ui/src/components/NexusOnboardingWizard.tsx
    - grep -q 'persistSession' ui/src/components/NexusOnboardingWizard.tsx
    - grep -q 'HEARTBEAT' ui/src/components/NexusOnboardingWizard.tsx (references heartbeat workflow in the prompt)
    - grep -q 'agents/me' ui/src/components/NexusOnboardingWizard.tsx (instructs agent to fetch its instructions)
  </acceptance_criteria>
  <done>
    - Hermes agents created via wizard get a promptTemplate that instructs the agent to follow the Nexus heartbeat workflow
    - promptTemplate includes Mustache variables (agentName, agentId, companyId, paperclipApiUrl, runId, taskId, taskTitle)
    - persistSession: true is set for Hermes agents
    - Claude_local agents are unaffected (no promptTemplate, same as before)
  </done>
</task>

<task type="auto">
  <name>Task 2: Add integration tests for probe route, wizard agent creation, and end-to-end flow</name>
  <read_first>
    - server/src/__tests__/heartbeat-workspace-session.test.ts (first 50 lines — for in-memory DB setup pattern)
    - server/src/__tests__/adapter-session-codecs.test.ts (first 30 lines — for test setup pattern)
    - server/src/routes/agents.ts (lines 1-20 for router setup, line 667 area for probe route from Plan 01)
    - server/src/adapters/index.ts (findServerAdapter signature)
  </read_first>
  <files>server/src/__tests__/29-default-provider.test.ts</files>
  <action>
Create a new test file at `server/src/__tests__/29-default-provider.test.ts` with unit tests covering:

**Test group 1: Adapter probe route logic**
These test the probe logic directly (no HTTP — test the route handler's logic):

```typescript
import { describe, it, expect, vi } from "vitest";
```

Since testing the Express route directly is complex, test the adapter probe logic by calling `findServerAdapter` and `testEnvironment` directly:

1. `it("findServerAdapter returns hermes_local adapter with testEnvironment")` — verify the adapter exists and has testEnvironment function
2. `it("hermes testEnvironment handles missing CLI gracefully")` — if hermes is not installed in CI, verify the result contains a check with level "error" and a code containing "not_found" or "cli"

**Test group 2: Hermes promptTemplate construction**
Extract the promptTemplate construction logic from the wizard into a testable helper OR test the expected string content:

3. `it("hermes promptTemplate contains required Mustache variables")` — build the template string and verify it contains `{{agentName}}`, `{{agentId}}`, `{{companyId}}`, `{{paperclipApiUrl}}`, `{{runId}}`, `{{taskId}}`, `{{taskTitle}}`
4. `it("hermes promptTemplate instructs agent to call /api/agents/me")` — verify the template contains `agents/me`
5. `it("hermes promptTemplate mentions HEARTBEAT.md workflow")` — verify the template references the heartbeat workflow

**Test group 3: Default agent instructions bundle (DFLT-03)**
6. `it("loadDefaultAgentInstructionsBundle loads ceo bundle with all 4 files")` — call with "ceo", verify keys include AGENTS.md, HEARTBEAT.md, SOUL.md, TOOLS.md
7. `it("loadDefaultAgentInstructionsBundle loads engineer bundle with all 4 files")` — same for "engineer"
8. `it("resolveDefaultAgentInstructionsBundleRole maps known roles correctly")` — verify ceo->ceo, engineer->engineer, general->general, unknown->default

Import `loadDefaultAgentInstructionsBundle` and `resolveDefaultAgentInstructionsBundleRole` from `../services/default-agent-instructions.js`.

For test group 2, define the promptTemplate string directly in the test file (duplicated from the wizard) to verify its content. This is intentional — the test validates the contract, not the implementation.
  </action>
  <verify>
    <automated>cd /opt/nexus && pnpm --filter server test --run -- 29-default-provider 2>&1 | tail -20</automated>
  </verify>
  <acceptance_criteria>
    - test file exists at server/src/__tests__/29-default-provider.test.ts
    - grep -q 'loadDefaultAgentInstructionsBundle' server/src/__tests__/29-default-provider.test.ts
    - grep -q 'HEARTBEAT.md' server/src/__tests__/29-default-provider.test.ts
    - grep -q 'promptTemplate' server/src/__tests__/29-default-provider.test.ts
    - pnpm --filter server test --run -- 29-default-provider exits 0
  </acceptance_criteria>
  <done>
    - All tests pass: probe logic, promptTemplate content validation, default bundle loading
    - Tests verify the contract that Hermes agents get skill content via the standard bundle path
    - Tests confirm promptTemplate contains all required Mustache variables for Hermes adapter
  </done>
</task>

</tasks>

<verification>
- `pnpm --filter server test --run -- 29-default-provider` passes all tests
- `pnpm --filter ui exec tsc --noEmit` compiles cleanly
- NexusOnboardingWizard contains promptTemplate injection for hermes_local
- Test file covers probe, promptTemplate, and bundle loading
</verification>

<success_criteria>
- Hermes agents created via wizard get a promptTemplate that enables the Nexus GSD workflow
- Integration tests validate the probe route logic and skill bundle loading
- A machine with only Hermes + Ollama can complete onboarding and get working agents (no paywalls)
</success_criteria>

<output>
After completion, create `.planning/phases/29-default-provider/29-02-SUMMARY.md`
</output>
