Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 96 additions & 14 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,83 @@ PMOVES.AI is a modular AI agent platform organized as a **submodule monorepo**,
- **`.claude/`** — Claude Code context, commands, hooks, MCP config
- **Root** — `Makefile` (delegates to pmoves), `CLAUDE.md`, `CONTRIBUTING.md`, `SECURITY.md`

## Operating in This Repo (Non-Obvious Rules)

These are the load-bearing conventions that are **not** obvious from reading a single file. Violating them has cost the fleet many hours. Full detail in [`.claude/PATTERNS.md`](.claude/PATTERNS.md) and [`.claude/BOOTSTRAP.md`](.claude/BOOTSTRAP.md).

### Known Roads — dangerous ops go through Make targets

Damage-control hooks block raw `docker`, `netsh`, `tailscale`, and `gh workflow` commands and redirect to an `ask` prompt. Every dangerous-but-necessary operation has a **canonical Make target** that bypasses the hook (it encapsulates the correct stop/restart/env-injection flow). When blocked, read the prompt — it names the target.

| Raw command (blocked) | Known Road |
|---|---|
| `docker volume rm <svc>` | `make -C pmoves volume-reset SERVICE=<svc>` |
| `docker compose up -d <svc>` | `make -C pmoves up-<svc>` (some services use **grouped** targets — e.g. Firefly/Wger/Open-Notebook/Jellyfin are `up-external`; Agent Zero/Archon are `up-agents`; run `make -C pmoves help` to find the real target) |
| `docker compose restart <svc>` | `make -C pmoves secrets-funnel && make -C pmoves up-<svc>` (re-injects secrets, then restarts the single service) |
| `tailscale status` (leaks raw IPs) | `make -C pmoves fleet-status` |
| `gh workflow run sync-secrets-local` | `make -C pmoves secrets-sync-trigger` |
| raw `-f docker-compose.<overlay>.yml up` | `make -C pmoves overlay-up-<tier>` (see layering trap below) |

Use raw commands **only** when the user explicitly directs.

### `env.shared` is Docker `env_file` format, NOT bash

Never `source pmoves/env.shared` — Windows paths and section headers will produce "command not found" errors and leave variables unset. Use the canonical loader:

```bash
bash pmoves/scripts/with-env.sh <command> # run any command with env.shared loaded
bash pmoves/scripts/with-env.sh pytest pmoves/tests/... # pytest with service env
```

To extract a single variable: `bash pmoves/scripts/with-env.sh bash -c 'printf "%s\n" "$MY_VAR"'` (the canonical loader — do not use `cut -d= -f2` which truncates values containing `=`, e.g. JWTs / base64 padding).

### Compose overlay layering — the single-file trap

The stack is split into `docker-compose.base.yml` (networks + anchors) + 6 tier overlays (`core` / `agents` / `media` / `ui` / `workers` / `apps`). Invoking `docker compose -f docker-compose.<overlay>.yml up -d` raw fails with `service "<svc>" refers to undefined network <name>` because the base layer is missing. Always use `make -C pmoves overlay-up-<tier>` (or `overlay-up-full`). Safe read-only validation (include base layer so networks/volumes resolve): `docker compose -f pmoves/docker-compose.base.yml -f pmoves/docker-compose.<overlay>.yml config`. Full runbook: `pmoves/docs/operations/COMPOSE_LAYERING_RUNBOOK.md`.

### `secrets-funnel` is in `pmoves/mk/codex.mk`, not the root Makefile

The canonical secrets pipeline is `make -C pmoves secrets-funnel`. It is defined in `pmoves/mk/codex.mk` (included by `pmoves/Makefile`); a grep of the root `Makefile` alone returns nothing. Before adding any secrets tooling, run `grep -rn 'secrets-funnel' pmoves/Makefile pmoves/mk/`. A duplicate funnel has been written twice by agents who skipped that check.

### Three-Body / Village Rule (governance)

No agent operates alone on production validation. Every lane follows **claim → work → sign → release** in [`pmoves/docs/AGENTS/AGNOTE4482PHI.t1.md`](pmoves/docs/AGENTS/AGNOTE4482PHI.t1.md) (the active claim register). Three bodies, enforced via Claude Code agent frontmatter in `.claude/agents/`: **Delivery** (edits code, `disallowedTools: EnterPlanMode`), **Control** (read-only review, `disallowedTools: Write, Edit, EnterPlanMode`), **Memory** (Cipher/CHIT only). When claiming a lane, write a `CLAIM` row with branch + scope + TTL; on completion write a `RELEASE` row and a signed ACK block.

