nexus/.planning/phases/29-default-provider/29-RESEARCH.md
Nexus Dev 51eb2edf0b chore: complete v1.5 Smart Onboarding + Personal AI Assistant milestone
6 phases, 13 plans, 21 requirements.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 23:03:46 +00:00

28 KiB

Phase 29: Default Provider & End-to-End - Research

Researched: 2026-04-01 Domain: Onboarding UX, agent template configuration, GSD workflow compatibility Confidence: HIGH

Summary

Phase 29 closes the last gap in the v1.4 milestone: a fresh Nexus install that has Hermes + Ollama but no cloud provider (Claude Code, Anthropic API key, etc.) currently hits a wall at every entry point. The Nexus-owned onboarding wizard (NexusOnboardingWizard.tsx) hardcodes adapterType: "claude_local" for all three agents it creates. The NewAgentDialog.tsx template shortcuts also hardcode adapterType: "claude_local". Neither component has any logic to detect which adapters are locally available.

The Hermes adapter is well-integrated at the runtime level: it has supportsLocalAgentJwt: true (so PAPERCLIP_API_KEY is injected automatically), handles session persistence via --resume, and the heartbeat service already has a Hermes-specific stateJson merge path. However, there is one confirmed gap: syncHermesSkills is a no-op (it returns the skill snapshot but writes nothing to disk), and the Hermes execute.ts does not inject Nexus-managed skill content into the prompt. This means the PM/Engineer HEARTBEAT.md/TOOLS.md bundles are recorded in the DB but are not actually available to the Hermes agent at runtime.

The agent template files (HEARTBEAT.md, TOOLS.md, AGENTS.md) are curl-based and entirely adapter-neutral. The onboarding-assets work for any adapter that has API access — the only barrier is getting skill content into the Hermes context.

Phase 29 involves five targeted areas: (1) provider detection at onboarding; (2) adapter default in the Nexus wizard; (3) AGENT_TEMPLATES fix in NewAgentDialog; (4) skill injection for Hermes agents; and (5) end-to-end smoke test.

Primary recommendation: The Nexus onboarding wizard should probe hermes availability via a board-authenticated route, then silently pre-select hermes_local when hermes is found and no cloud provider is detected. For skill injection, the simplest fix is to include the Nexus skill bundle content inline in the Hermes prompt template via ctx.context.skills (the heartbeat service already populates this; the adapter just needs to use it).

<user_constraints>

User Constraints (from CONTEXT.md)

Locked Decisions

All implementation choices are at Claude's discretion — discuss phase was skipped per user setting. Use ROADMAP phase goal, success criteria, and codebase conventions to guide decisions.

Claude's Discretion

All implementation choices are at Claude's discretion.

Deferred Ideas (OUT OF SCOPE)

None — discuss phase skipped. </user_constraints>

<phase_requirements>

Phase Requirements

ID Description Research Support
DFLT-01 If no cloud provider (Claude Code, etc.) is detected, Hermes + Ollama is offered as default during onboarding NexusOnboardingWizard.tsx hardcodes claude_local; needs provider detection + conditional adapter pre-selection
DFLT-02 Default agent templates (PM, Engineer, Generalist) work correctly with Hermes runtime AGENT_TEMPLATES in NewAgentDialog.tsx hardcodes claude_local; onboarding-assets adapter-agnostic but skill injection gap must be closed
DFLT-03 GSD workflow functions correctly with Hermes as the agent runtime Hermes has supportsLocalAgentJwt: true; skill sync is a no-op; need to inject Nexus skill content via prompt context
DFLT-04 Fresh install with only Hermes + Ollama works end-to-end (no paid subscription or API key required) All individual pieces exist; needs integration smoke test and skill injection gap closed
</phase_requirements>

Standard Stack

Core

Library Version Purpose Why Standard
hermes-paperclip-adapter 0.2.1 Hermes adapter module Already installed; provides testEnvironment, execute, detectModel
@tanstack/react-query (project version) Server state in onboarding wizard All other queries in the onboarding wizard use this
React (project version) UI component model Project standard

