Developing Harness Plugins
Relevant source files
The following files were used as context for generating this wiki page:
Harness plugins are adapter components that allow Opificia AI to interact with different AI models and coding agents. They run inside isolated sandboxes and translate a standardized RunnerTask into specific model instructions to perform software engineering work.
Sources: plugins/harness-claude/src/main.ts:1-12, ROADMAP.md:30-35
Architecture and Lifecycle
Harnesses follow a specific lifecycle managed by the Opificia server. The server launches a harness in a sandbox for two distinct stages: planning and implementation.
Harness Operation Flow
The following diagram illustrates how a harness processes a task from initialization to completion.
The diagram shows the sequential execution of a harness within a sandbox, branching based on the assigned task stage.
Sources: plugins/harness-claude/src/main.ts:80-96, apps/server/src/worker/effects.service.ts:100-140
Task Stages
Harnesses must handle two primary operation modes defined in the RunnerTask.
Planning Stage
In the plan stage, the harness analyzes the repository and the issue to generate a Markdown implementation plan.
- Goal: Produce a summary of changes, list affected files, and outline test requirements.
- Constraints: This stage is typically read-only; the harness should not modify the repository.
- Output: Returns a
plan_readyoutcome with the plan text.
Sources: plugins/harness-claude/src/main.ts:25-39, plugins/harness-claude/src/prompts.ts:77-100
Implementation Stage
In the implement stage, the harness applies the approved plan to the codebase.
- Goal: Modify code, add tests, and verify the change by running the test suite.
- Self-Review: The harness may include a self-review turn to inspect the diff and fix bugs before submission.
- Output: Returns an
implementation_readyoutcome with a summary, a Git bundle of the changes, and the new head SHA.
Sources: plugins/harness-claude/src/main.ts:41-78, plugins/harness-claude/src/prompts.ts:119-128
Core Components and Utilities
Harness development relies on the @opificia/runner and @opificia/contracts packages to maintain protocol compatibility.
Key Data Structures
| Type | Source | Description |
|---|---|---|
RunnerTask | @opificia/contracts | Contains repository info, issue details, budget, and stage. |
RunnerCompleteParams | @opificia/contracts | Defines the result format for the server. |
HarnessContext | @opificia/runner | Provides sandbox paths and emission utilities. |
Sources: plugins/harness-claude/src/main.ts:14-23, apps/server/src/worker/effects.service.ts:114-135
Workspace Management Functions
Harnesses use standard utilities to manage the sandbox environment:
prepareWorkspace: Sets up the repository from the input bundle, configures Git author details, and checks out the target branch.commitAll: Persists agent changes with a formatted message and necessary trailers.bundleBranch: Creates a Git bundle of the completed work for the server to ingest.
Sources: plugins/harness-claude/src/main.ts:82-90, plugins/harness-claude/src/main.ts:74-76
Security and Constraints
Harnesses operate under strict resource and network constraints to ensure sovereignty and safety.
Network Access Levels
The harness prompt must reflect the network environment configured by the operator.
| Level | Description |
|---|---|
none | No internet access allowed. |
registries | Access limited to package registries and download hosts over HTTPS. |
open | Most hosts reachable over HTTPS through a configured proxy. |
Sources: plugins/harness-claude/src/prompts.ts:6-12
Protected Files
Harnesses are strictly prohibited from modifying core project files or CI configurations. Blocked paths include:
AGENTS.mdCONTRIBUTING.md.github/workflows/MAINTAINERS.md
Sources: plugins/harness-claude/src/prompts.ts:50-53, apps/server/src/worker/effects.service.ts:58-60
Example Implementation Pattern
Harnesses typically use the runHarness wrapper to handle the entry point and initialization logic.
// plugins/harness-claude/src/main.ts:80-97
const exitCode = await runHarness({
name: "harness-name",
version: "0.0.0",
async run(task, context) {
const repoDir = join(context.workDir, "repo");
await prepareWorkspace({
bundlePath: join(context.inDir, "repo.bundle"),
dir: repoDir,
defaultBranch: task.repo.defaultBranch,
baseSha: task.repo.baseSha,
branch: task.branch,
authorName: task.commit.authorName,
authorEmail: task.commit.authorEmail,
});
return task.stage === "plan"
? plan(task, repoDir, context)
: implement(task, repoDir, context);
},
});
Sources: plugins/harness-claude/src/main.ts:80-97
Harnesses are essential for the "Opificia builds Opificia" goal, where the factory eventually uses its own harnesses to develop the platform further.
Sources: ROADMAP.md:46-51