Skip to content

Latest commit

 

History

History
817 lines (601 loc) · 39.6 KB

File metadata and controls

817 lines (601 loc) · 39.6 KB

Profile Schema

This document specifies the complete harness.yaml format for Harness Protocol v1. Every field, constraint, and validation rule is defined here. The JSON Schema at https://harnessprotocol.io/schema/v1/harness.schema.json is the machine-readable complement to this document.


Document Structure

A harness.yaml file is a single YAML document. Top-level keys are defined in the sections below. Unknown top-level keys (other than those prefixed with x-) are a validation error.

$schema: https://harnessprotocol.io/schema/v1/harness.schema.json
version: "1"
kind: profile

metadata:
  name: my-harness
  description: "..."

plugins: [...]
skills: [...]
architectural-constraints: { ... }
mcp-servers: { ... }
env: [...]
instructions: { ... }
permissions: { ... }
policy: { ... }
extends: [...]

$schema

Field Type Required Default
$schema string (URI) No

A URI identifying the JSON Schema this document validates against. Including $schema enables schema-aware editors to provide validation and autocompletion.

Recommended value:

$schema: https://harnessprotocol.io/schema/v1/harness.schema.json

The presence or absence of $schema does not affect runtime validation. Implementations validate against the version-appropriate schema regardless.


version

Field Type Required Default
version string Yes

The Harness Protocol version. For v1, this MUST be the string "1" — not the integer 1.

version: "1"   # CORRECT: string
version: 1     # INCORRECT: integer — validation error

The JSON Schema enforces "type": "string", so any conformant validator will reject the integer 1. Implementations SHOULD additionally provide a helpful error message suggesting version: "1" (string). Silently coercing the integer to a string is NOT permitted.

Non-normative: The legacy harness-kit format used version: 1 (integer) alongside marketplaces: and list-form plugins:. Implementations that need to support both formats SHOULD branch on typeof version: string signals Harness Protocol format; integer signals legacy format. Legacy format behavior is outside this specification.

Validation rule: If version is present and is not the string "1", validation fails with a clear error indicating the unsupported version.


kind

Field Type Required Default
kind enum No profile

Accepted values: profile, fragment

Declares the document type:

  • profile — A complete harness document. All required fields are enforced. Suitable for direct application.
  • fragment — A partial harness document. Required-field validation is relaxed. Used as a building block for composition via the extends mechanism or the v2 Exchange layer.

Validation behavior:

kind Required fields enforced Structural validation Co-constraints enforced
profile Yes Yes Yes
fragment No Yes Yes

A fragment that is referenced in extends MUST be structurally valid (correct types, valid enums, no forbidden field combinations) even though it may be incomplete.


metadata

metadata is required for kind: profile documents and optional for fragments.

Fields

Field Type Required Description
name string Yes (profile) Identifier for this harness. Lowercase letters, digits, hyphens only. Max 64 characters.
description string No Human-readable description of the harness's purpose.
author object No Author information.
author.name string Yes (if author present) Author's display name.
author.url string (URI) No URL for the author (GitHub profile, personal site, etc.).
version string No Semantic version of this harness profile (e.g., "1.0.0"). Not the same as the protocol version field.
license string No SPDX license identifier (e.g., Apache-2.0, MIT).
tags array of strings No Search tags for registry discoverability. Each tag: lowercase, max 32 characters. Max 10 tags.

Constraints

  • metadata.name MUST match the pattern ^[a-z0-9]([a-z0-9-]{0,62}[a-z0-9])?$. Must start and end with a lowercase letter or digit.
  • metadata.version, if present, MUST be a valid semver string.
  • metadata.license, if present, MUST be a valid SPDX expression.

Example

metadata:
  name: data-engineer
  description: "Harness for data engineering workflows: SQL, lineage, dbt."
  author:
    name: alice
    url: https://github.com/alice
  version: "1.0.0"
  license: Apache-2.0
  tags: [data-engineering, sql, dbt]

