AGENTS.md · git:20260825.1a5c382 · 2026-08-25 · sha256 bdbc1311d1285d0a

AGENTS.md git:20260825.1a5c382A

Immutable. This exact content is served forever at /api/v1/blob/bdbc1311d1285d0a.

# AGENTS.md

mARC (**Multi-Agent Relay Control**) is a Claude Code **plugin + self-marketplace**
that packages a portable, cross-repo AI engineering team: `@techlead` (channel
operator) convenes specialists `@dev`, `@sre`, `@design`, `@sec`, `@research`. The
team's
*governance* travels via one plugin; each consuming repo keeps its own facts.

**This repo is special: it is BOTH the product's source AND where the team dogfoods
on itself.** Self-improvement lessons legitimately flush to source *here* — the one
context where that is allowed (see Constraints).

## Architecture
- **Plugin** lives at `harnesses/claude-code/marc/`; the **marketplace** manifest is
  at repo root `.claude-plugin/marketplace.json` (name `nexaduo`, plugin `marc` →
  install `marc@nexaduo`, invoke `/marc:tech-lead`).
- **Leaders = skills** (`skills/<leader>/`, invoked `/marc:<leader>`); **specialists
  = a shared flat pool** (`agents/*.md`). Any leader convenes any specialist.
- Nesting under `harnesses/<harness>/` reserves the namespace for future non–Claude-Code
  harnesses. The growth model is in [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) —
  don't duplicate it here.
- **`core/` is the editable source; `harnesses/*/marc/` (skills, agents) is compiled
  output — never hand-edit it.** Run `scripts/compile_prompts.py` to regenerate from
  `core/` + each harness's `compile.json`, and commit the regenerated files.

## Operating principles
These distill a comparative study of agent-orchestration frameworks (OpenHands,
MetaGPT, CrewAI, Claude Code Agent Teams) down to what holds for mARC:
- **Stateless dispatch / context hygiene.** Specialists run fresh per task; durable
  state lives in git, the board/issues, and this file — not in long conversations.
- **Spec-driven leverage.** A vague task propagates destructively across parallel
  agents. The tech-lead's main leverage is spec clarity + acceptance criteria *before*
  dispatch (the sufficiency gate).
- **Dispatch in the background; isolate parallel writers.** Synchronous dispatch blocks
  the operator channel — fan out in background and track via notifications. When
  specialists edit files concurrently, isolate them (git worktrees) to avoid clobbering.
- **Bounded loops.** Cap runaway debugging; stop and surface rather than burn tokens in
  an infinite fix loop.
- **Tiered, size-capped memory with absolute decay.** Local/session memory is
  bounded (≤ 200 lines / ~2 KB) and organized as a lightweight recall index
  separating permanent invariants (`[PINNED]`) from time-bound entries with
  absolute dates (`[EXPIRES: YYYY-MM-DD]`). Substantive findings or oversized
  bodies spill into PR-gated artifacts (`docs/marc/` or consumer workspace) and
  are fetched on demand, never loaded unconditionally.
- **No self-merge; independent review.** Every PR gets a security pass; the author can't
  self-approve.

