6 phases, 13 plans, 21 requirements. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
14 KiB
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | |||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 29-default-provider | 02 | execute | 2 |
|
|
true |
|
|
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.
<execution_context> @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md </execution_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.mdFrom server/src/routes/agents.ts (Plan 01 addition):
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):
probeAdapter: (type: string) =>
api.get<{ available: boolean; status: string }>(`/adapters/${encodeURIComponent(type)}/probe`),
From server/src/services/default-agent-instructions.ts:
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:
// 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):
const [defaultAdapter, setDefaultAdapter] = useState<"claude_local" | "hermes_local">("claude_local");
// handleSubmit creates 3 agents with adapterType: defaultAdapter
Task 1: Add Hermes promptTemplate skill injection to NexusOnboardingWizard
- 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)
ui/src/components/NexusOnboardingWizard.tsx
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:
- Agent created with no promptTemplate => server loads full bundle via
loadDefaultAgentInstructionsBundle - Bundle materialized as managed files in the DB
- When heartbeat runs,
ctx.context.skillscontains the skill content - The HEARTBEAT.md content instructs the agent to call
GET /api/agents/meto get its instructions - The Hermes adapter has
supportsLocalAgentJwt: trueso 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:
// 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).
cd /opt/nexus && pnpm --filter ui exec tsc --noEmit 2>&1 | head -20
<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>
- 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)
Test group 1: Adapter probe route logic These test the probe logic directly (no HTTP — test the route handler's logic):
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:
it("findServerAdapter returns hermes_local adapter with testEnvironment")— verify the adapter exists and has testEnvironment functionit("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:
it("hermes promptTemplate contains required Mustache variables")— build the template string and verify it contains{{agentName}},{{agentId}},{{companyId}},{{paperclipApiUrl}},{{runId}},{{taskId}},{{taskTitle}}it("hermes promptTemplate instructs agent to call /api/agents/me")— verify the template containsagents/meit("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. cd /opt/nexus && pnpm --filter server test --run -- 29-default-provider 2>&1 | tail -20 <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> - 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
- `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<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>