plugins

plugins is an array of plugin declarations. Each entry references a plugin by source and version.

Plugin Entry Fields

Field Type Required Description
name string Yes Local name for this plugin within the harness. Used for override targeting in team overlays.
source string Yes Plugin source in owner/repo format (e.g., harnessprotocol/harness-kit).
version string No Semver range specifying the required plugin version (e.g., ">=0.2.0", "^1.0.0"). If absent, the implementation selects the latest compatible version.
description string No Human-readable note about why this plugin is included. Informational only.
config object No Plugin-specific configuration. Schema is defined by the individual plugin's plugin.json.
loading enum No When to load the plugin's tools and context into the agent's context window. Values: eager (default), deferred. See Loading Mode below.
integrity object No Content verification.
integrity.sha256 string No SHA-256 hash of the resolved plugin archive. Hex-encoded, lowercase. 64 characters.

Loading Mode

The loading field controls progressive skill disclosure — when a plugin's tools and context are injected into the agent's context window.

Value Behavior
eager (default) All plugin tools and context are loaded at session start. This is the current behavior and the default when loading is absent.
deferred Plugin tools are registered by name only at session start. Full tool schemas, context, and frontmatter are loaded the first time the agent invokes any tool from the plugin. Reduces initial context size.

Design rationale: Research (Chroma, LangChain) shows that model performance degrades as context length increases — a phenomenon called context rot. Progressive disclosure is one of three core mitigations identified by LangChain: don't load all tool frontmatter at startup, load on demand. Claude Code already implements deferred tool loading internally; this field lets profiles declare the intent portably.

Precedence: If a plugin's plugin.json declares loading: deferred but the harness profile declares loading: eager (or omits the field, defaulting to eager), the harness profile wins. The plugin manifest expresses the author's recommendation; the harness author has final say.

Backward compatibility: This field is optional with a default of eager, preserving current behavior for all existing harness documents.

Constraints

  • source MUST match the pattern ^[a-zA-Z0-9_][a-zA-Z0-9_.-]*/[a-zA-Z0-9_][a-zA-Z0-9_.-]*$. See Source Resolution for how source fields are resolved.
  • version, if present, MUST be a valid semver range expression.
  • integrity.sha256, if present, MUST be a 64-character lowercase hex string.
  • Plugin name values MUST be unique within the plugins array.
  • If integrity.sha256 is declared, implementations MUST verify the resolved plugin archive against this hash before loading. A mismatch is a fatal error.

Inheritance (via extends)

When a child harness extends a parent, plugin lists are unioned. If both declare a plugin with the same name, the child's entire plugin object replaces the parent's. There is no field-level merge within a plugin entry — the child's version, config, integrity, and all other fields are used as-is.

Example

plugins:
  - name: data-lineage
    source: harnessprotocol/harness-kit
    version: ">=0.2.0"
    description: "SQL lineage tracking and impact analysis"
    config:
      default-schema: public
    integrity:
      sha256: "a3f1e2b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2"

  - name: sql-assist
    source: harnessprotocol/plugins
    version: "^2.0.0"

skills

skills is an array of skill declarations. A skill is a portable, named capability — a directory containing a SKILL.md file and optional supporting resources — that an agent loads on demand. Skills may be bundled by a plugin or declared directly here; this section lets a harness declare a skill without authoring or depending on a full plugin. See HEP-4.

Skill Entry Fields

Field Type Required Default Description
name string Yes Lowercase kebab-case identifier. Max 64 characters. Pattern ^[a-z0-9]([a-z0-9-]{0,62}[a-z0-9])?$. Should match the skill's SKILL.md frontmatter name.
source string Yes¹ owner/repo, owner/repo/path/to/skill, or ./local/path. See Source Resolution.
version string No latest Semver range constraint for owner/repo sources. Ignored for local-path sources.
description string No Display override for this skill in this profile's context.
enabled boolean No true When false, the skill is declared but not activated — used to disable a skill inherited from a parent profile.
loading enum No deferred When the skill's full content is loaded. See Loading Mode.
integrity object No Content verification.
integrity.sha256 string No SHA-256 hash of the skill archive. Hex-encoded, lowercase. 64 characters.

