Files
ECC/docs/architecture/harness-adapter-compliance.md
T
eb49702651 feat: thin Pi adapter mounting ECC's canonical skills and commands (#2759)
* feat: add thin Pi adapter mounting ECC's canonical skills and commands

Adds first-class Pi (@earendil-works/pi-coding-agent) support as a thin
adapter layer, following the maintainer review on #2352. ECC's canonical
assets stay the single source of truth: nothing is copied or generated
under .pi/.

The `pi` manifest in package.json points Pi directly at `skills/` and
`commands/`. No transformation is needed — ECC's SKILL.md files already
follow the Agent Skills standard Pi implements, and ECC's command
frontmatter is already Pi's prompt-template format.

.pi/extensions/index.ts is the only adapter logic. It:

- uses Pi's documented `pi.on(...)` lifecycle, not an undocumented event bus
- resolves hook scripts from the installed package via `__dirname`, never
  `process.cwd()`, so global installs work from any project directory
- runs hooks with `execFile(process.execPath, [...])` and no shell, so paths
  containing spaces or shell metacharacters are safe
- invokes hooks through ECC's own `run-with-flags.js`, so `ECC_HOOK_PROFILE`
  and `ECC_DISABLED_HOOKS` keep gating hooks under Pi
- runs hooks in the user's project directory so project detection stays
  correct, while resolving the scripts themselves package-relative
- injects the SessionStart hook's `additionalContext` into the system prompt
  on the next `before_agent_start`
- isolates hook failures behind a timeout and an output limit
- registers `/ecc-doctor` for install diagnostics

Registers `.pi` in the platform-configs install module and adds a Pi row to
the harness adapter compliance matrix.

Verified against Pi 0.84.1: a global `pi install` exposes 285 skills and 94
commands resolved from `skills/` and `commands/`, plus `/ecc-doctor`, with
no generated copies.

Scope deliberately excludes subagents, chains, approval gates, todos,
profiles, and MCP; ECC works in Pi without any companion package.

* fix: address review findings on the Pi adapter

Bot review on #2759 surfaced two real runtime defects and several
hardening gaps.

Runtime fixes:

- Attach an `error` listener to the hook child's stdin. `stdin.end()`
  writes asynchronously, so a hook that exits, short-circuits, or is
  killed by the timeout before reading the payload raises EPIPE as an
  `error` event that the surrounding try/catch cannot see. Unhandled,
  that event would terminate the Pi session and break the isolation
  guarantee the adapter documents.
- Clear `pendingContext` at the top of the `session_start` handler. Pi
  can start a new session (/new, /resume, /fork) before
  `before_agent_start` consumes the previous value; if the newer hook
  then failed, the next agent start received context describing a
  different session's project state.
- Replace `require.resolve` companion detection with a read of Pi's own
  `packages` list, honoring `PI_CODING_AGENT_DIR`. Pi installs packages
  under its config directory, which is not on Node's module resolution
  path from the extension, so the previous check reported every
  companion as missing no matter what was installed.

Compliance matrix: remove internal semicolons and a trailing period from
the Pi record's list entries. The renderer joins entries with "; ", so
those characters split one entry into several in the rendered cell.

Tests: run profile gating against the temp skeleton instead of the real
checkout so it cannot leave marker artifacts behind; count files under
.pi/ by walking disk rather than git, so untracked copies cannot bypass
the regression guard; allow negated phrasing in the README heuristic;
pin the adapter's real parser guards with source assertions so the local
mirrors cannot silently diverge; add coverage for EPIPE isolation, stale
context clearing, and companion detection.

* docs: point users at existing companion Pi packages instead of bundling them

Every capability listed as out of scope is already provided by a maintained
community Pi package: pi-subagents, @juicesharp/rpiv-ask-user-question,
@juicesharp/rpiv-todo, and pi-mcp-adapter for MCP.