Supporting

Library Version Purpose When to Use
agentsApi (internal) Frontend API client for adapter queries testEnvironment, detectModel, adapterModels

Installation: No new packages needed. hermes-paperclip-adapter@0.2.1 is already in the monorepo.


Architecture Patterns

Changes span these files:

ui/src/components/
├── NexusOnboardingWizard.tsx        # Add provider detection + conditional hermes default
├── NewAgentDialog.tsx               # Make AGENT_TEMPLATES adapter-neutral
server/src/routes/
└── agents.ts                        # Add board-auth probe route (no companyId)
server/src/
└── hermes-skill-inject.ts           # (optional) skill injection helper
server/src/__tests__/
├── 29-default-provider.test.ts      # Probe route + agent creation tests
└── 29-hermes-skill-inject.test.ts   # Skill injection unit tests

Pattern 1: Provider Availability Detection at Onboarding

What: Before the Nexus onboarding wizard renders its form, fire a background probe to check if hermes is installed. If yes (and no cloud provider credential exists), pre-populate adapterType to hermes_local.

When to use: In NexusOnboardingWizard.tsx.

Implementation notes:

  • The existing testEnvironment route requires a companyId (it lives under /companies/:companyId/adapters/:type/test-environment). At wizard load time there is no company yet.
  • Approach: Add a board-authenticated route GET /api/adapters/:adapterType/probe that calls adapter.testEnvironment({}) without a company context. The testEnvironment function for hermes_local only checks CLI presence, Python, and model config — it does not need a real company.
  • The probe runs once on wizard mount, result cached in state.
// server/src/routes/agents.ts — NEW route (board auth only)
router.get("/adapters/:type/probe", async (req, res) => {
  if (req.actor.type !== "board") {
    res.status(403).json({ error: "Board auth required" });
    return;
  }
  const type = req.params.type as string;
  const adapter = findServerAdapter(type);
  if (!adapter?.testEnvironment) {
    res.json({ available: false });
    return;
  }
  const result = await adapter.testEnvironment({
    companyId: "",
    adapterType: type,
    config: {},
  });
  const hasCliError = result.checks.some(
    (c) => c.level === "error" && c.code?.includes("not_found")
  );
  res.json({ available: !hasCliError, status: result.status });
});

Pattern 2: Nexus Onboarding Wizard Adapter Auto-Selection

What: The Nexus wizard creates 3 agents with hardcoded adapterType: "claude_local". For DFLT-01, when Hermes is available and no cloud provider is found, use hermes_local instead.

Recommendation: Silent auto-selection — no extra UI step. The ROADMAP says "one-click path". When the probe resolves:

  • hermes=available, claude=not-detected: use hermes_local
  • both available: show a single "Which runtime?" card between the title and the directory input (keeps the single-form layout)
  • neither available: keep claude_local as default, let the user figure out installation

Note on cwd: For hermes_local, cwd is optional. The wizard's directory input should become optional when hermes_local is selected, or the label should change to "Working directory (optional)".

// NexusOnboardingWizard.tsx additions
const [defaultAdapterType, setDefaultAdapterType] =
  useState<"claude_local" | "hermes_local">("claude_local");
const [probing, setProbing] = useState(false);

useEffect(() => {
  if (!effectiveOnboardingOpen) return;
  setProbing(true);
  fetch("/api/adapters/hermes_local/probe", {
    headers: { Authorization: `Bearer ${boardToken}` },
  })
    .then((r) => r.json())
    .then((data: { available: boolean }) => {
      if (data.available) setDefaultAdapterType("hermes_local");
    })
    .catch(() => {})
    .finally(() => setProbing(false));
}, [effectiveOnboardingOpen]);