### CHIT trail signing

After significant multi-file work, sign a provenance entry: `make -C pmoves sign-trail SUMMARY="..." AGENT=<id> PHASE="..."`. If `$CHIT_PASSPHRASE` is unset (common in dev), the payload emits **unsigned** with a stderr warning — that is expected and acceptable locally; still run it. Never hardcode passphrases.

### Damage-control hook recovery

If `patterns.yaml` ever carries unresolved merge-conflict markers, the Bash hook fails closed and blocks **all** Bash commands (you cannot even run `git status`). Recovery escape hatch: the **Edit tool** routes through a separate hook that does not depend on `patterns.yaml` parsing. Use Read + Edit to resolve the conflict markers; Bash resumes on the next call. `patterns.yaml` is intentionally not in `readOnlyPaths` so this path stays open — do not add it.

### Node identity & cross-node state

This is a multi-node fleet (Z890, 5090, 4090, SPARK, Knuckles, KVM4-1/2, KVM2, Jetsons). Per the MOF invariant (PR #1378), every node is a **pore in the lattice** — capacity-class, not expertise-lane. Always verify state locally before assuming; Claude's context is **not** consistent across nodes (different containers, worktrees, claim-register state may exist).

```bash
hostname # which node am I on?
git branch # what branch?
git worktree list # am I in a worktree?
make -C pmoves fleet-status # fleet view (no raw tailscale status — it leaks IPs)
```

Cross-node delegation: Agent Zero `POST http://localhost:8080/mcp/*` (sync), A2A `/.well-known/agent-card.json` (disabled by default), NATS `agent.peer.heartbeat.v1` (Phase D, pending).

### Progressively-disclosed context

Don't dump everything into AGENTS.md. The tiered context map:

| You want | Load |
|---|---|
| Service ports, URLs, health endpoints | [`.claude/CATALOG.md`](.claude/CATALOG.md) |
Comment thread
POWERFULMOVES marked this conversation as resolved.
| Full Known Roads, dev patterns, CHIT, skill pairings, debug recipes | [`.claude/PATTERNS.md`](.claude/PATTERNS.md) |
| Emperor-CHIT-Humility disclosure checklist | [`.claude/BOOTSTRAP.md`](.claude/BOOTSTRAP.md) |
| Who is working on what right now | [`pmoves/docs/AGENTS/AGNOTE4482PHI.t1.md`](pmoves/docs/AGENTS/AGNOTE4482PHI.t1.md) |
| Cold-start orientation (read this first on fresh sessions) | [`pmoves/docs/AGENTS/AGNOTE4482_SITREP.md`](pmoves/docs/AGENTS/AGNOTE4482_SITREP.md) |
| Architecture thesis | [`pmoves/docs/architecture/PMOVES_MOF_ARCHITECTURE.md`](pmoves/docs/architecture/PMOVES_MOF_ARCHITECTURE.md) |


## Canonical Documentation

| Topic | Location |
Expand Down Expand Up @@ -72,29 +149,34 @@ All make targets live in `pmoves/Makefile`. Run with `make -C pmoves <target>`.
- Keep modules small and single-purpose

## Testing
- Framework: `pytest` — tests per service in `pmoves/tests/` (unit, smoke, integration, hardening)
- Framework: `pytest` — tests per service in `pmoves/tests/` (unit, smoke, integration, hardening) and inline `pmoves/services/<svc>/tests/`
- Mock external systems (NATS, Supabase, Neo4j); validate with sample payloads
- Run: `pytest -q pmoves/tests/unit/` or per-service paths
- Local CI checks: `docs/LOCAL_CI_CHECKS.md`
- Before pushing: run relevant smoke targets and document results in PR
- Run a single service suite: `pytest -q pmoves/services/<svc>/tests/` — run under env: `bash pmoves/scripts/with-env.sh pytest pmoves/tests/unit/`
- **Full-stack bring-up + verify** (mutating — starts Supabase, core, agents, media, TensorZero, n8n, Jellyfin, monitoring via `bringup-with-ui`): `cd pmoves && make verify-all`. For routine pre-push checks, prefer the targeted targets below instead.
- Targeted checks (read-only): `make -C pmoves smoke`, `GPU_SMOKE_STRICT=true make -C pmoves smoke-gpu`, `make -C pmoves model-readiness`
- Docstring coverage **≥ 80%** on new Python (CI gate; enforced by CodeRabbit)
- Local CI mirror: `docs/LOCAL_CI_CHECKS.md`
- Before pushing: run `/test:pr` (or the smoke targets above) and paste a **Testing** section into the PR description
- Submodule-pointer changes: always run `make -C pmoves submodule-integrity` before/after

## Commit & PR Guidelines
- Conventional Commits: `feat(scope): description`, `fix(scope): description`, `docs(scope): description`
- PRs: clear description, linked issues, affected services, testing evidence
- Branch prefixes: `feat/`, `fix/`, `infra/`, `docs/`, `refactor/`. Forbidden: `feature/`, `pr/`, `p1`–`p7` (use workstream id). Worktrees or `feat/w<n>-...` IDs are common.
- PRs: clear description, linked issues, affected services, **Testing** section with command evidence
- Keep changes atomic; update docs/schemas when interfaces change
- Merges are **gated** — not autonomous. The standing closeout flow (`pmoves/docs/operations/PR_CLOSEOUT.md`) requires: rebased on latest main, all review threads resolved, all required CI settled, a passing live-head audit, and (where the lane touches production) a Three-Body ACK (`[ACK: delivery] [ACK: control] [ACK: memory]`) in `AGNOTE4482_SIGNOFF_CHECKLIST.md`. Use the closeout flow; do not shortcut to `gh pr merge`.
- After merging: `make -C pmoves docs-reconcile` and sign a CHIT trail entry.
- Auto-review failure signatures + merge hazards (stacked-PR auto-close, squash-merge rebase, submodule-conflict `git update-index --cacheinfo`): see [`.claude/PATTERNS.md`](.claude/PATTERNS.md) §PR Review & Merge Workflow and §Merge Hazards.

## Secrets
- Never commit secrets. Copy `pmoves/env.shared.example` → `pmoves/env.shared`
- Shared defaults in `env.shared`, machine-specific in `.env.local`
- Shared defaults in `env.shared`, machine-specific in `.env.local` (long-form `path: .env.local / required: false` in compose — the short-form `env_file: .env.local` is REQUIRED by default and hard-fails bring-up on nodes without the file)
- Production secrets in GitHub Actions secrets and team vault
- Onboarding: `docs/SECRETS_ONBOARDING.md`

**The canonical secrets pipeline is `make -C pmoves secrets-funnel`.** It is the only
supported path into CHIT storage, and it is defined in `pmoves/mk/codex.mk` — **not** in
`pmoves/Makefile`. A grep of the root Makefile alone will not find it. Before adding any
secrets tooling, run `grep -rn 'secrets-funnel' pmoves/Makefile pmoves/mk/` and
`make -C pmoves help`. A duplicate funnel has been written twice by agents who checked only
the root Makefile and concluded the target did not exist.
- Onboarding: `docs/SECRETS_ONBOARDING.md`. Bootstrap: `make -C pmoves env-setup && make -C pmoves secrets-funnel && make -C pmoves auth-alignment`
- **Never paste API keys in chat.** Inputs to the secrets pipeline are `env.shared` / `local.env` (or the production CHIT bundle); `env.tier-*` files are **generated outputs** materialized by `make -C pmoves secrets-funnel` — placing a key directly in an `env.tier-*` file will be silently overwritten on the next funnel run. The funnel is the only supported path into CHIT storage.
- `*_FILE` secret support is wired across focus services via `pmoves/services/common/env.py::get_secret` — prefer the `_FILE` form for compose-injected secrets.

**The canonical secrets pipeline is `make -C pmoves secrets-funnel`.** It is defined in `pmoves/mk/codex.mk` — **not** in `pmoves/Makefile`. A grep of the root Makefile alone will not find it. Before adding any secrets tooling, run `grep -rn 'secrets-funnel' pmoves/Makefile pmoves/mk/` and `make -C pmoves help`. A duplicate funnel has been written twice by agents who checked only the root Makefile and concluded the target did not exist.

## Submodule Workflow
- Consult `.claude/context/submodules.md` and `pmoves/docs/AGENTS/SUBMODULE_CODEX_HOMES/README.md` before submodule changes
Expand Down
Loading