Pi supports pulling other pi packages in via dependencies plus
bundledDependencies, but this adapter deliberately does not. Bundling would
ship third-party code that executes with full user permissions in every ECC
install, turn optional capabilities into mandatory ones, and add four
fast-moving pins to maintain.

Instead /ecc-doctor now prints the exact `pi install npm:<name>` command for
each companion it does not find, so adopting one stays a deliberate user
choice.

Also corrects the MCP claim: Pi core has no MCP surface by design, but the
community pi-mcp-adapter package adds one. This adapter neither installs nor
verifies it, and ECC's MCP reference configs are not known to be compatible.

* docs: ECC's MCP configs work in Pi through pi-mcp-adapter, verbatim

Tested rather than assumed. The community pi-mcp-adapter package reads the
standard mcpServers format from .mcp.json and ~/.config/mcp/mcp.json, which
is exactly the format ECC already uses in .mcp.json and
mcp-configs/mcp-servers.json.

Verified against pi-mcp-adapter 2.21.2 in an isolated PI_CODING_AGENT_DIR:
copying mcp-configs/mcp-servers.json to a project's .mcp.json registers Pi's
`mcp` tool and `/mcp` command with all 35 ECC servers discovered, coexisting
with this adapter's /ecc-doctor. No translation layer and no ECC change are
needed, so this stops being a limitation and becomes documentation.

Recorded caveats: the adapter's first run against a new config performs
initialization that blocks in non-interactive mode, and only discovery was
verified, not live tool invocation.

ECC still neither installs nor depends on the package.

* feat: inject ECC's canonical engineering rules into Pi's system prompt

ECC's rules were the one durable asset the adapter did not deliver: skills
and commands reached Pi in full, but the 122 rule files that carry ECC's
coding style, testing, security, git workflow, and code-review standards
did not, so ECC in Pi was a library of skills rather than a set of
enforced standards.

Rules are read at runtime from the canonical rules/common/ directory of
the installed package and appended to the system prompt inside an
<ecc-engineering-rules> block. Nothing is copied or generated under .pi/,
which keeps the single-source-of-truth constraint this PR exists to
satisfy. Injection reuses the before_agent_start path already built for
session context, so no new lifecycle mapping is introduced.

Rules are re-applied every turn because they are standing policy, while
the session context stays one-shot and is consumed on first use.

agents.md, hooks.md, and performance.md are excluded: they describe Claude
Code primitives Pi does not have (Task/TodoWrite delegation, Claude hook
event types, thinking-budget toggles), so injecting them would point the
model at tools that are not there. A test asserts they stay excluded, and
a leakage test asserts none of those primitives appear in the injected
text. Language-specific rules under rules/<language>/ are out of scope for
this first adapter.

Injection is bounded by MAX_RULES_BYTES and can be disabled with
ECC_PI_RULES, following ECC's existing off-switch convention. /ecc-doctor
reports the state and injected size.

Measured on this repo: 7 files, 12,361 characters, roughly 3k tokens.

Also replaces a Function() call in the test helper with direct arithmetic,
and repins a stale assertion that pinned one spelling of the context
handoff rather than the guarantee (read before clear, clear before return).

* fix: /ecc-doctor misreported filtered packages and partial rule installs

Two reporting defects in /ecc-doctor, the command whose whole job is telling
a user what is actually installed.

Pi's settings accept a `packages` entry in two shapes: the bare source string
("npm:pi-subagents") and an object carrying that source alongside resource
filters ({ source: "npm:pi-subagents", skills: [] }). normalizePiPackageName
only recognized the string, so a user who narrowed which resources a companion
contributes was told the companion was not installed, along with an install
command for something already present. The source type still decides whether a
name is comparable, so an object wrapping a git source or a path stays
unrecognized exactly as before.

loadPortableRules drops rule files it cannot read, drops empty ones, and stops
at MAX_RULES_BYTES, but describeRulesStatus reported PORTABLE_RULE_FILES.length
regardless. A partial install that loaded 3 of 7 files reported "7 rule file(s)"
to the one command a user runs to find a partial install. The loaded count is
now tracked next to the cache and reported as a ratio, with the shortfall named.