// In handleSubmit, use defaultAdapterType instead of hardcoded "claude_local"
const adapterConfig =
  defaultAdapterType === "hermes_local"
    ? rootDir.trim() ? { cwd: rootDir.trim() } : {}
    : { cwd: rootDir.trim() };

Pattern 3: AGENT_TEMPLATES in NewAgentDialog.tsx (DFLT-02)

What: The AGENT_TEMPLATES constant hardcodes adapterType: "claude_local". For DFLT-02, templates should work with Hermes.

Recommendation: Remove adapterType from the template navigation URL. The /agents/new form already handles Hermes via its own adapter detection. Without adapterType in the URL, the form defaults to its own logic (currently claude_local, but that default can be updated separately via the same probe mechanism).

// NewAgentDialog.tsx
// Before:
function handleTemplateSelect(template: typeof AGENT_TEMPLATES[number]) {
  navigate(`/agents/new?adapterType=${encodeURIComponent(template.adapterType)}&role=...`);
}

// After:
function handleTemplateSelect(template: { id: string; label: string; role: string }) {
  navigate(`/agents/new?role=${encodeURIComponent(template.role)}&name=${encodeURIComponent(template.label)}`);
}

If the /agents/new form's default adapter needs to respect the probe result, the same useQuery-based probe can be added to that page.

Pattern 4: Hermes Skill Injection (DFLT-03 Critical Gap)

What: syncHermesSkills is a confirmed no-op — it returns the snapshot but writes nothing to ~/.hermes/skills/. The Hermes execute.ts does not read ctx.context.skills. This means a Hermes agent created with PM/Engineer role currently runs with only Hermes's default prompt (which knows nothing about the Nexus API or the HEARTBEAT.md workflow).

Confirmed by: Reading hermes-paperclip-adapter@0.2.1/dist/server/skills.jssyncHermesSkills returns the snapshot directly without writing files. Reading dist/server/execute.js — zero references to skills in the execute function.

Fix options:

Option A (recommended): Update the Hermes execute.ts (in the adapter package) to append Nexus-managed skill content to the prompt when ctx.context.skills contains entries. This is the cleanest approach and uses the existing AdapterExecutionContext.skills mechanism.

Option B (simpler, no adapter change): Override the Hermes promptTemplate on agent creation in NexusOnboardingWizard.tsx to embed the Nexus skill bundle content directly in the adapterConfig.promptTemplate field. The Hermes adapter supports custom prompt templates with {{variable}} substitution.

Option C: Make syncHermesSkills actually write skills to ~/.hermes/skills/{skill-key}/SKILL.md. Hermes loads skills from that directory automatically. This is the most "native" approach but requires knowing the right directory layout.

Recommended approach: Option B for this phase (no adapter package changes needed). The NexusOnboardingWizard.tsx already loads the company-skills data; it can embed the required skill content in the promptTemplate at agent creation time. The Hermes DEFAULT_PROMPT_TEMPLATE in execute.js already handles task assignment, comments, and API calls — the Nexus bundle is supplementary.

Alternative path: If the planner wants a cleaner long-term solution, file a note that Option A (patching the adapter) would make hermes behave like codex/gemini with automatic skill injection — but that requires publishing a new version of hermes-paperclip-adapter.

// NexusOnboardingWizard.tsx — when creating Hermes agents, inject skill content
// The Hermes prompt template supports {{agentName}}, {{companyId}}, etc.
// We can extend it with the HEARTBEAT.md content.

const hermesPromptTemplate = buildHermesPromptWithSkills({
  role: "ceo",  // or "engineer" / "general"
  nexusSkillContent: {
    heartbeat: CEO_HEARTBEAT_MD,  // imported at build time
    tools: CEO_TOOLS_MD,
  }
});
// Then pass promptTemplate in adapterConfig when creating the agent

