Skip to content

harness-select

Declarative choice of WHICH harness targets spex materialize delivers into — spexcode.json's harnesses set (native ids, or one plugin), validated fail-loud; deselecting a harness prunes its artifacts.

A project does not always want SpexCode delivered into every harness. harness-select is the ONE declarative knob for that choice: the harnesses field in spexcode.json. It is PERSISTENT config, never a one-shot flag, because harness-delivery's materialize is driven by a content-hash gate (re-run on every .config edit) — the intent must live where every re-materialize re-reads it, not in a command a human has to remember.

The vocabulary is small. Each member is either a NATIVE harness id (claude, codex — the ids the harness-adapter registers) or a PLUGIN bundle. Omitting the field defaults to every native harness (the zero-config "deliver natively, everywhere"); it never silently collapses to "nothing".

Two invariants are enforced fail-loud — an illegal set aborts materialize/spex init with a stated reason, never a silent or partial delivery:

  • plugin exclusivity — a set containing a plugin may carry NO native harness. A plugin bundle is a SUPERSET delivery to its host agent, so pairing it with a native harness double-delivers. Choose EITHER native harnesses OR plugin(s).
  • explicit plugin folder — a plugin target MUST name its landing folder (.claude / .zcode / .codex / custom), because every host agent scans a different plugins dir. A bare "plugin" with no folder is rejected.

This node owns ONLY the vocabulary + validation (resolveHarnessTargets) and the select-vs-prune split (partitionHarnesses, which hands the resolved plugin folders to its plugins result). It does NOT emit plugin bundles — that is plugin-harness, which materialize drives off partitionHarnesses's plugins; here a plugin target only validates (and, being exclusive, leaves every native harness UNSELECTED → pruned).

Selection has a back-edge. materialize write()s the SELECTED harnesses and clean()s the UNSELECTED ones (the harness-adapter's clean primitive), so NARROWING harnesses prunes the dropped harness's products on the next re-materialize — its managed contract block, generated shim, trust, and named skill/agent files — while the user's own prose and .spec data are never touched.