Also reconciles the Notes bullet in .pi/README.md, which still called MCP out of
scope after the MCP section landed documenting that ECC's configs load in Pi
through pi-mcp-adapter.

Both defects were reported by CodeRabbit and verified against Pi's own
packages.md before fixing. Adapter tests go from 24 to 26; the two source
contracts that pinned the previous spellings now pin the new guards, so the
object-form unwrapping and the loaded-count reporting cannot be silently
reverted.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 18:41:33 -04:00

8.5 KiB

Harness Adapter Compliance Matrix

This matrix is the public onramp for teams that want to use ECC across more than one coding harness. It turns the cross-harness architecture into a practical scorecard: what works today, what is instruction-only, what needs an adapter, and what evidence an operator should collect before trusting a setup.

ECC's durable units stay in shared sources:

  • skills/*/SKILL.md
  • rules/
  • commands/
  • hooks/hooks.json
  • scripts/hooks/
  • MCP reference configs
  • session and observability contracts

Harness-specific files should only adapt loading, event shape, command names, or platform limits.

Compliance States

State Meaning
Native ECC can install or verify the surface directly for this harness.
Adapter-backed ECC has a thin adapter, plugin, or package surface, but parity differs by harness.
Instruction-backed ECC can provide the guidance and files, but the harness does not expose the runtime hook/session surface ECC needs for enforcement.
Reference-only The tool is useful as a design pressure or external runtime, but ECC does not yet ship a direct installer or adapter for it.

Matrix

The matrix below is rendered from scripts/lib/harness-adapter-compliance.js and verified by npm run harness:adapters -- --check.

Harness or runtime State Supported assets Unsupported or different surfaces Install or onramp Verification command Risk notes
Claude Code Native Claude plugin assets; skills; commands; hooks; MCP config; local rules; statusline-oriented workflows Claude-native hooks do not imply parity in other harnesses ./install.sh --profile minimal --target claude; Claude plugin install npm run harness:audit -- --format json; node scripts/session-inspect.js --list-adapters Avoid loading every skill by default; keep hooks opt-in and inspectable.
Codex Instruction-backed AGENTS.md; Codex plugin metadata; skills; MCP reference config; command patterns Native hook enforcement and Claude slash-command semantics are not equivalent ./install.sh --profile minimal --target codex; repo-local AGENTS.md review npm run harness:audit -- --format json Treat hooks as policy text unless a native Codex hook surface exists.
OpenCode Adapter-backed OpenCode package/plugin metadata; shared skills; MCP config; event adapter patterns Event names, plugin packaging, and command dispatch differ from Claude Code OpenCode package or plugin surface from this repo node tests/scripts/build-opencode.test.js; npm run harness:audit -- --format json Keep hook logic in shared scripts and adapt only event shape at the edge.
Pi Adapter-backed Pi package manifest; canonical ECC skills (skills/); canonical ECC commands as prompt templates (commands/); canonical ECC engineering rules (rules/common/) injected into the system prompt; session lifecycle hook adapter; /ecc-doctor diagnostics command Subagents, chains, approval prompts, and persistent todos require companion Pi packages and are not part of this adapter; Pi core has no MCP surface, though ECC MCP configs load verbatim through the community pi-mcp-adapter package, which ECC neither installs nor depends on pi install git:github.com/affaan-m/ECC; pi install /path/to/ECC from a local checkout node tests/pi/pi-package-manifest.test.js; node tests/pi/pi-extension-adapter.test.js; npm run harness:adapters -- --check Pi extensions execute with full user permissions, and hooks run without a shell and resolve from the installed package rather than the user project; Keep canonical skills and commands as the single source of truth, and never generate copies under .pi/
Cursor Adapter-backed Cursor rules; project-local skills; hook adapter; shared scripts Cursor hook events and rule loading differ from Claude Code ./install.sh --profile minimal --target cursor node tests/lib/install-targets.test.js; npm run harness:audit -- --format json Cursor adapters must preserve existing project rules and avoid silent overwrite.
Gemini Instruction-backed Gemini project-local instructions; shared skills; rules; compatibility docs No full ECC hook parity; ecosystem ports must document drift from upstream ECC ./install.sh --profile minimal --target gemini node tests/lib/install-targets.test.js Treat Gemini ports as ecosystem adapters until validated end to end inside Gemini CLI.
Zed Adapter-backed Zed project settings; flattened project rules; shared skills; commands; agents Zed external agents and native Agent Panel permissions are not Claude hooks ./install.sh --profile minimal --target zed node tests/lib/install-targets.test.js; npm run harness:audit -- --format json Keep project settings conservative and do not copy BYOK/OpenRouter secrets into .zed/.
dmux Adapter-backed session snapshots; tmux/worktree orchestration status; handoff exports dmux is an orchestration runtime, not an install target for skills/rules node scripts/session-inspect.js --list-adapters; dmux session target inspection node tests/lib/session-adapters.test.js Treat dmux events as session/runtime signals, not as a replacement for repo validation.
Orca Reference-only worktree lifecycle; review state; notification; provider-identity design pressure No ECC installer or direct adapter today Use as a comparison target for worktree/session state requirements npm run observability:ready Do not import product-specific assumptions; convert lessons into ECC event fields.
Superset Reference-only workspace presets; parallel-agent review loops; worktree isolation design pressure No ECC installer or direct adapter today Use as a comparison target for workspace preset taxonomy npm run observability:ready Keep ECC portable; do not require a desktop workspace to get basic value.
Ghast Reference-only terminal-native pane grouping; cwd grouping; search; notifications No ECC installer or direct adapter today Use as a comparison target for terminal-first session grouping node scripts/session-inspect.js --list-adapters Preserve terminal ergonomics before adding visual UI assumptions.
Terminal-only Native skills; rules; commands; scripts; harness audit; observability readiness; handoffs No external UI, no automatic session control unless scripts are run explicitly Clone repo; run commands directly; use minimal profile for project installs npm run harness:audit -- --format json; npm run observability:ready This is the fallback contract; every higher-level adapter should degrade to it.

Scorecard Onramp

Use this sequence before asking ECC to make a team or repo setup more autonomous:

npm run harness:adapters -- --check
npm run harness:audit -- --format json
npm run observability:ready
node scripts/session-inspect.js --list-adapters
node scripts/loop-status.js --json --write-dir .ecc/loop-status

Read the result as a setup scorecard, not a product badge:

  • harness:adapters -- --check proves this public matrix still matches the adapter source data and required evidence fields.
  • harness:audit scores tool coverage, context efficiency, quality gates, memory persistence, eval coverage, security guardrails, and cost efficiency.
  • observability:ready proves the repo still exposes the local status, session, tool-activity, risk-ledger, and release-onramp signals.
  • session-inspect --list-adapters shows which session surfaces are actually inspectable in the current environment.
  • loop-status --json creates a machine-readable handoff/status payload for longer autonomous runs.

Data-Backed Scorecard Contract

Each adapter record exposes:

  • id
  • state
  • supported_assets
  • unsupported_surfaces
  • install_or_onramp
  • verification_commands
  • risk_notes
  • last_verified_at
  • owner
  • source_docs

The validator fails if a public adapter claim has no install path, verification command, risk note, owner, source doc, or verification date.

Operating Rules

  • Prefer small, additive adapters over harness-specific forks of the same workflow.
  • Do not call a harness native until the adapter has an install path and a verification command.
  • Keep Codex, Gemini, and Zed surfaces honest when enforcement is instruction-backed rather than runtime-backed.
  • Treat reference-only tools as design pressure until ECC has a direct adapter.
  • Keep the terminal-only path healthy; it is the portability floor.