## Concurrent operators on one clone
Harnesses are **board-mediated peers** (#202) — two `@techlead` operators (different
harnesses, or two sessions) may run against the same clone at once. There is no
supervisor between them; these four rules are the whole coordination protocol (#204,
#205):
- **The assignee field is the claim token, not Status.** Assign yourself
  (`gh issue edit <N> --add-assignee @me`) *before* dispatching, then set Status to
  `In Progress`. Only the assignee carries operator identity: Status is a shared enum
  with no author, so it can say an item is claimed but never *by whom* — and
  `board.py reconcile` doesn't surface assignees on the board-configured path
  (`core/scripts/board.py:417`). Verify with `gh issue view <N> --json assignees`.
  GitHub's own coding agent claims work this way; Renovate does **not** — it has no
  claim field at all and serializes with external CI locks plus a per-instance work
  directory, so it is precedent for single-writer *outcome*, not for this mechanism.
- **The claim is racy, knowingly.** `board.py::set_status` is read-check-act with no
  compare-and-swap, so simultaneous claims silently last-write-wins. This is **accepted,
  not deferred**: GitHub's GraphQL exposes no optimistic-concurrency field on
  `UpdateIssueInput` or `UpdateProjectV2ItemFieldValueInput` (verified against the live
  schema), so there is nothing to adopt and closing it means building external locking.
  Re-read the *assignees* after claiming. If you aren't alone, break the tie
  deterministically — the **case-insensitively lowest login keeps the item**; the rest
  run `gh issue edit <N> --remove-assignee @me` and re-pick. Fold case, or two harnesses
  can reach opposite answers from the same read (`Bob` vs `alice`: raw picks `Bob`,
  folded picks `alice`). Never both-drop: a mutual drop stalls
  the item nobody then owns. The loser isn't starved of work, but it does lose every
  contested claim to a lower-sorting peer — accepted; rotation isn't worth machinery at
  two operators.
- **Stale claims are reclaimed by a human, never by a timer.** An item sitting
  `In Progress` with no linked PR is *not* self-evidently abandoned — TTL reapers
  misfire on slow-but-alive workers. Surface it and ask; don't auto-steal. Where you
  don't control the peer operator, a claim that never clears is a **squat** — escalate,
  don't race it.
- **Isolate-parallel-writers extends to the operators themselves**, not just to the
  specialists they dispatch: any operator that will mutate files takes its own worktree,
  and two of them never share a branch or working tree. One `.git` hosts many worktrees
  cross-harness (verified).

## Constraints
- **Anti-anchoring / genericization (hard gate):** everything under `harnesses/` must
  stay **stack-agnostic** — zero references to any consuming repo's stack. CI enforces
  this with a grep gate. Repo-specific facts belong in the *consuming* repo's
  `AGENTS.md`/`.agents/team.toml`, read at runtime — never hardcoded here.
- **Keep this file minimal.** Record only what isn't discoverable by reading the repo.
  If agents repeat a mistake, tighten the linter/CI/test — don't grow prose here. LLMs
  anchor on whatever sits in context, including deprecated caveats.
- **An installed plugin is immutable from the user's side.** Product changes flow only
  through releases: bump `harnesses/claude-code/marc/.claude-plugin/plugin.json`
  `version` + `CHANGELOG.md`; users get it via `claude plugin update marc@nexaduo`.
  Never edit the plugin cache; auto-update is OFF for third-party marketplaces.
- **Self-improvement is context-gated:** flush-to-source is allowed ONLY in this repo.
  When mARC runs *installed* in a user's repo, lessons persist **locally** (their
  `AGENTS.md`/`team.toml`/memory); product-level lessons become **opt-in,
  human-approved, sanitized** upstream PRs — never autonomous, never leaking user
  context. This machinery lives in a dedicated `@scribe` agent, not the tech-lead skill.
- **No silent file writes** in any repo. Onboarding (`/marc:init`) is opt-in and shows
  content before writing.
- **Durable team artifacts (PEF, #46): `docs/marc/`, operator-materialized, PR-gated.**
  `@research` briefs / `@sec` reports / decision records worth keeping land in
  `docs/marc/` per its README (`YYYY-MM-DD-<type>-<slug>.md`). That folder is served
  **publicly** by GitHub Pages — nothing sensitive goes there, ever. `@sec`/`@research`
  stay strictly comment-only; the operator (`@techlead`) copies the issue comment into
  the file and lands it via a reviewed PR — never a direct commit, never a write
  carve-out for read-only agents. This binding is THIS repo's; consumer repos pin their
  own via `team.toml` (`workspace_dir`) / their `AGENTS.md`.
- **Zero-config is a feature:** the team must work in any repo with no `team.toml`
  (runtime discovery via `gh` + session memory). Don't regress that.

## Release phases
There is **no staging/prod deploy pipeline** — mARC is a distributable plugin, not a
hosted stack. Don't fake staging/prod phases. "Done" here means:
1. PR with **green CI** (`.github/workflows/ci.yml`: Tier 1 structural + Tier 2
   install/cross-repo — deterministic, no secret, no token cost).
2. **Security review before merge** (`@sec`); **no self-merge**. Skill/agent changes
   carry a high review bar (injection surface).
3. **Version bumped + CHANGELOG** updated.
4. For user-facing behavior, **dogfood in a real repo** and confirm the *terminal* state
   (PR/CI/logs), not the "enqueued" step.
- Validate locally with `claude plugin validate harnesses/claude-code/marc` (a benign
  `minimumVersion` "unknown field" warning is expected — don't "fix" it away).
- The landing page (`docs/` → GitHub Pages → marc.nexaduo.com) is served DNS-only; a
  `SessionStart` hook injects `team.toml` and nudges on outdated versions.

## Lessons
- **Base freshness — branch from freshly-fetched `origin/main`.** Local `main` does NOT
  advance when PRs merge on the remote; a stale base makes already-merged work reappear
  as conflicts. For a stale open PR, run `gh pr update-branch`. (This bites humans too:
  always `git fetch` before reasoning about what's merged.)
- **Never delete flagged code on a stale base.** A security review on a stale base once
  nearly recommended *reverting live merged code*. Re-sync the base (the three-dot PR
  diff is authoritative), don't strip the flagged lines.
- **The self-improvement loop can bug itself.** Dogfooding surfaced that the improvement
  machinery misbehaves installed-vs-in-source — hence the context gating. Trust the
  gate, not the instinct to "just flush to source."
- **Don't fabricate work.** On a quiet channel, ask for the demand or triage the board;
  never invent tasks to look busy.