Anti-Patterns to Avoid

  • Polling testEnvironment repeatedly: This spawns a hermes subprocess. Run only once on wizard mount, cache the result.
  • Blocking the wizard on adapter detection: If the probe takes >2s, show the wizard with a default and update async.
  • Changing NexusOnboardingWizard to multi-step: Keep it single-form. Only add a runtime selector card if both adapters are available.
  • Hardcoding hermes_local as universal default: Detect, don't assume. Users with Claude Code must still get claude_local.
  • Writing skills to ~/.hermes/skills/ without understanding Hermes skill layout: Hermes expects ~/.hermes/skills/{category}/{skill-name}/SKILL.md — the layout must match Hermes's scanner.

Don't Hand-Roll

Problem Don't Build Use Instead Why
Adapter availability check Custom subprocess spawn adapter.testEnvironment({}) Already handles ENOENT, version checks, Python checks
Model detection Parsing ~/.hermes/config.yaml manually detectModel() from hermes-paperclip-adapter/server Already implemented, handles edge cases
Session management Custom --resume logic Hermes sessionCodec + heartbeat session store Already implemented in Phase 27
Skill file reading at build time Custom fs.readFile in routes Import at compile time (Node ESM import.meta.url + fs.readFile) Use the existing loadDefaultAgentInstructionsBundle pattern from default-agent-instructions.ts

Key insight: The runtime plumbing for Hermes authentication and session persistence is complete. The two remaining gaps are UI entry points (wizard/templates) and skill content delivery to the Hermes prompt.


Common Pitfalls

Pitfall 1: testEnvironment Requires a companyId at the Existing Route

What goes wrong: The existing agentsApi.testEnvironment(companyId, type, data) client method sends to /companies/{companyId}/adapters/{type}/test-environment. During initial onboarding, no company exists yet. Why it happens: The route was designed for post-onboarding agent configuration. How to avoid: Add a new board-authenticated route GET /api/adapters/:adapterType/probe (no companyId) that runs the test with empty config. Board auth is available at wizard load time. Warning signs: 401/404 errors when wizard tries to probe before company creation.

Pitfall 2: Hermes CLI Not in PATH in the Server Process

What goes wrong: testEnvironment spawns hermes --version from the server Node.js process. The server's PATH may differ from the user's shell PATH (especially if installed via pip install --user without PATH setup). Why it happens: Node.js child_process inherits process.env.PATH from server startup, not the interactive shell. How to avoid: The probe should return available: false gracefully on ENOENT. The UI handles available: false by keeping claude_local as default. Warning signs: hermes_cli_not_found check code in the environment test result.

Pitfall 3: Hermes Skill Injection is a No-Op — Agents Won't Follow the Nexus Heartbeat Loop

What goes wrong: A PM or Engineer agent created with the Hermes runtime runs a heartbeat but never calls GET /api/agents/me or consults HEARTBEAT.md — it uses only Hermes's built-in default prompt. Why it happens (confirmed): syncHermesSkills at hermes-paperclip-adapter@0.2.1/dist/server/skills.js is explicitly a no-op. It returns the skill snapshot but writes nothing to disk. The Hermes execute.ts has zero references to ctx.context.skills. How to avoid: Use Option B from Pattern 4: embed the Nexus skill bundle content in the adapterConfig.promptTemplate when creating Hermes agents in NexusOnboardingWizard.tsx. The Hermes adapter supports custom prompt templates. Warning signs: Hermes heartbeat output shows Hermes's default task-completion workflow ("mark the issue as completed: curl...") rather than the multi-step PM delegation loop.

Pitfall 4: Hermes adapterConfig.cwd is Optional, Not Required

What goes wrong: The Nexus wizard passes cwd: rootDir.trim() to all agents. For claude_local, cwd is required. For hermes_local, cwd is optional — Hermes defaults to ".". Why it happens: The wizard was written for claude_local semantics. How to avoid: When hermes_local is selected, make the directory input optional. buildHermesConfig already handles absent cwd gracefully. Warning signs: Wizard form validation errors when user skips directory input for a Hermes agent.

Pitfall 5: NexusOnboardingWizard vs OnboardingWizard Alias