¹ source is required for any skill that is being declared. It is optional when the entry only suppresses an inherited skill via enabled: false — in that case name alone identifies the skill to suppress. The JSON Schema enforces this conditionally.

Skill Loading Mode

A skill is progressive-disclosure by construction: only its name and description are needed at session start, with the body loaded on demand. The loading field controls this.

Value Behavior
deferred (default) Only the skill's metadata (name, description) is loaded at session start; the body is loaded on first invocation. This is the default — it keeps initial context small.
eager The full skill is loaded at session start.

Note that this default is the inverse of plugins, which default to eager. Skills default to deferred because the format is designed for on-demand loading.

Constraints

  • name MUST match the kebab-case pattern and be unique within the skills array.
  • source MUST resolve via the Source Resolution algorithm.
  • integrity.sha256, if present, MUST be a 64-character lowercase hex string. If declared, implementations MUST verify the resolved skill archive against it before loading; a mismatch is a fatal error.
  • A skill is third-party content. Implementations MUST apply the installation-review requirements in Skill Behavioral Injection regardless of how the skill is declared.

Resolution Relative to Plugins

A skill bundled by a plugin and a skill declared in skills are both registered for the session. If a directly-declared skill and a plugin-bundled skill share a name, the directly-declared skills entry wins — the harness author's explicit declaration is more specific than a transitive plugin payload. A directly-declared entry with enabled: false therefore suppresses both an inherited skills entry and a plugin-bundled skill of the same name.

Inheritance (via extends)

The skills array is unioned by name, following the same rule as mcp-servers: the child's full entry replaces a parent's entry with the same name; new names from parent and child are both retained. A child sets enabled: false to suppress an inherited skill.

Example

skills:
  - name: pdf-forms
    source: harnessprotocol/skills/pdf-forms
    version: ">=1.0.0"
    description: "Fill and extract PDF form fields."
    integrity:
      sha256: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"

  - name: house-style
    source: ./skills/house-style
    loading: eager

architectural-constraints

architectural-constraints is an optional object that declares the structural rules, linters, and review policies enforced by this harness. It separates deterministic enforcement (linters, structural tests) from probabilistic review (LLM-based) — combining both creates defense-in-depth for AI-generated code.

The field is derived from Böckeler's analysis of production AI-maintained codebases, which identifies a three-component harness model: context engineering, architectural constraints, and garbage collection. This section encodes the second component as a first-class protocol field.

Enforcement Levels

Level Mechanism Behavior
Deterministic Linters, structural tests Cannot be overridden; violations block commits or merges
Review LLM-based review agent Probabilistic; can request exceptions or suggest alternatives
Advisory Linters or tests with enforcement: warn Violations logged but do not block progress

Sub-Sections

linters

An array of deterministic enforcement rules. Each linter entry:

Field Type Required Default Description
name string Yes Linter identifier. Must be unique within the linters array.
description string Yes What architectural invariant this linter enforces.
enforcement enum No block block = violations prevent merge. warn = violations logged but don't prevent merge.
config object No Tool-specific linter configuration. Schema is tool-dependent.
source string No custom (defined in this harness) or a GitHub path (e.g., owner/repo/path).

structural-tests

An array of programmatic tests verifying architectural invariants (e.g., ArchUnit-style tests, layered architecture checks). Each entry:

Field Type Required Default Description
name string Yes Test identifier. Must be unique within the structural-tests array.
description string Yes What architectural invariant this test verifies.
entrypoint string No Command to run the test (e.g., gradle architectureTest).
enforcement enum No block block = failures prevent merge. warn = failures logged but don't prevent merge.
source string No custom or a GitHub path.

