Concepts

Projects

Repositories where HarnessTap scans, applies, mirrors, and tracks harness configuration.

A project is a repository (or directory tree) where HarnessTap materializes harness configuration. Project workflows center on scan, apply, mirror, drift detection, and snapshot revert — all scoped to the working tree rather than machine-wide home paths.

Repo-scoped apply

apply is the canonical write path for project baselines:

ht apply my-setup --project . --harness claude-code,codex,cursor --dry-run
ht apply my-setup --project . --harness claude-code,codex,cursor
ht status .

Stack multiple plugins in one command (see Scenario 25). apply resolves bare catalog names at apply time, so public baselines work without a prior plugin pull:

ht apply engineering-foundation --project .

Plugin-version policy when the plugin carries plugin pins:

ht apply my-setup --strict-plugin-versions   # exit 2 on pin violation
ht apply my-setup --ignore-plugin-versions   # skip validation
ht apply my-setup --sync-plugins             # refresh plugin resources first

Applying a plugin writes a known baseline onto disk. It is distinct from mirror, which syncs alias harness outputs from the current on-disk main harness without re-specifying the plugin.

Scan and track

Discover existing configuration before composing plugins:

ht scan .
ht resource list
ht plugin from-project inferred-stack --project .

When the target repository has a git origin, apply stores a snapshot of tracked generated files before writing. Snapshots power history, drift detection, and revert.

CommandGit requirement
historyGit-backed project with origin
status --checkGit-backed project
apply (with snapshot)Git origin on target project
revertSnapshot ID from history
harness project set / harness project statusGit-backed project

apply can write files outside git, but snapshot and history support only works when the target project has a git origin.

Mirror

mirror propagates configuration from the main harness to alias harnesses in the same repository — useful after manual edits to the primary harness files:

ht mirror .
ht mirror . --force-shift-reference codex
ht mirror . --dry-run

Mirror compares and shifts references between harness-specific file layouts. It does not re-resolve plugin composition; use apply when you need a fresh baseline from the library.

See Scenario 27 for the cross-harness mirror walkthrough and Scenario 33 for plugin fallback behavior.

Drift and revert

After apply, teammates may edit generated files directly. HarnessTap tracks drift against the last apply or mirror snapshot:

ht status . --check
ht status . --check --format json   # exit 1 when drift exists
ht history .
ht revert <snapshot-id>

status --check compares the current working tree against the latest apply/mirror snapshot. Exit code 1 means actionable drift was found — useful in CI guardrails.

See Scenario 21.

Project harness preferences

Per-project harness settings (main and aliases for this repo) are separate from global harness set:

ht harness project status .
ht harness project set --main claude-code --aliases cursor

These require a git-backed project and influence which harnesses apply and mirror target by default.

Snapshots in practice

Typical lifecycle:

  1. ht apply team-baseline --project . — writes files, stores snapshot (when git origin exists)
  2. Developer edits .cursor/rules/foo.mdc by hand
  3. ht status . --check — reports divergence from snapshot
  4. Either re-apply the plugin, mirror from main, or ht revert <id> to restore

Preview before writing:

ht apply team-baseline --project . --dry-run

See Scenario 7.

Profiles vs projects

ProfilesProjects
ScopeMachine home harness pathsRepository working tree
Primary commandprofile useapply
Typical useWork/personal machine presetsTeam repo baselines
Drift / revertNot trackedstatus --check / revert

Use Profiles for machine-wide defaults and projects for repository-specific configuration.

Project profile config

Repositories can declare named profiles, environments, and plugin composition in apm.yml at the repo root. Standard OpenAPM keys (name, version, targets, dependencies) still parse. Extra top-level keys are preserved. HarnessTap-only fields are first-class top-level keys (default_profile, environments, profiles, plugins); vanilla APM readers ignore them. environments.default names the active environment; other environments keys are named how-value bundles (secret refs only).

Example:

name: demo
version: "1.0.0"
targets: [cursor, claude]
dependencies:
  apm:
    - team-stack
  mcp:
    - io.github.github/github-mcp-server
    - name: filesystem
      registry: false
      command: npx
      args: ["-y", "@modelcontextprotocol/server-filesystem", "/workspace"]
default_profile: dev
environments:
  default: shared
  shared:
    values:
      REGION: us
    secret_refs:
      API_KEY:
        provider: env
        ref: API_KEY
profiles:
  - name: dev
    source: local
    selector: team-stack
  - name: prod
    source: catalog
    selector: acme/platform/frontend@1.0.0
  - name: custom
    source: inline
    plugin: embedded-plugin
plugins:
  - name: embedded-plugin
    description: Small inline plugin bundled with the repo

Inspect and validate the resolved config:

ht config show
ht config show --format json
ht config validate --project .
ht config validate --format json   # exit 1 when invalid

Create a starter config from local profile plugins:

ht config init
ht config init --profile work --profile personal --default work
ht config init --force   # overwrite an existing file

config init maps each selected profile plugin to a local source: local entry and sets default_profile. Opening a project in HarnessTap Desktop instead seeds a project default profile from that repository’s on-disk resources. It does not add global default to the project.

Switch to a configured profile with ht use:

ht use                        # interactive picker when multiple profiles exist
ht use --profile dev          # apply the dev profile directly
ht use --list                 # list profiles without applying

Project profiles reuse the same plugin sources as machine-wide Profiles, but apply through ht use in the repository instead of profile use at home paths.