What goes wrong: The Vite alias redirects all imports of ./components/OnboardingWizard to NexusOnboardingWizard.tsx. Changes to OnboardingWizard.tsx (the original) have no effect in the running UI. Why it happens: Phase 4 introduced this alias to preserve the original for upstream rebase compatibility. How to avoid: Edit NexusOnboardingWizard.tsx for all onboarding changes. The alias is in vite.config.ts. Warning signs: UI changes not appearing after hot-reload; git shows changes to the wrong file.


Code Examples

Server Route: Board-Auth Adapter Probe (No companyId)

// server/src/routes/agents.ts — new route, add before existing company-scoped routes
// Source: existing testEnvironment pattern + board auth pattern from llms.ts

router.get("/adapters/:type/probe", async (req, res) => {
  if (req.actor.type !== "board") {
    res.status(403).json({ error: "Board authentication required" });
    return;
  }
  const type = req.params.type as string;
  const adapter = findServerAdapter(type);
  if (!adapter?.testEnvironment) {
    res.json({ available: false, status: "unknown" });
    return;
  }
  try {
    const result = await adapter.testEnvironment({
      companyId: "",  // not needed for CLI presence check
      adapterType: type,
      config: {},
    });
    const hasCliNotFound = result.checks.some(
      (c) => c.level === "error" && (c.code?.includes("not_found") || c.code?.includes("cli"))
    );
    res.json({ available: !hasCliNotFound, status: result.status, checks: result.checks });
  } catch {
    res.json({ available: false, status: "error" });
  }
});

Frontend: Detection in NexusOnboardingWizard

// NexusOnboardingWizard.tsx
import { useEffect, useState } from "react";

const [defaultAdapterType, setDefaultAdapterType] =
  useState<"claude_local" | "hermes_local">("claude_local");

// Probe once when wizard opens
useEffect(() => {
  if (!effectiveOnboardingOpen) return;
  fetch("/api/adapters/hermes_local/probe")
    .then((r) => r.ok ? r.json() : { available: false })
    .then((data: { available: boolean }) => {
      if (data.available) setDefaultAdapterType("hermes_local");
    })
    .catch(() => {}); // graceful — keep claude_local default
}, [effectiveOnboardingOpen]);

// In handleSubmit — replace hardcoded "claude_local" with defaultAdapterType
const agentAdapterConfig =
  defaultAdapterType === "hermes_local" && rootDir.trim()
    ? { cwd: rootDir.trim() }
    : defaultAdapterType === "hermes_local"
    ? {}
    : { cwd: rootDir.trim() };

AGENT_TEMPLATES Fix (NewAgentDialog.tsx)

// Before (hardcoded adapterType):
const AGENT_TEMPLATES = [
  { id: "pm", label: "Project Manager", role: "pm" as const, adapterType: "claude_local" as const },
  { id: "engineer", label: "Engineer", role: "engineer" as const, adapterType: "claude_local" as const },
];
// handleTemplateSelect passes adapterType in URL

// After (adapter-neutral):
const AGENT_TEMPLATES = [
  { id: "pm", label: "Project Manager", role: "pm" as const },
  { id: "engineer", label: "Engineer", role: "engineer" as const },
];

function handleTemplateSelect(template: typeof AGENT_TEMPLATES[number]) {
  closeNewAgent();
  setShowAdvancedCards(false);
  // No adapterType in URL — /agents/new form picks its own default
  navigate(
    `/agents/new?role=${encodeURIComponent(template.role)}&name=${encodeURIComponent(template.label)}`
  );
}

Hermes Skill Content Injection in Prompt Template

// NexusOnboardingWizard.tsx — when adapterType === "hermes_local"
// Load the role-specific skill bundle and embed in promptTemplate