review-policy

An object declaring LLM-based architectural review behavior:

Field Type Required Default Description
enabled boolean No true Whether LLM review is active.
model string No Model identifier for review (implementation-specific).
patterns array No Architectural patterns to verify. Each has name, rule (prose), and severity (error, warning, info).
guidance string No Prose document describing the harness's architectural philosophy. Agents use this to calibrate reviews.

review-policy.patterns[].severity values:

  • error — blocks merge if reviewer detects a violation
  • warning (default) — flagged but merge allowed
  • info — noted but non-blocking

Constraints

  • Linter name values MUST be unique within the linters array.
  • Structural test name values MUST be unique within the structural-tests array.
  • The architectural-constraints object MUST contain at least one of linters, structural-tests, or review-policy if the field is present.
  • source: custom declarations should be accompanied by documentation or supporting files in the harness repository.

Hybrid Enforcement Model

The deterministic and review layers serve different roles. Linters and structural tests provide guarantees that a review agent cannot: a deterministic boundary check will never be convinced to accept a module boundary violation. The LLM review layer catches semantic and design-level issues that are impractical to express as rules.

Together they form a defense-in-depth model: linters and structural tests catch mechanical violations, review agents catch architectural drift. This combination is well-evidenced in production AI-maintained codebases operating at scale.

Portability

Implementations MUST:

  1. Discover and report all block-enforcement linters and structural tests declared in the harness
  2. Honor enforcement: warn by logging violations without blocking
  3. Apply the review-policy to configure LLM-based architectural review (if the implementation supports it)

A harness using only source: custom constraints with inline config is fully portable. Harnesses referencing external source GitHub paths depend on tool support for fetching those sources.

Example

architectural-constraints:
  linters:
    - name: module-boundary-checker
      description: Enforces that agents do not import across module boundaries
      enforcement: block
      config:
        boundaries:
          - module: /src/auth
            may_import_from: [/src/common, /src/types]
          - module: /src/database
            may_import_from: [/src/common, /src/types]
      source: custom

  structural-tests:
    - name: layered-architecture
      description: Verifies that each layer only depends on layers below it
      entrypoint: gradle architectureTest
      enforcement: block
      source: custom

  review-policy:
    enabled: true
    patterns:
      - name: module-cohesion
        rule: Each module should have a single, clear responsibility.
        severity: warning
      - name: dependency-direction
        rule: Dependencies flow strictly one direction. Circular dependencies are never acceptable.
        severity: error
    guidance: |
      Architecture follows a layered model:
      api/ → service/ → repository/ → types/ (shared by all).

mcp-servers

mcp-servers is a map (YAML object) from server name to server declaration. Keys are the server names as exposed to the agent at runtime.

Transport: stdio

Field Type Required Description
transport string Yes Must be "stdio".
command string Yes Executable to run (e.g., uvx, npx, node).
args array of strings No Arguments to pass to the command.
env object No Environment variable map passed to the process. Values may reference harness env declarations via ${VAR_NAME} syntax.
source string No Provenance identifier: a registry identity in reverse-DNS form (e.g., io.github.owner/server) or owner/repo. Records where the server originates. Does not change how command is invoked.
version string No Version or semver range for the server package, complementing any version pinned in args.
integrity object No Content verification for the server package.
integrity.sha256 string No SHA-256 of the server package archive. Hex-encoded, lowercase, 64 characters.

Transport: streamable-http (and http alias)

Field Type Required Description
transport string Yes "streamable-http" (canonical) or "http" (accepted alias — treated identically).
url string (URI) Yes HTTP(S) URL of the MCP server endpoint.
headers object No HTTP headers to include in requests. Values may reference harness env declarations via ${VAR_NAME} syntax.
source string No Provenance identifier (reverse-DNS registry identity or owner/repo).
version string No Version or semver range identifying the remote server build.

streamable-http is the canonical remote transport and is RECOMMENDED for all remote servers. http is retained as an accepted alias and MUST be treated identically.

Transport: sse and ws

The sse (Server-Sent Events) and ws (WebSocket) transports share the same network field structure (transport, url, headers, plus optional source/version). sse is the legacy transport, deprecated for new servers and retained for compatibility; implementations MAY warn when it is declared. ws is non-standard and implementation-specific — not part of the recommended set, but retained for forward compatibility. New servers SHOULD use streamable-http. See MCP Declarations for full details, transport guidance, and security considerations for all network transports.

Environment Variable References

Values in env (for stdio) and headers (for http) may use ${VAR_NAME} syntax to reference variables declared in the harness env array.

Validation rule: Every ${VAR_NAME} reference in mcp-servers MUST correspond to a declared entry in the top-level env array. Referencing an undeclared variable name is a validation error. This rule ensures that all variable dependencies are visible in the harness document and that implementations can prompt for missing values before attempting to start servers.

Inheritance (via extends)

When a child harness extends a parent, MCP server maps are unioned by server name. If both declare a server with the same name, the child's declaration takes precedence entirely (no field-level merge — the child's full server object wins).

Example

mcp-servers:
  postgres:
    transport: stdio
    command: uvx
    args:
      - mcp-server-postgres
      - --connection-string
      - "${DB_CONNECTION_STRING}"
    env:
      PGAPPNAME: my-harness
    source: "io.github.example/postgres"
    version: "1.4.2"

  data-api:
    transport: streamable-http
    url: "https://api.example.com/mcp"
    source: "io.github.example/data-api"
    headers:
      Authorization: "Bearer ${DATA_API_TOKEN}"
      X-Client-Version: "1.0"

env

env is an array of environment variable declarations. Each entry describes a variable that the harness requires or optionally uses at runtime.

Env Entry Fields

Field Type Required Default Description
name string Yes Variable name. Must match ^[A-Z_][A-Z0-9_]*$ — uppercase letters, digits, underscores; may start with an underscore.
description string Yes Human-readable description of the variable's purpose and expected format. Shown to users when prompting for a value.
required boolean No false If true, the implementation MUST verify the variable is set before applying the harness. Missing required variables are a fatal error.
sensitive boolean No true If true, the variable contains secret data. See security constraints below.
when string No Human-readable description of when this variable is needed (e.g., "When accessing private GitHub repositories"). Implementations MAY evaluate it as a condition expression but are NOT REQUIRED to do so; when not evaluated, it is displayed as informational text.
default string No Default value used when the variable is not set in the environment. Forbidden when sensitive: true.

Security Constraints

  • sensitive: true + default is FORBIDDEN. A harness with both sensitive: true and a default value on the same env entry MUST fail validation. Providing a default for a sensitive variable defeats the purpose of keeping it out of the harness file.
  • Sensitive variable values MUST NOT appear in logs, error messages, or stored configuration.
  • Implementations MUST treat env entries as sensitive: true by default. A variable is only non-sensitive when explicitly declared sensitive: false.

Constraint: Declaration Coverage

Every variable referenced via ${VAR_NAME} in mcp-servers MUST have a corresponding entry in env. The reverse is not required — env may declare variables that are not used in mcp-servers (they may be used by plugins or instructions).

Inheritance (via extends)

When a child harness extends a parent, env arrays are unioned by variable name. If both declare the same variable name, the child's declaration takes precedence.

Example

env:
  - name: DB_CONNECTION_STRING
    description: "PostgreSQL connection string (e.g., postgresql://user:pass@host/db)"
    required: true
    sensitive: true

  - name: DATA_API_TOKEN
    description: "API token for the data service"
    required: true
    sensitive: true

  - name: DEFAULT_SCHEMA
    description: "Default schema to use for SQL operations"
    required: false
    sensitive: false
    default: "public"

  - name: ENABLE_LINEAGE
    description: "Enable automatic lineage tracking (true/false)"
    required: false
    sensitive: false
    default: "true"
    when: "plugins contains 'data-lineage'"