// These are static imports (or dynamic loads from /api/onboarding-assets)
// The content of AGENTS.md, HEARTBEAT.md, SOUL.md, TOOLS.md for the given role
function buildHermesAdapterConfig(
  role: "ceo" | "engineer" | "general",
  cwd: string | null,
  skillBundle: Record<string, string>  // { "AGENTS.md": "...", "HEARTBEAT.md": "...", ... }
) {
  const skillSection = Object.entries(skillBundle)
    .map(([name, content]) => `## ${name}\n\n${content}`)
    .join("\n\n---\n\n");

  const promptTemplate = `You are "{{agentName}}", an AI agent in a Nexus-managed company.

Your Nexus identity:
  Agent ID: {{agentId}}
  Company ID: {{companyId}}
  API Base: {{paperclipApiUrl}}
  Run ID: {{runId}}

IMPORTANT: Use \`terminal\` tool with \`curl\` for ALL Nexus API calls.
IMPORTANT: Always include the header \`-H "X-Paperclip-Run-Id: {{runId}}"\` on API calls that modify data.

${skillSection}

{{#taskId}}
Assigned task: {{taskId}} — {{taskTitle}}
{{/taskId}}
`;

  return {
    ...(cwd ? { cwd } : {}),
    persistSession: true,
    promptTemplate,
  };
}

Runtime State Inventory

Skipped — this is a greenfield UI/config phase, not a rename/refactor. No runtime state migration required.


Environment Availability

Dependency Required By Available Version Fallback
hermes CLI (hermes) DFLT-01 probe, DFLT-03 execution Runtime check via probe Detected via hermes --version Show install guidance (existing OLLA-05 pattern)
Ollama DFLT-04 (Hermes model serving) Runtime check Detected via Ollama service hermes can use API-based providers
Node.js / pnpm Build system (project standard)

Missing dependencies with no fallback:

  • None that block development — the probe gracefully handles hermes not being installed.

Missing dependencies with fallback:

  • hermes CLI not in PATH: probe returns available: false; wizard falls back to claude_local.

Validation Architecture

Test Framework

Property Value
Framework Vitest
Config file server/vitest.config.ts
Quick run command pnpm --filter server test --run
Full suite command pnpm --filter server test --run && pnpm --filter ui test --run

Phase Requirements → Test Map

Req ID Behavior Test Type Automated Command File Exists?
DFLT-01 Probe route returns available: true when hermes CLI is found unit pnpm --filter server test --run -- 29-default-provider Wave 0
DFLT-01 Probe route returns available: false when hermes CLI absent (ENOENT) unit pnpm --filter server test --run -- 29-default-provider Wave 0
DFLT-02 PM/Engineer agent created with hermes_local adapterType saves correct record unit pnpm --filter server test --run -- 29-default-provider Wave 0
DFLT-03 Hermes heartbeat injects PAPERCLIP_API_KEY (supportsLocalAgentJwt: true) unit (existing) pnpm --filter server test --run -- adapter-session-codecs
DFLT-03 Hermes promptTemplate includes Nexus skill content when created via wizard unit pnpm --filter server test --run -- 29-hermes-skill-inject Wave 0
DFLT-04 Full smoke: probe → wizard submit → PM agent (hermes_local) → issue → heartbeat-run record created integration pnpm --filter server test --run -- 29-default-provider Wave 0

Sampling Rate

  • Per task commit: pnpm --filter server test --run
  • Per wave merge: pnpm --filter server test --run && pnpm --filter ui test --run
  • Phase gate: Full suite green before /gsd:verify-work

Wave 0 Gaps

  • server/src/__tests__/29-default-provider.test.ts — probe route (DFLT-01), agent creation with hermes_local (DFLT-02), smoke (DFLT-04)
  • server/src/__tests__/29-hermes-skill-inject.test.tsbuildHermesAdapterConfig produces promptTemplate containing skill bundle content (DFLT-03)

(Both new test files; they share the existing in-memory db setup pattern from heartbeat-workspace-session.test.ts)


State of the Art