instructions

instructions maps three instruction scopes and controls how they combine with inherited instructions.

Fields

Field Type Required Default Description
operational string or null No Instructions for how to work: build commands, architecture, gotchas. Maps to AGENTS.md — the cross-tool instruction file read natively by most implementations — and to CLAUDE.md (Claude Code) or the equivalent operational context file. Value is inline text, a file:// path, or an https:// URL.
behavioral string or null No Instructions for how to behave: tone, autonomy level, workflow conventions. Maps to AGENT.md (Claude Code) or the equivalent behavioral instructions file. Same value formats.
identity string or null No Identity context for the agent. Maps to SOUL.md (Claude Code) or the equivalent identity context file. Same value formats. Set to null to explicitly declare no identity instructions.
import-mode enum No merge How child instructions combine with parent instructions. Values: merge, replace, skip.

Value Formats

Each instruction field accepts three content formats:

Format Example Description
Inline text "Always use conventional commits." Literal string content included directly.
File reference file://./instructions/operational.md Path relative to the harness file. Resolved at apply time.
URL reference https://example.com/instructions.md Fetched at apply time. Must be HTTPS.

Import Mode Semantics

merge (default): The child's instruction content is appended after the parent's instruction content for each field. Both sets of instructions are active. This is the safe default — it never silently discards parent instructions.

replace: The child's instruction content replaces the parent's entirely for fields the child declares. Fields the child does not declare pass through unchanged from the parent. Because replace discards parent instructions — which may include safety or policy constraints — conformant implementations MUST require explicit user confirmation before applying a profile that uses import-mode: replace.

skip: The child declares no instructions. The parent's instructions pass through to the session unchanged. Useful for fragments that are purely additive (plugins, MCP servers) and intentionally defer to whatever instructions the consuming profile uses.

Inheritance Interaction

import-mode governs how this harness's instructions relate to the instructions inherited from extends. It does not affect how a child of this harness inherits from this harness — that is governed by the child's own import-mode.

Example

instructions:
  operational: file://./instructions/operational.md
  behavioral: |
    Prioritize correctness over speed. Always explain SQL query plans
    when writing new queries. Prefer CTEs over subqueries for readability.
  identity: null
  import-mode: merge

permissions

permissions defines the tool access, filesystem access, and network access boundaries for the agent session.

permissions.tools

Field Type Required Description
allow array of strings No Tool names or glob patterns the agent is permitted to use.
deny array of strings No Tool names or glob patterns the agent is explicitly forbidden from using. Deny takes precedence over allow.
ask array of strings No Tool names or glob patterns that require user confirmation before each use.

Tool name patterns support * as a wildcard. Examples:

  • "Read" — the Read tool by exact name
  • "mcp__*__delete_*" — any MCP tool whose name contains "delete"
  • "Bash" — the Bash tool

Inheritance rules for tools:

  • allow: Intersected across the inheritance chain. A tool is allowed only if every ancestor that defines a permissions.tools.allow list includes it (or uses a matching pattern). Most restrictive wins.
  • deny: Unioned across the inheritance chain. A tool is denied if any ancestor denies it. One ancestor's denial propagates to all children.
  • ask: Unioned across the inheritance chain. If any ancestor requires confirmation for a tool, confirmation is required.

permissions.paths

Field Type Required Description
writable array of strings No Paths (relative to project root) the agent may write to. Glob patterns supported.
readonly array of strings No Paths the agent may read but not write.

Path entries are relative to the project root unless they begin with /. Both writable and readonly support glob patterns.

Inheritance rules for paths: Path lists are unioned across the inheritance chain. A child can add writable/readonly paths but cannot remove paths that a parent has restricted. To remove a path restriction, use the team overlay syntax (v2 Exchange layer).

permissions.network

Field Type Required Description
allowed-hosts array of strings No Hostnames or hostname patterns (supporting * wildcard) that the agent may make network requests to.

If allowed-hosts is not declared, network permission behavior is implementation-defined.

Inheritance rules for network: allowed-hosts lists are unioned across the inheritance chain.

Example

permissions:
  tools:
    allow:
      - Read
      - Glob
      - Grep
      - Write
      - Edit
      - Bash
    deny:
      - "mcp__*__delete_*"
      - "mcp__*__drop_*"
    ask:
      - Bash
      - Write

  paths:
    writable:
      - src/
      - tests/
      - migrations/
    readonly:
      - config/
      - "*.lock"

  network:
    allowed-hosts:
      - "*.github.com"
      - "api.example.com"

policy

policy is an optional object expressing organization or team governance constraints. Unlike every other section, a policy is a ceiling: it constrains what extending or consuming profiles may grant, and downstream profiles may narrow it but never widen it. A document with no policy section imposes no managed constraints — exactly the behavior of v1 before this section existed. The protocol declares intent and specifies what implementations MUST reject at apply time; the runtime enforcement boundary remains the implementation's. See HEP-6 and Inheritance for precedence semantics.

Fields

Field Type Description
mcp-servers.allowed-sources array of strings Allowlist of MCP server source/host patterns. A declared server matching none is rejected.
mcp-servers.denied-sources array of strings Denylist of MCP server source/host patterns. Deny overrides allow.
plugins.allowed-sources array of strings Allowlist of plugin owner/repo patterns.
plugins.denied-sources array of strings Denylist of plugin patterns. Deny overrides allow.
plugins.allowed-marketplaces array of strings Allowlist of marketplaces/registries plugins may be fetched from.
skills.allowed-sources array of strings Allowlist of skill source patterns.
skills.denied-sources array of strings Denylist of skill source patterns. Deny overrides allow.
permissions.tools.allow array of strings Ceiling: the maximum set of tools any profile may grant. A grant matching none of these is a violation.
permissions.tools.deny array of strings Tools always denied regardless of any grant.
permissions.network.allowed-hosts array of strings Ceiling: the maximum set of network hosts any profile may allow.
require-integrity boolean (default false) When true, every plugin, skill, and MCP server package MUST carry a verifiable integrity hash; declarations without one are rejected.

Enforcement

A policy is applied after extends resolution produces a candidate effective configuration (see Application). A violation — a server/plugin/skill source outside the allowlist or matched by the denylist, a permission grant exceeding a ceiling, or a missing integrity hash when require-integrity is true — is a validation error. The harness is not applied; there is no partial application and no silent stripping of violating entries.

Constraints within policy.plugins are conjunctive: when both allowed-sources and allowed-marketplaces are present, a plugin must satisfy both — its source must match an allowed source and it must be fetched from an allowed marketplace.

Inheritance

policy does not merge like other sections. The effective policy is the accumulation of all policy sections in the resolution set: allowlists intersect, denylists union, permission ceilings intersect, and require-integrity is true if any layer sets it. A child can only make a policy stricter, never laxer.

Example

policy:
  mcp-servers:
    allowed-sources: ["io.github.acme/*", "io.modelcontextprotocol/*"]
    denied-sources: ["*/experimental-*"]
  plugins:
    allowed-sources: ["acme/*"]
    allowed-marketplaces: ["acme/internal-marketplace"]
  skills:
    allowed-sources: ["acme/*", "harnessprotocol/skills/*"]
  permissions:
    tools:
      allow: [Read, Grep, Glob, Edit, Write, Bash, "mcp__*"]
      deny: ["mcp__*__drop_*", "mcp__*__delete_*"]
    network:
      allowed-hosts: ["*.acme.internal", "api.anthropic.com"]
  require-integrity: true

extends