Old Approach Current Approach When Changed Impact
Original OnboardingWizard.tsx (multi-step, adapter picker) NexusOnboardingWizard.tsx (single-step, no adapter choice) Phase 4 (Vite alias) Simpler UX but hardcodes claude_local
Manual hermes model detection detectModel() from hermes adapter Phase 28 Can probe ~/.hermes/config.yaml without running hermes
No Hermes adapter hermes-paperclip-adapter@0.2.1 Phase 27 Full hermes execution pipeline available
Skill sync writes to disk (claude/codex) Hermes skill sync is a no-op (confirmed) Hermes adapter design Skills must be injected via prompt template instead

Deprecated/outdated:

  • SESSIONED_LOCAL_ADAPTERS does NOT include hermes_local — intentional. Hermes manages session IDs itself; Nexus just stores the returned session ID. No change needed.

Open Questions

  1. Should the probe route return 403 for non-board actors, or 200 with available: false?

    • What we know: Board auth is present at wizard load time. Non-board actors should not need to probe adapter availability.
    • Recommendation: Return 403 for non-board. The wizard frontend should only show to board users.
  2. Does NexusOnboardingWizard need a visible runtime selector, or is silent auto-selection sufficient?

    • What we know: ROADMAP says "one-click path." Silent selection is simpler.
    • Recommendation: Silent when only one option is available; show a two-card toggle when both hermes and claude are available.
  3. Should the Hermes prompt template embed full skill content, or just inject section headings?

    • What we know: The full HEARTBEAT.md for CEO is about 70 lines. Embedding in every heartbeat prompt increases context usage.
    • Recommendation: Embed full content. The GSD workflow requires the agent to follow the exact HEARTBEAT.md checklist. Abbreviated hints won't produce correct behavior.

Sources

Primary (HIGH confidence)

  • /opt/nexus/ui/src/components/NexusOnboardingWizard.tsx — hardcoded adapterType: "claude_local" (lines 97, 106, 115) confirmed
  • /opt/nexus/ui/src/components/NewAgentDialog.tsx — hardcoded templates at lines 98-99 confirmed
  • /opt/nexus/ui/src/components/OnboardingWizard.tsx — upstream wizard; Hermes in "More adapters" (hidden by default)
  • /opt/nexus/server/src/adapters/registry.tshermesLocalAdapter with supportsLocalAgentJwt: true (line 186)
  • /opt/nexus/server/src/services/heartbeat.tsSESSIONED_LOCAL_ADAPTERS (lines 71-78): hermes_local intentionally absent
  • hermes-paperclip-adapter@0.2.1/dist/server/execute.jsbuildPaperclipEnv confirmed; zero skill references
  • hermes-paperclip-adapter@0.2.1/dist/server/skills.jssyncHermesSkills confirmed no-op (returns snapshot, writes nothing)
  • hermes-paperclip-adapter@0.2.1/dist/server/test.jstestEnvironment checks: CLI, Python, model config, API keys
  • /opt/nexus/server/src/onboarding-assets/ — all role bundles (ceo, engineer, pm, general) confirmed adapter-agnostic
  • /opt/nexus/skills/paperclip/SKILL.md — GSD heartbeat workflow curl-based, adapter-neutral
  • /opt/nexus/server/src/services/default-agent-instructions.tsloadDefaultAgentInstructionsBundle pattern for reading skill files

Secondary (MEDIUM confidence)

  • /opt/nexus/.planning/ROADMAP.md — Phase 29 success criteria and description
  • hermes-paperclip-adapter@0.2.1/dist/ui/build-config.jsbuildHermesConfig: cwd optional, provider resolved at runtime

Metadata

Confidence breakdown:

  • Standard stack: HIGH — all packages already installed, no new dependencies
  • Architecture: HIGH — hardcoded adapter locations confirmed via direct code reading; skill injection gap confirmed
  • Pitfalls: HIGH — all identified from direct code reading (no assumptions)

Research date: 2026-04-01 Valid until: 2026-05-01 (stable domain; hermes-paperclip-adapter version pinned)