extends is an ordered array of parent harness references. The current document is the child. Parents are resolved and applied before the child's fields are merged on top. See Source Resolution for how source fields are resolved.

Extends Entry Fields

Field Type Required Description
source string Yes Parent harness source. owner/repo format, owner/repo/path/to/file.yaml, or a local path (./, ../).
version string No Semver range for the parent harness version. If absent, the implementation selects the latest compatible version.

Resolution Order

When multiple parents are listed, they are applied left to right. Later entries take precedence over earlier entries in the case of conflicts, before the child's own fields are applied. The child always takes final precedence.

For a harness with extends: [A, B]:

  1. A is resolved and applied.
  2. B is merged on top of A (B wins on conflicts).
  3. The child's own fields are merged on top (child wins on conflicts).

Section-Specific Merge Semantics

Section Merge behavior
plugins Union by name. Child/later wins on conflict.
skills Union by name. Child/later wins on conflict. enabled: false suppresses an inherited skill.
mcp-servers Union by server name. Child/later wins on conflict (full object, not field-level).
env Union by name. Child/later wins on conflict.
instructions Governed by import-mode (child's setting).
permissions.tools.allow Intersection (most restrictive).
permissions.tools.deny Union (any ancestor's denial propagates).
permissions.tools.ask Union (any ancestor's ask propagates).
permissions.paths Union (additive only).
permissions.network Union (additive only).
policy Does not merge like other sections — it accumulates as a ceiling (allowlists intersect, denylists union, permission ceilings intersect, require-integrity monotonic). A child can only tighten it. See policy.
metadata Child's metadata is used as-is; parent metadata is not merged.
kind Child's kind is used.

Circular Dependency

A harness MUST NOT extend itself directly or transitively. Implementations MUST detect circular extends chains and fail validation with a clear error.

Example

extends:
  - source: harnessprotocol/profiles/backend
    version: ">=1.0.0"
  - source: my-org/shared-harness
    version: "^2.1.0"

x- Extension Fields

Any top-level key or nested key prefixed with x- is an implementation extension field. The core schema does not define their structure. Conformant implementations MUST:

  1. Not reject a harness document solely because it contains unrecognized x- fields.
  2. Ignore x- fields they do not support.
  3. Not allow x- fields to shadow or override core schema fields.

Extension fields are not portable. Authors who use x- fields SHOULD document which implementation(s) support them.

# Example: Claude Code-specific model hint (ignored by other implementations)
x-claude-model: claude-opus-4
x-claude-thinking-budget: 10000

Full Example

$schema: https://harnessprotocol.io/schema/v1/harness.schema.json
version: "1"
kind: profile

metadata:
  name: data-engineer
  description: "Harness for data engineering: PostgreSQL, lineage, dbt."
  author:
    name: alice
    url: https://github.com/alice
  version: "1.2.0"
  license: Apache-2.0
  tags: [data-engineering, postgresql, sql, dbt]

plugins:
  - name: data-lineage
    source: harnessprotocol/harness-kit
    version: ">=0.2.0"
    config:
      default-schema: public
    integrity:
      sha256: "a3f1e2b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2"

mcp-servers:
  postgres:
    transport: stdio
    command: uvx
    args: [mcp-server-postgres, --connection-string, "${DB_CONNECTION_STRING}"]

env:
  - name: DB_CONNECTION_STRING
    description: "PostgreSQL connection string"
    required: true
    sensitive: true

instructions:
  operational: file://./instructions/operational.md
  behavioral: "Prioritize query correctness. Explain plans for new queries."
  import-mode: merge

permissions:
  tools:
    allow: [Read, Glob, Grep, Write, Edit, Bash]
    deny: ["mcp__postgres__drop_*"]
    ask: [Bash]
  paths:
    writable: [sql/, migrations/, dbt/]
    readonly: [config/]
  network:
    allowed-hosts: ["*.github.com"]

extends:
  - source: harnessprotocol/profiles/backend
    version: ">=1.0.0"